Skip to content

Commit

Permalink
Added APIs to set and get access control properties for paths
Browse files Browse the repository at this point in the history
  • Loading branch information
zezha-msft committed Jun 7, 2019
1 parent 03bfd33 commit cc4c077
Show file tree
Hide file tree
Showing 14 changed files with 1,285 additions and 880 deletions.
2 changes: 1 addition & 1 deletion azure-storage-blob/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
## Version XX.XX.XX

- Added support for directory operations: create, rename, and delete.
- Added support for path operations: create and delete directory, rename path, get and set path access control.

## Version 2.0.1:

Expand Down
10 changes: 10 additions & 0 deletions azure-storage-blob/azure/storage/blob/_deserialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
BlobPrefix,
AccountInformation,
UserDelegationKey,
PathProperties,
)
from ._encryption import _decrypt_blob
from azure.storage.common.models import _list
Expand Down Expand Up @@ -559,3 +560,12 @@ def _convert_xml_to_user_delegation_key(response):
def _parse_continuation_token(response):
marker = response.headers.get('x-ms-continuation')
return marker if marker is not '' else None


def _parse_path_permission_and_acl(response):
props = PathProperties()
props.owner = response.headers.get('x-ms-owner')
props.group = response.headers.get('x-ms-group')
props.permissions = response.headers.get('x-ms-permissions')
props.acl = response.headers.get('x-ms-acl')
return props
180 changes: 161 additions & 19 deletions azure-storage-blob/azure/storage/blob/baseblobservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
_parse_account_information,
_convert_xml_to_user_delegation_key,
_parse_continuation_token,
_parse_path_permission_and_acl,
)
from ._download_chunking import _download_blob_chunks
from ._error import (
Expand Down Expand Up @@ -1545,7 +1546,6 @@ def set_blob_service_properties(
'''
if all(parameter is None for parameter in [logging, hour_metrics, minute_metrics, cors, target_version,
delete_retention_policy, static_website]):

raise ValueError("set_blob_service_properties should be called with at least one parameter")

request = HTTPRequest()
Expand Down Expand Up @@ -3549,19 +3549,19 @@ def delete_directory(self, container_name, directory_path, fail_not_exist=False,
else:
return True, self._perform_request(request, parser=_parse_continuation_token)

def rename_directory(self, container_name, new_directory_path, source_directory_path,
mode=None, marker=None, lease_id=None, source_lease_id=None,
metadata=None, source_if_modified_since=None, source_if_unmodified_since=None,
source_if_match=None, source_if_none_match=None, timeout=None):
def rename_path(self, container_name, new_path, source_path,
mode=None, marker=None, lease_id=None, source_lease_id=None,
metadata=None, source_if_modified_since=None, source_if_unmodified_since=None,
source_if_match=None, source_if_none_match=None, timeout=None):
"""
Rename a directory which can contain other directories or blobs.
Rename a blob or directory(which can contain other directories or blobs).
:param str container_name:
Name of existing container.
:param str new_directory_path:
New path for source_directory_path. Ex: 'dirfoo/dirsubfoo'.
:param str source_directory_path:
Directory to be renamed. Ex: 'dirfoo/dirbar'.
:param str new_path:
New path for source_path. Ex: 'dirfoo/dirsubfoo'.
:param str source_path:
Path to be renamed. Ex: 'dirfoo/dirbar'.
:param mode:
Optional. Valid only when namespace is enabled.
This parameter determines the behavior of the rename operation.
Expand All @@ -3573,13 +3573,13 @@ def rename_directory(self, container_name, new_directory_path, source_directory_
a continuation token is returned. When a continuation token is returned,
it must be specified in a subsequent invocation of the rename operation to continue renaming the directory.
:param str lease_id:
Optional. A lease ID for the new_directory_path.
The new_directory_path must have an active lease and the lease ID must match.
Optional. A lease ID for the new_path.
The new_path must have an active lease and the lease ID must match.
:param str source_lease_id:
Optional. A lease ID for the source_directory_path.
The source_directory_path must have an active lease and the lease ID must match.
Optional. A lease ID for the source_path.
The source_path must have an active lease and the lease ID must match.
:param metadata:
Optional. A dict with name_value pairs to associate with the directory as metadata.
Optional. A dict with name_value pairs to associate with the path as metadata.
Example:{'Category':'test'}.
If metadata is specified, it will overwrite the existing metadata;
otherwise, the existing metadata will be preserved.
Expand All @@ -3603,21 +3603,21 @@ def rename_directory(self, container_name, new_directory_path, source_directory_
A continuation marker if applicable. Otherwise return None.
:rtype: str
"""
_validate_not_none('source_directory_path', source_directory_path)
_validate_not_none('new_directory_path', new_directory_path)
_validate_not_none('source_path', source_path)
_validate_not_none('new_path', new_path)

request = HTTPRequest()
# TODO remove endpoint swapping after service update
request.host_locations = self._swap_blob_endpoints(self._get_host_locations())
request.method = 'PUT'
request.path = _get_path(container_name, new_directory_path)
request.path = _get_path(container_name, new_path)
request.query = {
'mode': mode,
'continuation': _to_str(marker),
'timeout': _int_to_str(timeout),
}
request.headers = {
'x-ms-rename-source': _get_path(container_name, source_directory_path),
'x-ms-rename-source': _get_path(container_name, source_path),
'x-ms-lease-id': _to_str(lease_id),
'x-ms-source-lease-id': _to_str(source_lease_id),
'x-ms-source-if-modified-since': _datetime_to_utc_string(source_if_modified_since),
Expand All @@ -3629,6 +3629,148 @@ def rename_directory(self, container_name, new_directory_path, source_directory_
_add_file_or_directory_properties_header(metadata, request)
return self._perform_request(request, parser=_parse_continuation_token)

def get_path_access_control(self, container_name, path, user_principle_names=False,
lease_id=None, if_modified_since=None, if_unmodified_since=None,
if_match=None, if_none_match=None, timeout=None):
"""
Retrieve the access control properties of a path(directory or blob).
:param str container_name:
Name of existing container.
:param str path:
Path of the directory/blob.
:param user_principle_names:
Valid only when Hierarchical Namespace is enabled for the account.
If "true", the user identity values returned for owner, group, and acl will be transformed
from Azure Active Directory Object IDs to User Principal Names.
If "false", the values will be returned as Azure Active Directory Object IDs.
The default value is false. Note that group and application Object IDs are not translated
because they do not have unique friendly names.
:param str lease_id:
Required if the path has an active lease.
:param datetime if_modified_since:
A date and time value. Specify this header to perform the operation only if the resource
has been modified since the specified date and time.
:param datetime if_unmodified_since:
A date and time value. Specify this header to perform the operation only if the resource
has not been modified since the specified date and time.
:param datetime if_match:
An ETag value. Specify this header to perform the operation
only if the resource's ETag matches the value specified. The ETag must be specified in quotes.
:param datetime if_none_match:
An ETag value or the special wildcard ("*") value.
Specify this header to perform the operation only if the resource's ETag
does not match the value specified. The ETag must be specified in quotes.
:param int timeout:
The timeout parameter is expressed in seconds.
:return: ETag and last modified time of the new directory.
:rtype: :class:`~azure.storage.blob.models.ResourceProperties`
"""
_validate_not_none('path', path)

request = HTTPRequest()
# TODO remove endpoint swapping after service update
request.host_locations = self._swap_blob_endpoints(self._get_host_locations())
request.method = 'HEAD'
request.path = _get_path(container_name, path)
request.query = {
'action': 'getAccessControl',
'upn': _to_str(user_principle_names),
'timeout': _int_to_str(timeout),
}
request.headers = {
'x-ms-lease-id': _to_str(lease_id),
'If-Modified-Since': _datetime_to_utc_string(if_modified_since),
'If-Unmodified-Since': _datetime_to_utc_string(if_unmodified_since),
'If-Match': _to_str(if_match),
'If-None-Match': _to_str(if_none_match),
}
return self._perform_request(request, parser=_parse_path_permission_and_acl)

def set_path_access_control(self, container_name, path, owner=None, group=None, permissions=None,
acl=None, lease_id=None, if_modified_since=None, if_unmodified_since=None,
if_match=None, if_none_match=None, timeout=None):
"""
Set the access control properties of a path(directory or blob).
:param str container_name:
Name of existing container.
:param str path:
Path of the directory/blob.
:param str owner:
Sets the owner of the file or directory.
:param str group:
Sets the owning group of the file or directory.
:param str permissions:
Invalid in conjunction with acl.
Sets POSIX access permissions for the file owner, the file owning group, and others.
Each class may be granted read, write, or execute permission.
The sticky bit is also supported. Both symbolic (rwxrw-rw-)
and 4-digit octal notation (e.g. 0766) are supported.
:param str acl:
Invalid in conjunction with permissions.
Sets POSIX access control rights on files and directories.
The value is a comma-separated list of access control entries that fully replaces the existing
access control list (ACL). Each access control entry (ACE) consists of a scope, a type,
a user or group identifier, and permissions in the format "[scope:][type]:[id]:[permissions]".
The scope must be "default" to indicate the ACE belongs to the default ACL for a directory;
otherwise scope is implicit and the ACE belongs to the access ACL.
There are four ACE types: "user" grants rights to the owner or a named user,
"group" grants rights to the owning group or a named group,
"mask" restricts rights granted to named users and the members of groups,
and "other" grants rights to all users not found in any of the other entries.
The user or group identifier is omitted for entries of type "mask" and "other".
The user or group identifier is also omitted for the owner and owning group.
The permission field is a 3-character sequence where the first character is 'r' to grant read access,
the second character is 'w' to grant write access, and the third character is 'x'
to grant execute permission. If access is not granted, the '-' character is used to denote
that the permission is denied. For example, the following ACL grants read, write, and execute rights to
the file owner and john.doe@contoso, the read right to the owning group,
and nothing to everyone else: "user::rwx,user:john.doe@contoso:rwx,group::r--,other::---,mask=rwx".
:param str lease_id:
Required if the path has an active lease.
:param datetime if_modified_since:
A date and time value. Specify this header to perform the operation only if the resource
has been modified since the specified date and time.
:param datetime if_unmodified_since:
A date and time value. Specify this header to perform the operation only if the resource
has not been modified since the specified date and time.
:param datetime if_match:
An ETag value. Specify this header to perform the operation
only if the resource's ETag matches the value specified. The ETag must be specified in quotes.
:param datetime if_none_match:
An ETag value or the special wildcard ("*") value.
Specify this header to perform the operation only if the resource's ETag
does not match the value specified. The ETag must be specified in quotes.
:param int timeout:
The timeout parameter is expressed in seconds.
:return: ETag and last modified time of the new directory.
:rtype: :class:`~azure.storage.blob.models.ResourceProperties`
"""
_validate_not_none('path', path)

request = HTTPRequest()
# TODO remove endpoint swapping after service update
request.host_locations = self._swap_blob_endpoints(self._get_host_locations())
request.method = 'PATCH'
request.path = _get_path(container_name, path)
request.query = {
'action': 'setAccessControl',
'timeout': _int_to_str(timeout),
}
request.headers = {
'x-ms-owner': _to_str(owner),
'x-ms-group': _to_str(group),
'x-ms-permissions': _to_str(permissions),
'x-ms-acl': _to_str(acl),
'x-ms-lease-id': _to_str(lease_id),
'If-Modified-Since': _datetime_to_utc_string(if_modified_since),
'If-Unmodified-Since': _datetime_to_utc_string(if_unmodified_since),
'If-Match': _to_str(if_match),
'If-None-Match': _to_str(if_none_match),
}
return self._perform_request(request, parser=_parse_base_properties)

# ----------------------------Helpers for directory manipulations---------------------------- #
@staticmethod
def _swap_blob_endpoints(host_locations):
Expand Down
22 changes: 22 additions & 0 deletions azure-storage-blob/azure/storage/blob/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -823,3 +823,25 @@ def __init__(self):
self.signed_service = None
self.signed_version = None
self.value = None


class PathProperties(object):
"""
Represent a path's properties(only permissions and acl at the moment).
The path can be either a directory or a file.
:ivar string owner:
Represents the owner of the path.
:ivar string group:
Represents the group of the path.
:ivar string permissions:
Represents the permissions of the path.
:ivar string acl:
Represents the acl of the path.
"""

def __init__(self):
self.owner = None
self.group = None
self.permissions = None
self.acl = None
Loading

0 comments on commit cc4c077

Please sign in to comment.