Skip to content

Commit 703d67b

Browse files
committed
Added APIs to set and get access control properties for paths
1 parent 63d08e1 commit 703d67b

14 files changed

+1285
-880
lines changed

azure-storage-blob/ChangeLog.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
## Version XX.XX.XX
66

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

99
## Version 2.0.1:
1010

azure-storage-blob/azure/storage/blob/_deserialization.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
BlobPrefix,
3838
AccountInformation,
3939
UserDelegationKey,
40+
PathProperties,
4041
)
4142
from ._encryption import _decrypt_blob
4243
from azure.storage.common.models import _list
@@ -559,3 +560,12 @@ def _convert_xml_to_user_delegation_key(response):
559560
def _parse_continuation_token(response):
560561
marker = response.headers.get('x-ms-continuation')
561562
return marker if marker is not '' else None
563+
564+
565+
def _parse_path_permission_and_acl(response):
566+
props = PathProperties()
567+
props.owner = response.headers.get('x-ms-owner')
568+
props.group = response.headers.get('x-ms-group')
569+
props.permissions = response.headers.get('x-ms-permissions')
570+
props.acl = response.headers.get('x-ms-acl')
571+
return props

azure-storage-blob/azure/storage/blob/baseblobservice.py

Lines changed: 161 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
_parse_account_information,
7373
_convert_xml_to_user_delegation_key,
7474
_parse_continuation_token,
75+
_parse_path_permission_and_acl,
7576
)
7677
from ._download_chunking import _download_blob_chunks
7778
from ._error import (
@@ -1545,7 +1546,6 @@ def set_blob_service_properties(
15451546
'''
15461547
if all(parameter is None for parameter in [logging, hour_metrics, minute_metrics, cors, target_version,
15471548
delete_retention_policy, static_website]):
1548-
15491549
raise ValueError("set_blob_service_properties should be called with at least one parameter")
15501550

15511551
request = HTTPRequest()
@@ -3549,19 +3549,19 @@ def delete_directory(self, container_name, directory_path, fail_not_exist=False,
35493549
else:
35503550
return True, self._perform_request(request, parser=_parse_continuation_token)
35513551

3552-
def rename_directory(self, container_name, new_directory_path, source_directory_path,
3553-
mode=None, marker=None, lease_id=None, source_lease_id=None,
3554-
metadata=None, source_if_modified_since=None, source_if_unmodified_since=None,
3555-
source_if_match=None, source_if_none_match=None, timeout=None):
3552+
def rename_path(self, container_name, new_path, source_path,
3553+
mode=None, marker=None, lease_id=None, source_lease_id=None,
3554+
metadata=None, source_if_modified_since=None, source_if_unmodified_since=None,
3555+
source_if_match=None, source_if_none_match=None, timeout=None):
35563556
"""
3557-
Rename a directory which can contain other directories or blobs.
3557+
Rename a blob or directory(which can contain other directories or blobs).
35583558
35593559
:param str container_name:
35603560
Name of existing container.
3561-
:param str new_directory_path:
3562-
New path for source_directory_path. Ex: 'dirfoo/dirsubfoo'.
3563-
:param str source_directory_path:
3564-
Directory to be renamed. Ex: 'dirfoo/dirbar'.
3561+
:param str new_path:
3562+
New path for source_path. Ex: 'dirfoo/dirsubfoo'.
3563+
:param str source_path:
3564+
Path to be renamed. Ex: 'dirfoo/dirbar'.
35653565
:param mode:
35663566
Optional. Valid only when namespace is enabled.
35673567
This parameter determines the behavior of the rename operation.
@@ -3573,13 +3573,13 @@ def rename_directory(self, container_name, new_directory_path, source_directory_
35733573
a continuation token is returned. When a continuation token is returned,
35743574
it must be specified in a subsequent invocation of the rename operation to continue renaming the directory.
35753575
:param str lease_id:
3576-
Optional. A lease ID for the new_directory_path.
3577-
The new_directory_path must have an active lease and the lease ID must match.
3576+
Optional. A lease ID for the new_path.
3577+
The new_path must have an active lease and the lease ID must match.
35783578
:param str source_lease_id:
3579-
Optional. A lease ID for the source_directory_path.
3580-
The source_directory_path must have an active lease and the lease ID must match.
3579+
Optional. A lease ID for the source_path.
3580+
The source_path must have an active lease and the lease ID must match.
35813581
:param metadata:
3582-
Optional. A dict with name_value pairs to associate with the directory as metadata.
3582+
Optional. A dict with name_value pairs to associate with the path as metadata.
35833583
Example:{'Category':'test'}.
35843584
If metadata is specified, it will overwrite the existing metadata;
35853585
otherwise, the existing metadata will be preserved.
@@ -3603,21 +3603,21 @@ def rename_directory(self, container_name, new_directory_path, source_directory_
36033603
A continuation marker if applicable. Otherwise return None.
36043604
:rtype: str
36053605
"""
3606-
_validate_not_none('source_directory_path', source_directory_path)
3607-
_validate_not_none('new_directory_path', new_directory_path)
3606+
_validate_not_none('source_path', source_path)
3607+
_validate_not_none('new_path', new_path)
36083608

36093609
request = HTTPRequest()
36103610
# TODO remove endpoint swapping after service update
36113611
request.host_locations = self._swap_blob_endpoints(self._get_host_locations())
36123612
request.method = 'PUT'
3613-
request.path = _get_path(container_name, new_directory_path)
3613+
request.path = _get_path(container_name, new_path)
36143614
request.query = {
36153615
'mode': mode,
36163616
'continuation': _to_str(marker),
36173617
'timeout': _int_to_str(timeout),
36183618
}
36193619
request.headers = {
3620-
'x-ms-rename-source': _get_path(container_name, source_directory_path),
3620+
'x-ms-rename-source': _get_path(container_name, source_path),
36213621
'x-ms-lease-id': _to_str(lease_id),
36223622
'x-ms-source-lease-id': _to_str(source_lease_id),
36233623
'x-ms-source-if-modified-since': _datetime_to_utc_string(source_if_modified_since),
@@ -3629,6 +3629,148 @@ def rename_directory(self, container_name, new_directory_path, source_directory_
36293629
_add_file_or_directory_properties_header(metadata, request)
36303630
return self._perform_request(request, parser=_parse_continuation_token)
36313631

3632+
def get_path_access_control(self, container_name, path, user_principle_names=False,
3633+
lease_id=None, if_modified_since=None, if_unmodified_since=None,
3634+
if_match=None, if_none_match=None, timeout=None):
3635+
"""
3636+
Retrieve the access control properties of a path(directory or blob).
3637+
3638+
:param str container_name:
3639+
Name of existing container.
3640+
:param str path:
3641+
Path of the directory/blob.
3642+
:param user_principle_names:
3643+
Valid only when Hierarchical Namespace is enabled for the account.
3644+
If "true", the user identity values returned for owner, group, and acl will be transformed
3645+
from Azure Active Directory Object IDs to User Principal Names.
3646+
If "false", the values will be returned as Azure Active Directory Object IDs.
3647+
The default value is false. Note that group and application Object IDs are not translated
3648+
because they do not have unique friendly names.
3649+
:param str lease_id:
3650+
Required if the path has an active lease.
3651+
:param datetime if_modified_since:
3652+
A date and time value. Specify this header to perform the operation only if the resource
3653+
has been modified since the specified date and time.
3654+
:param datetime if_unmodified_since:
3655+
A date and time value. Specify this header to perform the operation only if the resource
3656+
has not been modified since the specified date and time.
3657+
:param datetime if_match:
3658+
An ETag value. Specify this header to perform the operation
3659+
only if the resource's ETag matches the value specified. The ETag must be specified in quotes.
3660+
:param datetime if_none_match:
3661+
An ETag value or the special wildcard ("*") value.
3662+
Specify this header to perform the operation only if the resource's ETag
3663+
does not match the value specified. The ETag must be specified in quotes.
3664+
:param int timeout:
3665+
The timeout parameter is expressed in seconds.
3666+
:return: ETag and last modified time of the new directory.
3667+
:rtype: :class:`~azure.storage.blob.models.ResourceProperties`
3668+
"""
3669+
_validate_not_none('path', path)
3670+
3671+
request = HTTPRequest()
3672+
# TODO remove endpoint swapping after service update
3673+
request.host_locations = self._swap_blob_endpoints(self._get_host_locations())
3674+
request.method = 'HEAD'
3675+
request.path = _get_path(container_name, path)
3676+
request.query = {
3677+
'action': 'getAccessControl',
3678+
'upn': _to_str(user_principle_names),
3679+
'timeout': _int_to_str(timeout),
3680+
}
3681+
request.headers = {
3682+
'x-ms-lease-id': _to_str(lease_id),
3683+
'If-Modified-Since': _datetime_to_utc_string(if_modified_since),
3684+
'If-Unmodified-Since': _datetime_to_utc_string(if_unmodified_since),
3685+
'If-Match': _to_str(if_match),
3686+
'If-None-Match': _to_str(if_none_match),
3687+
}
3688+
return self._perform_request(request, parser=_parse_path_permission_and_acl)
3689+
3690+
def set_path_access_control(self, container_name, path, owner=None, group=None, permissions=None,
3691+
acl=None, lease_id=None, if_modified_since=None, if_unmodified_since=None,
3692+
if_match=None, if_none_match=None, timeout=None):
3693+
"""
3694+
Set the access control properties of a path(directory or blob).
3695+
3696+
:param str container_name:
3697+
Name of existing container.
3698+
:param str path:
3699+
Path of the directory/blob.
3700+
:param str owner:
3701+
Sets the owner of the file or directory.
3702+
:param str group:
3703+
Sets the owning group of the file or directory.
3704+
:param str permissions:
3705+
Invalid in conjunction with acl.
3706+
Sets POSIX access permissions for the file owner, the file owning group, and others.
3707+
Each class may be granted read, write, or execute permission.
3708+
The sticky bit is also supported. Both symbolic (rwxrw-rw-)
3709+
and 4-digit octal notation (e.g. 0766) are supported.
3710+
:param str acl:
3711+
Invalid in conjunction with permissions.
3712+
Sets POSIX access control rights on files and directories.
3713+
The value is a comma-separated list of access control entries that fully replaces the existing
3714+
access control list (ACL). Each access control entry (ACE) consists of a scope, a type,
3715+
a user or group identifier, and permissions in the format "[scope:][type]:[id]:[permissions]".
3716+
The scope must be "default" to indicate the ACE belongs to the default ACL for a directory;
3717+
otherwise scope is implicit and the ACE belongs to the access ACL.
3718+
There are four ACE types: "user" grants rights to the owner or a named user,
3719+
"group" grants rights to the owning group or a named group,
3720+
"mask" restricts rights granted to named users and the members of groups,
3721+
and "other" grants rights to all users not found in any of the other entries.
3722+
The user or group identifier is omitted for entries of type "mask" and "other".
3723+
The user or group identifier is also omitted for the owner and owning group.
3724+
The permission field is a 3-character sequence where the first character is 'r' to grant read access,
3725+
the second character is 'w' to grant write access, and the third character is 'x'
3726+
to grant execute permission. If access is not granted, the '-' character is used to denote
3727+
that the permission is denied. For example, the following ACL grants read, write, and execute rights to
3728+
the file owner and john.doe@contoso, the read right to the owning group,
3729+
and nothing to everyone else: "user::rwx,user:john.doe@contoso:rwx,group::r--,other::---,mask=rwx".
3730+
:param str lease_id:
3731+
Required if the path has an active lease.
3732+
:param datetime if_modified_since:
3733+
A date and time value. Specify this header to perform the operation only if the resource
3734+
has been modified since the specified date and time.
3735+
:param datetime if_unmodified_since:
3736+
A date and time value. Specify this header to perform the operation only if the resource
3737+
has not been modified since the specified date and time.
3738+
:param datetime if_match:
3739+
An ETag value. Specify this header to perform the operation
3740+
only if the resource's ETag matches the value specified. The ETag must be specified in quotes.
3741+
:param datetime if_none_match:
3742+
An ETag value or the special wildcard ("*") value.
3743+
Specify this header to perform the operation only if the resource's ETag
3744+
does not match the value specified. The ETag must be specified in quotes.
3745+
:param int timeout:
3746+
The timeout parameter is expressed in seconds.
3747+
:return: ETag and last modified time of the new directory.
3748+
:rtype: :class:`~azure.storage.blob.models.ResourceProperties`
3749+
"""
3750+
_validate_not_none('path', path)
3751+
3752+
request = HTTPRequest()
3753+
# TODO remove endpoint swapping after service update
3754+
request.host_locations = self._swap_blob_endpoints(self._get_host_locations())
3755+
request.method = 'PATCH'
3756+
request.path = _get_path(container_name, path)
3757+
request.query = {
3758+
'action': 'setAccessControl',
3759+
'timeout': _int_to_str(timeout),
3760+
}
3761+
request.headers = {
3762+
'x-ms-owner': _to_str(owner),
3763+
'x-ms-group': _to_str(group),
3764+
'x-ms-permissions': _to_str(permissions),
3765+
'x-ms-acl': _to_str(acl),
3766+
'x-ms-lease-id': _to_str(lease_id),
3767+
'If-Modified-Since': _datetime_to_utc_string(if_modified_since),
3768+
'If-Unmodified-Since': _datetime_to_utc_string(if_unmodified_since),
3769+
'If-Match': _to_str(if_match),
3770+
'If-None-Match': _to_str(if_none_match),
3771+
}
3772+
return self._perform_request(request, parser=_parse_base_properties)
3773+
36323774
# ----------------------------Helpers for directory manipulations---------------------------- #
36333775
@staticmethod
36343776
def _swap_blob_endpoints(host_locations):

azure-storage-blob/azure/storage/blob/models.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,3 +823,25 @@ def __init__(self):
823823
self.signed_service = None
824824
self.signed_version = None
825825
self.value = None
826+
827+
828+
class PathProperties(object):
829+
"""
830+
Represent a path's properties(only permissions and acl at the moment).
831+
The path can be either a directory or a file.
832+
833+
:ivar string owner:
834+
Represents the owner of the path.
835+
:ivar string group:
836+
Represents the group of the path.
837+
:ivar string permissions:
838+
Represents the permissions of the path.
839+
:ivar string acl:
840+
Represents the acl of the path.
841+
"""
842+
843+
def __init__(self):
844+
self.owner = None
845+
self.group = None
846+
self.permissions = None
847+
self.acl = None

0 commit comments

Comments
 (0)