Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/sugar_network/db/routes.py
diff options
context:
space:
mode:
Diffstat (limited to 'sugar_network/db/routes.py')
-rw-r--r--sugar_network/db/routes.py166
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)