========================= ================================
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.
X-Object-Manifest Object parts prefix in ``<container>/<object>`` 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
========================== ===============================
X-Object-Manifest Object parts prefix in ``<container>/<object>`` 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
========================== ===============================
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):
* 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``.
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':
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.
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.
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:
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 = []
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:
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
@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):
@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):