input filters
This commit is contained in:
parent
13c8cc3208
commit
2ae364681b
3 changed files with 59 additions and 32 deletions
16
backend.py
16
backend.py
|
@ -33,7 +33,7 @@ def convert_obj(obj):
|
||||||
|
|
||||||
:param obj: object to convert
|
:param obj: object to convert
|
||||||
|
|
||||||
:return: object that can be stored in MongoDB.
|
:returns: object that can be stored in MongoDB.
|
||||||
"""
|
"""
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return None
|
return None
|
||||||
|
@ -59,7 +59,7 @@ def convert(seq):
|
||||||
|
|
||||||
:param seq: sequence or object to convert
|
:param seq: sequence or object to convert
|
||||||
|
|
||||||
:return: object that can be stored in MongoDB.
|
:returns: object that can be stored in MongoDB.
|
||||||
"""
|
"""
|
||||||
if isinstance(seq, dict):
|
if isinstance(seq, dict):
|
||||||
d = {}
|
d = {}
|
||||||
|
@ -104,7 +104,7 @@ class EventManDB(object):
|
||||||
:param url: URL of the database
|
:param url: URL of the database
|
||||||
:type url: str (or None to connect to localhost)
|
:type url: str (or None to connect to localhost)
|
||||||
|
|
||||||
:return: the database we're connected to
|
:returns: the database we're connected to
|
||||||
:rtype: :class:`~pymongo.database.Database`
|
:rtype: :class:`~pymongo.database.Database`
|
||||||
"""
|
"""
|
||||||
if self.db is not None:
|
if self.db is not None:
|
||||||
|
@ -125,7 +125,7 @@ class EventManDB(object):
|
||||||
:param _id: unique ID of the document
|
:param _id: unique ID of the document
|
||||||
:type _id: str or :class:`~bson.objectid.ObjectId`
|
:type _id: str or :class:`~bson.objectid.ObjectId`
|
||||||
|
|
||||||
:return: the document with the given `_id`
|
:returns: the document with the given `_id`
|
||||||
:rtype: dict
|
:rtype: dict
|
||||||
"""
|
"""
|
||||||
results = self.query(collection, convert({'_id': _id}))
|
results = self.query(collection, convert({'_id': _id}))
|
||||||
|
@ -139,7 +139,7 @@ class EventManDB(object):
|
||||||
:param query: search for documents with those attributes
|
:param query: search for documents with those attributes
|
||||||
:type query: dict or None
|
:type query: dict or None
|
||||||
|
|
||||||
:return: list of matching documents
|
:returns: list of matching documents
|
||||||
:rtype: list
|
:rtype: list
|
||||||
"""
|
"""
|
||||||
db = self.connect()
|
db = self.connect()
|
||||||
|
@ -156,7 +156,7 @@ class EventManDB(object):
|
||||||
:param _id: the _id of the document to store; if None, it's generated
|
:param _id: the _id of the document to store; if None, it's generated
|
||||||
:type _id: object
|
:type _id: object
|
||||||
|
|
||||||
:return: the document, as created in the database
|
:returns: the document, as created in the database
|
||||||
:rtype: dict
|
:rtype: dict
|
||||||
"""
|
"""
|
||||||
db = self.connect()
|
db = self.connect()
|
||||||
|
@ -174,7 +174,7 @@ class EventManDB(object):
|
||||||
:param data: the document information to store
|
:param data: the document information to store
|
||||||
:type data: dict
|
:type data: dict
|
||||||
|
|
||||||
:return: True if the document was already present
|
:returns: True if the document was already present
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
db = self.connect()
|
db = self.connect()
|
||||||
|
@ -212,7 +212,7 @@ class EventManDB(object):
|
||||||
:param create: if True, the document is created if no document matches
|
:param create: if True, the document is created if no document matches
|
||||||
:type create: bool
|
:type create: bool
|
||||||
|
|
||||||
:return: a boolean (True if an existing document was updated) and the document after the update
|
:returns: a boolean (True if an existing document was updated) and the document after the update
|
||||||
:rtype: tuple of (bool, dict)
|
:rtype: tuple of (bool, dict)
|
||||||
"""
|
"""
|
||||||
db = self.connect()
|
db = self.connect()
|
||||||
|
|
|
@ -68,8 +68,9 @@ class BaseHandler(tornado.web.RequestHandler):
|
||||||
permissions = {
|
permissions = {
|
||||||
'event|read': True,
|
'event|read': True,
|
||||||
'event:tickets|all': True,
|
'event:tickets|all': True,
|
||||||
|
'event:tickets-all|create': True,
|
||||||
'events-all|read': True,
|
'events-all|read': True,
|
||||||
'person|create': True
|
'persons-all|create': True
|
||||||
}
|
}
|
||||||
|
|
||||||
# A property to access the first value of each argument.
|
# A property to access the first value of each argument.
|
||||||
|
@ -129,6 +130,7 @@ class BaseHandler(tornado.web.RequestHandler):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_user_info(self):
|
def current_user_info(self):
|
||||||
|
"""Information about the current user, including their permissions."""
|
||||||
current_user = self.current_user
|
current_user = self.current_user
|
||||||
user_info = {'permissions': set([k for (k, v) in self.permissions.iteritems() if v is True])}
|
user_info = {'permissions': set([k for (k, v) in self.permissions.iteritems() if v is True])}
|
||||||
if current_user:
|
if current_user:
|
||||||
|
@ -162,6 +164,7 @@ class BaseHandler(tornado.web.RequestHandler):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def build_error(self, message='', status=400):
|
def build_error(self, message='', status=400):
|
||||||
|
"""Build and write an error message."""
|
||||||
self.set_status(status)
|
self.set_status(status)
|
||||||
self.write({'error': True, 'message': message})
|
self.write({'error': True, 'message': message})
|
||||||
|
|
||||||
|
@ -204,7 +207,7 @@ class CollectionHandler(BaseHandler):
|
||||||
:param seq: unique name of the sequence
|
:param seq: unique name of the sequence
|
||||||
:type seq: str
|
:type seq: str
|
||||||
|
|
||||||
:return: the next value of the sequence
|
:returns: the next value of the sequence
|
||||||
:rtype: int
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
if not self.db.query(self.counters_collection, {'seq_name': seq}):
|
if not self.db.query(self.counters_collection, {'seq_name': seq}):
|
||||||
|
@ -223,7 +226,7 @@ class CollectionHandler(BaseHandler):
|
||||||
:param random_alpha: number of random lowercase alphanumeric chars
|
:param random_alpha: number of random lowercase alphanumeric chars
|
||||||
:type random_alpha: int
|
:type random_alpha: int
|
||||||
|
|
||||||
:return: unique ID
|
:returns: unique ID
|
||||||
:rtype: str"""
|
:rtype: str"""
|
||||||
t = str(time.time()).replace('.', '_')
|
t = str(time.time()).replace('.', '_')
|
||||||
seq = str(self.get_next_seq(seq))
|
seq = str(self.get_next_seq(seq))
|
||||||
|
@ -238,7 +241,7 @@ class CollectionHandler(BaseHandler):
|
||||||
:param params: a dictionary of items that must all be present in an original list item to be included in the return
|
:param params: a dictionary of items that must all be present in an original list item to be included in the return
|
||||||
:type params: dict
|
:type params: dict
|
||||||
|
|
||||||
:return: list of items that have all the keys with the same values as params
|
:returns: list of items that have all the keys with the same values as params
|
||||||
:rtype: list"""
|
:rtype: list"""
|
||||||
if not params:
|
if not params:
|
||||||
return results
|
return results
|
||||||
|
@ -284,11 +287,16 @@ class CollectionHandler(BaseHandler):
|
||||||
continue
|
continue
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def apply_filter(self, output, filter_name):
|
def apply_filter(self, data, filter_name):
|
||||||
filter_method = getattr(self, filter_name, None)
|
"""Apply a filter to the data.
|
||||||
|
|
||||||
|
:param data: the data to filter
|
||||||
|
:returns: the modified (possibly also in place) data
|
||||||
|
"""
|
||||||
|
filter_method = getattr(self, 'filter_%s' % filter_name, None)
|
||||||
if filter_method is not None:
|
if filter_method is not None:
|
||||||
output = filter_method(output)
|
data = filter_method(data)
|
||||||
return output
|
return data
|
||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
@authenticated
|
@authenticated
|
||||||
|
@ -298,9 +306,10 @@ class CollectionHandler(BaseHandler):
|
||||||
permission = '%s:%s%s|read' % (self.document, resource, '-all' if resource_id is None else '')
|
permission = '%s:%s%s|read' % (self.document, resource, '-all' if resource_id is None else '')
|
||||||
if acl and not self.has_permission(permission):
|
if acl and not self.has_permission(permission):
|
||||||
return self.build_error(status=401, message='insufficient permissions: %s' % permission)
|
return self.build_error(status=401, message='insufficient permissions: %s' % permission)
|
||||||
method = getattr(self, 'handle_get_%s' % resource, None)
|
handler = getattr(self, 'handle_get_%s' % resource, None)
|
||||||
if method and callable(method):
|
if handler and callable(handler):
|
||||||
output = method(id_, resource_id, **kwargs) or {}
|
output = handler(id_, resource_id, **kwargs) or {}
|
||||||
|
output = self.apply_filter(output, 'get_%s' % resource)
|
||||||
self.write(output)
|
self.write(output)
|
||||||
return
|
return
|
||||||
return self.build_error(status=404, message='unable to access resource: %s' % resource)
|
return self.build_error(status=404, message='unable to access resource: %s' % resource)
|
||||||
|
@ -310,7 +319,7 @@ class CollectionHandler(BaseHandler):
|
||||||
if acl and not self.has_permission(permission):
|
if acl and not self.has_permission(permission):
|
||||||
return self.build_error(status=401, message='insufficient permissions: %s' % permission)
|
return self.build_error(status=401, message='insufficient permissions: %s' % permission)
|
||||||
output = self.db.get(self.collection, id_)
|
output = self.db.get(self.collection, id_)
|
||||||
output = self.apply_filter(output, 'filter_get')
|
output = self.apply_filter(output, 'get')
|
||||||
self.write(output)
|
self.write(output)
|
||||||
else:
|
else:
|
||||||
# return an object containing the list of all objects in the collection;
|
# return an object containing the list of all objects in the collection;
|
||||||
|
@ -321,7 +330,7 @@ class CollectionHandler(BaseHandler):
|
||||||
if acl and not self.has_permission(permission):
|
if acl and not self.has_permission(permission):
|
||||||
return self.build_error(status=401, message='insufficient permissions: %s' % permission)
|
return self.build_error(status=401, message='insufficient permissions: %s' % permission)
|
||||||
output = {self.collection: self.db.query(self.collection, self.arguments)}
|
output = {self.collection: self.db.query(self.collection, self.arguments)}
|
||||||
output = self.apply_filter(output, 'filter_get_all')
|
output = self.apply_filter(output, 'get_all')
|
||||||
self.write(output)
|
self.write(output)
|
||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
|
@ -330,25 +339,33 @@ class CollectionHandler(BaseHandler):
|
||||||
data = escape.json_decode(self.request.body or '{}')
|
data = escape.json_decode(self.request.body or '{}')
|
||||||
self._clean_dict(data)
|
self._clean_dict(data)
|
||||||
method = self.request.method.lower()
|
method = self.request.method.lower()
|
||||||
|
crud_method = 'create' if method == 'post' else 'update'
|
||||||
if resource:
|
if resource:
|
||||||
permission = '%s:%s|%s' % (self.document, resource, 'create' if method == 'post' else 'update')
|
permission = '%s:%s%s|%s' % (self.document, resource, '-all' if resource_id is None else '', crud_method)
|
||||||
if not self.has_permission(permission):
|
if not self.has_permission(permission):
|
||||||
return self.build_error(status=401, message='insufficient permissions: %s' % permission)
|
return self.build_error(status=401, message='insufficient permissions: %s' % permission)
|
||||||
# Handle access to sub-resources.
|
# Handle access to sub-resources.
|
||||||
handler = getattr(self, 'handle_%s_%s' % (method, resource), None)
|
handler = getattr(self, 'handle_%s_%s' % (method, resource), None)
|
||||||
if handler and callable(handler):
|
if handler and callable(handler):
|
||||||
self.write(handler(id_, resource_id, data, **kwargs))
|
data = self.apply_filter(data, 'input_%s_%s' % (method, resource))
|
||||||
|
output = handler(id_, resource_id, data, **kwargs)
|
||||||
|
output = self.apply_filter(output, 'get_%s' % resource)
|
||||||
|
self.write(output)
|
||||||
return
|
return
|
||||||
if id_ is None:
|
if id_ is not None:
|
||||||
permission = '%s-all|%s' % (self.collection, 'create' if method == 'post' else 'update')
|
permission = '%s|%s' % (self.document, crud_method)
|
||||||
if not self.has_permission(permission):
|
|
||||||
return self.build_error(status=401, message='insufficient permissions: %s' % permission)
|
|
||||||
newData = self.db.add(self.collection, data, _id=self.gen_id())
|
|
||||||
else:
|
|
||||||
permission = '%s|%s' % (self.document, 'create' if method == 'post' else 'update')
|
|
||||||
if not self.has_permission(permission):
|
if not self.has_permission(permission):
|
||||||
return self.build_error(status=401, message='insufficient permissions: %s' % permission)
|
return self.build_error(status=401, message='insufficient permissions: %s' % permission)
|
||||||
|
data = self.apply_filter(data, 'input_%s' % _method)
|
||||||
merged, newData = self.db.update(self.collection, id_, data)
|
merged, newData = self.db.update(self.collection, id_, data)
|
||||||
|
newData = self.apply_filter(newData, method)
|
||||||
|
else:
|
||||||
|
permission = '%s-all|%s' % (self.collection, crud_method)
|
||||||
|
if not self.has_permission(permission):
|
||||||
|
return self.build_error(status=401, message='insufficient permissions: %s' % permission)
|
||||||
|
data = self.apply_filter(data, 'input_%s_all' % method)
|
||||||
|
newData = self.db.add(self.collection, data, _id=self.gen_id())
|
||||||
|
newData = self.apply_filter(newData, '%s_all' % method)
|
||||||
self.write(newData)
|
self.write(newData)
|
||||||
|
|
||||||
# PUT (update an existing document) is handled by the POST (create a new document) method
|
# PUT (update an existing document) is handled by the POST (create a new document) method
|
||||||
|
@ -359,7 +376,7 @@ class CollectionHandler(BaseHandler):
|
||||||
def delete(self, id_=None, resource=None, resource_id=None, **kwargs):
|
def delete(self, id_=None, resource=None, resource_id=None, **kwargs):
|
||||||
if resource:
|
if resource:
|
||||||
# Handle access to sub-resources.
|
# Handle access to sub-resources.
|
||||||
permission = '%s:%s|delete' % (self.document, resource)
|
permission = '%s:%s%s|delete' % (self.document, resource, '-all' if resource_id is None else '')
|
||||||
if not self.has_permission(permission):
|
if not self.has_permission(permission):
|
||||||
return self.build_error(status=401, message='insufficient permissions: %s' % permission)
|
return self.build_error(status=401, message='insufficient permissions: %s' % permission)
|
||||||
method = getattr(self, 'handle_delete_%s' % resource, None)
|
method = getattr(self, 'handle_delete_%s' % resource, None)
|
||||||
|
@ -371,6 +388,8 @@ class CollectionHandler(BaseHandler):
|
||||||
if not self.has_permission(permission):
|
if not self.has_permission(permission):
|
||||||
return self.build_error(status=401, message='insufficient permissions: %s' % permission)
|
return self.build_error(status=401, message='insufficient permissions: %s' % permission)
|
||||||
self.db.delete(self.collection, id_)
|
self.db.delete(self.collection, id_)
|
||||||
|
else:
|
||||||
|
self.write({'success': False})
|
||||||
self.write({'success': True})
|
self.write({'success': True})
|
||||||
|
|
||||||
def on_timeout(self, cmd, pipe):
|
def on_timeout(self, cmd, pipe):
|
||||||
|
@ -509,6 +528,14 @@ class EventsHandler(CollectionHandler):
|
||||||
event['persons'] = []
|
event['persons'] = []
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
def filter_input_post_tickets(self, data):
|
||||||
|
if not self.has_permission('event|update'):
|
||||||
|
if 'attended' in data:
|
||||||
|
del data['attended']
|
||||||
|
return data
|
||||||
|
|
||||||
|
filter_input_put_tickets = filter_input_post_tickets
|
||||||
|
|
||||||
def _get_person_data(self, person_id_or_query, persons):
|
def _get_person_data(self, person_id_or_query, persons):
|
||||||
"""Filter a list of persons returning the first item with a given person_id
|
"""Filter a list of persons returning the first item with a given person_id
|
||||||
or which set of keys specified in a dictionary match their respective values."""
|
or which set of keys specified in a dictionary match their respective values."""
|
||||||
|
|
2
utils.py
2
utils.py
|
@ -36,7 +36,7 @@ def csvParse(csvStr, remap=None, merge=None):
|
||||||
:param merge: merge these information into each line
|
:param merge: merge these information into each line
|
||||||
:type merge: dict
|
:type merge: dict
|
||||||
|
|
||||||
:return: tuple with a dict of total and valid lines and the data
|
:returns: tuple with a dict of total and valid lines and the data
|
||||||
:rtype: tuple
|
:rtype: tuple
|
||||||
"""
|
"""
|
||||||
fd = StringIO.StringIO(csvStr)
|
fd = StringIO.StringIO(csvStr)
|
||||||
|
|
Loading…
Reference in a new issue