From 067cf1fc08f5d140e44ccc4d298c1daa12ad3106 Mon Sep 17 00:00:00 2001 From: Antony Chazapis Date: Tue, 13 Sep 2011 09:30:45 +0300 Subject: [PATCH] Report allowed actions in cross-user object requests, with the 'X-Object-Allowed-To' header. --- docs/source/devguide.rst | 9 ++++++--- pithos/api/functions.py | 6 +++--- pithos/api/util.py | 10 +++++++--- pithos/backends/base.py | 5 +++-- pithos/backends/modular.py | 15 ++++++++++++--- pithos/backends/simple.py | 14 +++++++++++--- 6 files changed, 42 insertions(+), 17 deletions(-) diff --git a/docs/source/devguide.rst b/docs/source/devguide.rst index b99b645..c191915 100644 --- a/docs/source/devguide.rst +++ b/docs/source/devguide.rst @@ -25,11 +25,12 @@ Document Revisions ========================= ================================ Revision Description ========================= ================================ -0.6 (Sept 12, 2011) Reply with Merkle hash as the ETag when updating objects. +0.6 (Sept 13, 2011) Reply with Merkle hash as the ETag when updating objects. \ Include version id in object replace/change replies. \ Change conflict (409) replies format to text. \ Tags should be migrated to a meta value. \ Container ``PUT`` updates metadata/policy. +\ Report allowed actions in shared object replies. 0.5 (July 22, 2011) Object update from another object's data. \ Support object truncate. \ Create object using a standard HTML form. @@ -566,6 +567,7 @@ X-Object-Modified-By The user that comitted the object's version X-Object-Manifest Object parts prefix in ``/`` form (optional) X-Object-Sharing Object permissions (optional) X-Object-Shared-By Object inheriting permissions (optional) +X-Object-Allowed-To Allowed actions on object (optional) X-Object-Public Object's publicly accessible URI (optional) X-Object-Meta-* Optional user defined metadata ========================== =============================== @@ -659,6 +661,7 @@ X-Object-Modified-By The user that comitted the object's version X-Object-Manifest Object parts prefix in ``/`` form (optional) X-Object-Sharing Object permissions (optional) X-Object-Shared-By Object inheriting permissions (optional) +X-Object-Allowed-To Allowed actions on object (optional) X-Object-Public Object's publicly accessible URI (optional) X-Object-Meta-* Optional user defined metadata ========================== =============================== @@ -909,7 +912,7 @@ Sharing and Public Objects Read and write control in Pithos is managed by setting appropriate permissions with the ``X-Object-Sharing`` header. The permissions are applied using prefix-based inheritance. Thus, each set of authorization directives is applied to all objects sharing the same prefix with the object where the corresponding ``X-Object-Sharing`` header is defined. For simplicity, nested/overlapping permissions are not allowed. Setting ``X-Object-Sharing`` will fail, if the object is already "covered", or another object with a longer common-prefix name already has permissions. When retrieving an object, the ``X-Object-Shared-By`` header reports where it gets its permissions from. If not present, the object is the actual source of authorization directives. -A user may ``GET`` another account or container. The result will include a limited reply, containing only the allowed containers or objects respectively. A top-level request with an authentication token, will return a list of allowed accounts, so the user can easily find out which other users share objects. +A user may ``GET`` another account or container. The result will include a limited reply, containing only the allowed containers or objects respectively. A top-level request with an authentication token, will return a list of allowed accounts, so the user can easily find out which other users share objects. The ``X-Object-Allowed-To`` header lists the actions allowed on an object, if it does not belong to the requesting user. Objects that are marked as public, via the ``X-Object-Public`` meta, are also available at the corresponding URI returned for ``HEAD`` or ``GET``. Requests for public objects do not need to include an ``X-Auth-Token``. Pithos will ignore request parameters and only include the following headers in the reply (all ``X-Object-*`` meta is hidden): @@ -952,7 +955,7 @@ List of differences from the OOS API: * Conditional object create/update operations, using ``If-Match`` and ``If-None-Match`` headers. * Time-variant account/container listings via the ``until`` parameter. * Object versions - parameter ``version`` in ``HEAD``/``GET`` (list versions with ``GET``), ``X-Object-Version-*`` meta in replies, ``X-Source-Version`` in ``PUT``/``COPY``. -* Sharing/publishing with ``X-Object-Sharing``, ``X-Object-Public`` at the object level. Cross-user operations are allowed - controlled by sharing directives. Permissions may include groups defined with ``X-Account-Group-*`` at the account level. These apply to the object - not its versions. +* Sharing/publishing with ``X-Object-Sharing``, ``X-Object-Public`` at the object level. Cross-user operations are allowed - controlled by sharing directives. Available actions in cross-user requests are reported with ``X-Object-Allowed-To``. Permissions may include groups defined with ``X-Account-Group-*`` at the account level. These apply to the object - not its versions. * Support for prefix-based inheritance when enforcing permissions. Parent object carrying the authorization directives is reported in ``X-Object-Shared-By``. * Large object support with ``X-Object-Manifest``. * Trace the user that created/modified an object with ``X-Object-Modified-By``. diff --git a/pithos/api/functions.py b/pithos/api/functions.py index 89067e4..34939b7 100644 --- a/pithos/api/functions.py +++ b/pithos/api/functions.py @@ -498,7 +498,7 @@ def object_list(request, v_account, v_container): rename_meta_key(meta, 'modified_by', 'x_object_modified_by') rename_meta_key(meta, 'version', 'x_object_version') rename_meta_key(meta, 'version_timestamp', 'x_object_version_timestamp') - update_sharing_meta(permissions, v_account, v_container, x[0], meta) + update_sharing_meta(request, permissions, v_account, v_container, x[0], meta) update_public_meta(public, meta) object_meta.append(printable_header_dict(meta)) if request.serialization == 'xml': @@ -534,7 +534,7 @@ def object_meta(request, v_account, v_container, v_object): raise ItemNotFound('Version does not exist') update_manifest_meta(request, v_account, meta) - update_sharing_meta(permissions, v_account, v_container, v_object, meta) + update_sharing_meta(request, permissions, v_account, v_container, v_object, meta) update_public_meta(public, meta) # Evaluate conditions. @@ -599,7 +599,7 @@ def object_read(request, v_account, v_container, v_object): raise ItemNotFound('Version does not exist') update_manifest_meta(request, v_account, meta) - update_sharing_meta(permissions, v_account, v_container, v_object, meta) + update_sharing_meta(request, permissions, v_account, v_container, v_object, meta) update_public_meta(public, meta) # Evaluate conditions. diff --git a/pithos/api/util.py b/pithos/api/util.py index 8104e5d..12c8818 100644 --- a/pithos/api/util.py +++ b/pithos/api/util.py @@ -160,7 +160,9 @@ def put_object_headers(response, meta, restricted=False): response['X-Object-Version-Timestamp'] = http_date(int(meta['version_timestamp'])) for k in [x for x in meta.keys() if x.startswith('X-Object-Meta-')]: response[smart_str(k, strings_only=True)] = smart_str(meta[k], strings_only=True) - for k in ('Content-Encoding', 'Content-Disposition', 'X-Object-Manifest', 'X-Object-Sharing', 'X-Object-Shared-By', 'X-Object-Public'): + for k in ('Content-Encoding', 'Content-Disposition', 'X-Object-Manifest', + 'X-Object-Sharing', 'X-Object-Shared-By', 'X-Object-Allowed-To', + 'X-Object-Public'): if k in meta: response[k] = smart_str(meta[k], strings_only=True) else: @@ -189,10 +191,10 @@ def update_manifest_meta(request, v_account, meta): md5.update(hash) meta['hash'] = md5.hexdigest().lower() -def update_sharing_meta(permissions, v_account, v_container, v_object, meta): +def update_sharing_meta(request, permissions, v_account, v_container, v_object, meta): if permissions is None: return - perm_path, perms = permissions + allowed, perm_path, perms = permissions if len(perms) == 0: return ret = [] @@ -205,6 +207,8 @@ def update_sharing_meta(permissions, v_account, v_container, v_object, meta): meta['X-Object-Sharing'] = '; '.join(ret) if '/'.join((v_account, v_container, v_object)) != perm_path: meta['X-Object-Shared-By'] = perm_path + if request.user != v_account: + meta['X-Object-Allowed-To'] = allowed def update_public_meta(public, meta): if not public: diff --git a/pithos/backends/base.py b/pithos/backends/base.py index 005762f..9fa962c 100644 --- a/pithos/backends/base.py +++ b/pithos/backends/base.py @@ -311,10 +311,11 @@ class BaseBackend(object): return '' def get_object_permissions(self, user, account, container, name): - """Return the path from which this object gets its permissions from, + """Return the action allowed on the object, the path + from which the object gets its permissions from, along with a dictionary containing the permissions. - The keys are: + The dictionary keys are (also used for defining the action): 'read': The object is readable by the users/groups in the list 'write': The object is writable by the users/groups in the list diff --git a/pithos/backends/modular.py b/pithos/backends/modular.py index 54fc340..631b8bd 100644 --- a/pithos/backends/modular.py +++ b/pithos/backends/modular.py @@ -414,13 +414,22 @@ class ModularBackend(BaseBackend): @backend_method def get_object_permissions(self, user, account, container, name): - """Return the path from which this object gets its permissions from,\ + """Return the action allowed on the object, the path + from which the object gets its permissions from, along with a dictionary containing the permissions.""" logger.debug("get_object_permissions: %s %s %s", account, container, name) - self._can_read(user, account, container, name) + allowed = 'write' + if user != account: + path = '/'.join((account, container, name)) + if self.permissions.access_check(path, self.WRITE, user): + allowed = 'write' + elif self.permissions.access_check(path, self.READ, user): + allowed = 'read' + else: + raise NotAllowedError path = self._lookup_object(account, container, name)[0] - return self.permissions.access_inherit(path) + return (allowed,) + self.permissions.access_inherit(path) @backend_method def update_object_permissions(self, user, account, container, name, permissions): diff --git a/pithos/backends/simple.py b/pithos/backends/simple.py index 76d8251..1e2f8a1 100644 --- a/pithos/backends/simple.py +++ b/pithos/backends/simple.py @@ -449,13 +449,21 @@ class SimpleBackend(BaseBackend): @backend_method def get_object_permissions(self, user, account, container, name): - """Return the path from which this object gets its permissions from,\ + """Return the action allowed on the object, the path + from which the object gets its permissions from, along with a dictionary containing the permissions.""" logger.debug("get_object_permissions: %s %s %s", account, container, name) - self._can_read(user, account, container, name) + allowed = 'write' + if user != account: + if self._is_allowed(user, account, container, name, 'write'): + allowed = 'write' + elif self._is_allowed(user, account, container, name, 'read'): + allowed = 'read' + else: + raise NotAllowedError path = self._get_objectinfo(account, container, name)[0] - return self._get_permissions(path) + return (allowed,) + self._get_permissions(path) @backend_method def update_object_permissions(self, user, account, container, name, permissions): -- 1.7.10.4