diff options
Diffstat (limited to 'sugar_network/db/routes.py')
-rw-r--r-- | sugar_network/db/routes.py | 166 |
1 files changed, 83 insertions, 83 deletions
diff --git a/sugar_network/db/routes.py b/sugar_network/db/routes.py index f319658..c74a93e 100644 --- a/sugar_network/db/routes.py +++ b/sugar_network/db/routes.py @@ -19,7 +19,7 @@ from contextlib import contextmanager from sugar_network import toolkit from sugar_network.db.metadata import Aggregated -from sugar_network.toolkit.router import ACL, File, route, fallbackroute +from sugar_network.toolkit.router import ACL, route, fallbackroute from sugar_network.toolkit.coroutine import this from sugar_network.toolkit import http, parcel, enforce @@ -32,64 +32,62 @@ _logger = logging.getLogger('db.routes') class Routes(object): def __init__(self, volume, find_limit=None): - self.volume = volume + this.volume = self.volume = volume self._find_limit = find_limit - this.volume = self.volume @route('POST', [None], acl=ACL.AUTH, mime_type='application/json') - def create(self, request): - with self._post(request, ACL.CREATE) as doc: + def create(self): + with self._post(ACL.CREATE) as doc: doc.created() - if request.principal: + if this.principal: authors = doc.posts['author'] = {} - self._useradd(authors, request.principal, ACL.ORIGINAL) - self.volume[request.resource].create(doc.posts) + self._useradd(authors, this.principal, ACL.ORIGINAL) + self.volume[this.request.resource].create(doc.posts) return doc['guid'] @route('GET', [None], arguments={'offset': int, 'limit': int, 'reply': ('guid',)}, mime_type='application/json') - def find(self, request, reply, limit): - self._preget(request) - if self._find_limit: - if limit <= 0: - request['limit'] = self._find_limit - elif limit > self._find_limit: - _logger.warning('The find limit is restricted to %s', - self._find_limit) - request['limit'] = self._find_limit + def find(self, reply, limit): + self._preget() + request = this.request + if self._find_limit and limit > self._find_limit: + _logger.warning('The find limit is restricted to %s', + self._find_limit) + request['limit'] = self._find_limit documents, total = self.volume[request.resource].find( not_state='deleted', **request) - result = [self._postget(request, i, reply) for i in documents] + result = [self._postget(i, reply) for i in documents] return {'total': total, 'result': result} @route('GET', [None, None], cmd='exists', mime_type='application/json') - def exists(self, request): - return self.volume[request.resource][request.guid].exists + def exists(self): + return self.volume[this.request.resource][this.request.guid].exists @route('PUT', [None, None], acl=ACL.AUTH | ACL.AUTHOR) - def update(self, request): - with self._post(request, ACL.WRITE) as doc: + def update(self): + with self._post(ACL.WRITE) as doc: if not doc.posts: return doc.updated() - self.volume[request.resource].update(doc.guid, doc.posts) + self.volume[this.request.resource].update(doc.guid, doc.posts) @route('PUT', [None, None, None], acl=ACL.AUTH | ACL.AUTHOR) - def update_prop(self, request): + def update_prop(self): + request = this.request if request.content is None: value = request.content_stream else: value = request.content request.content = {request.prop: value} - self.update(request) + self.update() @route('DELETE', [None, None], acl=ACL.AUTH | ACL.AUTHOR) - def delete(self, request): + def delete(self): # Node data should not be deleted immediately # to make master-slave synchronization possible - directory = self.volume[request.resource] - doc = directory[request.guid] + directory = self.volume[this.request.resource] + doc = directory[this.request.guid] enforce(doc.exists, http.NotFound, 'Resource not found') doc.posts['state'] = 'deleted' doc.updated() @@ -97,45 +95,43 @@ class Routes(object): @route('GET', [None, None], arguments={'reply': list}, mime_type='application/json') - def get(self, request, reply): + def get(self, reply): if not reply: reply = [] - for prop in self.volume[request.resource].metadata.values(): - if prop.acl & ACL.READ and not (prop.acl & ACL.LOCAL) and \ - not isinstance(prop, Aggregated): + for prop in self.volume[this.request.resource].metadata.values(): + if prop.acl & ACL.READ and not isinstance(prop, Aggregated): reply.append(prop.name) - self._preget(request) - doc = self.volume[request.resource].get(request.guid) + self._preget() + doc = self.volume[this.request.resource].get(this.request.guid) enforce(doc.exists and doc['state'] != 'deleted', http.NotFound, 'Resource not found') - return self._postget(request, doc, reply) + return self._postget(doc, reply) @route('GET', [None, None, None], mime_type='application/json') - def get_prop(self, request, response): + def get_prop(self): + request = this.request directory = self.volume[request.resource] directory.metadata[request.prop].assert_access(ACL.READ) - value = directory[request.guid].repr(request.prop) - enforce(value is not File.AWAY, http.NotFound, 'No blob') - return value + return directory[request.guid].repr(request.prop) @route('HEAD', [None, None, None]) - def get_prop_meta(self, request, response): - return self.get_prop(request, response) + def get_prop_meta(self): + return self.get_prop() @route('POST', [None, None, None], acl=ACL.AUTH, mime_type='application/json') - def insert_to_aggprop(self, request): - return self._aggpost(request, ACL.INSERT) + def insert_to_aggprop(self): + return self._aggpost(ACL.INSERT) @route('PUT', [None, None, None, None], acl=ACL.AUTH, mime_type='application/json') - def update_aggprop(self, request): - self._aggpost(request, ACL.REPLACE, request.key) + def update_aggprop(self): + self._aggpost(ACL.REPLACE) @route('DELETE', [None, None, None, None], acl=ACL.AUTH, mime_type='application/json') - def remove_from_aggprop(self, request): - self._aggpost(request, ACL.REMOVE, request.key) + def remove_from_aggprop(self): + self._aggpost(ACL.REMOVE) @route('GET', [None, None, None, None], mime_type='application/json') def get_aggprop(self): @@ -147,13 +143,12 @@ class Routes(object): agg_value = doc[prop.name].get(this.request.key) enforce(agg_value is not None, http.NotFound, 'Aggregated item not found') - value = prop.subreprcast(agg_value['value']) - enforce(value is not File.AWAY, http.NotFound, 'No blob') - return value + return prop.subreprcast(agg_value['value']) @route('PUT', [None, None], cmd='useradd', arguments={'role': 0}, acl=ACL.AUTH | ACL.AUTHOR) - def useradd(self, request, user, role): + def useradd(self, user, role): + request = this.request enforce(user, "Argument 'user' is not specified") directory = self.volume[request.resource] authors = directory.get(request.guid)['author'] @@ -161,9 +156,10 @@ class Routes(object): directory.update(request.guid, {'author': authors}) @route('PUT', [None, None], cmd='userdel', acl=ACL.AUTH | ACL.AUTHOR) - def userdel(self, request, user): + def userdel(self, user): + request = this.request enforce(user, "Argument 'user' is not specified") - enforce(user != request.principal, 'Cannot remove yourself') + enforce(user != this.principal, 'Cannot remove yourself') directory = self.volume[request.resource] authors = directory.get(request.guid)['author'] enforce(user in authors, 'No such user') @@ -171,38 +167,36 @@ class Routes(object): directory.update(request.guid, {'author': authors}) @route('GET', [None, None], cmd='clone') - def clone(self, request): - clone = self.volume.clone(request.resource, request.guid) + def clone(self): + clone = self.volume.clone(this.request.resource, this.request.guid) return parcel.encode([('push', None, clone)]) @fallbackroute('GET', ['blobs']) def blobs(self): - return this.volume.blobs.get(this.request.guid) - - def on_aggprop_update(self, request, prop, value): - pass + return self.volume.blobs.get(this.request.guid) @contextmanager - def _post(self, request, access): - content = request.content + def _post(self, access): + content = this.request.content enforce(isinstance(content, dict), http.BadRequest, 'Invalid value') if access == ACL.CREATE: - if 'guid' in content: - # TODO Temporal security hole, see TODO - guid = content['guid'] + guid = content.get('guid') + if guid: + enforce(this.principal and this.principal.admin, + http.BadRequest, 'GUID should not be specified') enforce(_GUID_RE.match(guid) is not None, - http.BadRequest, 'Malformed %s GUID', guid) + http.BadRequest, 'Malformed GUID') else: guid = toolkit.uuid() - doc = self.volume[request.resource][guid] + doc = self.volume[this.request.resource][guid] enforce(not doc.exists, 'Resource already exists') doc.posts['guid'] = guid for name, prop in doc.metadata.items(): if name not in content and prop.default is not None: doc.posts[name] = prop.default else: - doc = self.volume[request.resource][request.guid] + doc = self.volume[this.request.resource][this.request.guid] enforce(doc.exists, 'Resource not found') this.resource = doc @@ -223,7 +217,7 @@ class Routes(object): except Exception, error: error = 'Value %r for %r property is invalid: %s' % \ (value, prop.name, error) - toolkit.exception(error) + _logger.exception(error) raise http.BadRequest(error) yield doc except Exception: @@ -232,22 +226,19 @@ class Routes(object): else: teardown(doc.origs, doc.posts) - def _preget(self, request): - reply = request.get('reply') + def _preget(self): + reply = this.request.get('reply') if not reply: - request['reply'] = ('guid',) + this.request['reply'] = ('guid',) else: - directory = self.volume[request.resource] + directory = self.volume[this.request.resource] for prop in reply: directory.metadata[prop].assert_access(ACL.READ) - def _postget(self, request, doc, props): + def _postget(self, doc, props): result = {} for name in props: - value = doc.repr(name) - if isinstance(value, File): - value = value.url - result[name] = value + result[name] = doc.repr(name) return result def _useradd(self, authors, user, role): @@ -270,20 +261,29 @@ class Routes(object): props['order'] = 0 authors[user] = props - def _aggpost(self, request, acl, aggid=None): + def _aggpost(self, acl): + request = this.request doc = this.resource = self.volume[request.resource][request.guid] prop = doc.metadata[request.prop] enforce(isinstance(prop, Aggregated), http.BadRequest, 'Property is not aggregated') prop.assert_access(acl) + def enforce_authority(author): + if prop.acl & ACL.AUTHOR: + author = doc['author'] + enforce(not author or this.principal in author or + this.principal and this.principal.admin, + http.Forbidden, 'Authors only') + + aggid = request.key if aggid and aggid in doc[request.prop]: aggvalue = doc[request.prop][aggid] - self.on_aggprop_update(request, prop, aggvalue) + enforce_authority(aggvalue.get('author')) prop.subteardown(aggvalue['value']) else: enforce(acl != ACL.REMOVE, http.NotFound, 'No aggregated item') - self.on_aggprop_update(request, prop, None) + enforce_authority(None) aggvalue = {} if acl != ACL.REMOVE: @@ -299,10 +299,10 @@ class Routes(object): aggid = toolkit.uuid() aggvalue['value'] = value - if request.principal: + if this.principal: authors = aggvalue['author'] = {} - role = ACL.ORIGINAL if request.principal in doc['author'] else 0 - self._useradd(authors, request.principal, role) + role = ACL.ORIGINAL if this.principal in doc['author'] else 0 + self._useradd(authors, this.principal, role) doc.posts[request.prop] = {aggid: aggvalue} doc.updated() self.volume[request.resource].update(request.guid, doc.posts) |