Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DelegationSas]support directory sas & add feature for delegation sas #4

Draft
wants to merge 2 commits into
base: merge
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ def readall(self):
"""Download the contents of this blob.

This operation is blocking until all data is downloaded.

:rtype: bytes or str
"""
stream = BytesIO()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ class QueryStringConstants(object):
SIGNED_KEY_SERVICE = 'sks'
SIGNED_KEY_VERSION = 'skv'

# for ADLS
SIGNED_AUTHORIZED_OID = 'saoid'
SIGNED_UNAUTHORIZED_OID = 'suoid'
SIGNED_CORRELATION_ID = 'scid'
SIGNED_DIRECTORY_DEPTH = 'sdd'

@staticmethod
def to_list():
return [
Expand Down Expand Up @@ -68,6 +74,11 @@ def to_list():
QueryStringConstants.SIGNED_KEY_EXPIRY,
QueryStringConstants.SIGNED_KEY_SERVICE,
QueryStringConstants.SIGNED_KEY_VERSION,
# for ADLS
QueryStringConstants.SIGNED_AUTHORIZED_OID,
QueryStringConstants.SIGNED_UNAUTHORIZED_OID,
QueryStringConstants.SIGNED_CORRELATION_ID,
QueryStringConstants.SIGNED_DIRECTORY_DEPTH,
]


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def generate_blob(self, container_name, blob_name, snapshot=None, version_id=Non
expiry=None, start=None, policy_id=None, ip=None, protocol=None,
cache_control=None, content_disposition=None,
content_encoding=None, content_language=None,
content_type=None):
content_type=None, **kwargs):
'''
Generates a shared access signature for the blob or one of its snapshots.
Use the returned signature with the sas_token parameter of any BlobService.
Expand Down Expand Up @@ -126,12 +126,14 @@ def generate_blob(self, container_name, blob_name, snapshot=None, version_id=Non

resource = 'bs' if snapshot else 'b'
resource = 'bv' if version_id else resource
resource = 'd' if kwargs.pop("is_directory", None) else resource
sas.add_resource(resource)

sas.add_timestamp(snapshot or version_id)
sas.add_override_response_headers(cache_control, content_disposition,
content_encoding, content_language,
content_type)
sas.add_info_for_hns_account(**kwargs)
sas.add_resource_signature(self.account_name, self.account_key, resource_path,
user_delegation_key=self.user_delegation_key)

Expand All @@ -141,7 +143,7 @@ def generate_container(self, container_name, permission=None, expiry=None,
start=None, policy_id=None, ip=None, protocol=None,
cache_control=None, content_disposition=None,
content_encoding=None, content_language=None,
content_type=None):
content_type=None, **kwargs):
'''
Generates a shared access signature for the container.
Use the returned signature with the sas_token parameter of any BlobService.
Expand Down Expand Up @@ -206,6 +208,7 @@ def generate_container(self, container_name, permission=None, expiry=None,
sas.add_override_response_headers(cache_control, content_disposition,
content_encoding, content_language,
content_type)
sas.add_info_for_hns_account(**kwargs)
sas.add_resource_signature(self.account_name, self.account_key, container_name,
user_delegation_key=self.user_delegation_key)
return sas.get_token()
Expand All @@ -216,6 +219,12 @@ class _BlobSharedAccessHelper(_SharedAccessHelper):
def add_timestamp(self, timestamp):
self._add_query(BlobQueryStringConstants.SIGNED_TIMESTAMP, timestamp)

def add_info_for_hns_account(self, **kwargs):
self._add_query(QueryStringConstants.SIGNED_DIRECTORY_DEPTH, kwargs.pop('sdd', None))
self._add_query(QueryStringConstants.SIGNED_AUTHORIZED_OID, kwargs.pop('preauthorized_agent_object_id', None))
self._add_query(QueryStringConstants.SIGNED_UNAUTHORIZED_OID, kwargs.pop('agent_object_id', None))
self._add_query(QueryStringConstants.SIGNED_CORRELATION_ID, kwargs.pop('correlation_id', None))

def get_value_to_append(self, query):
return_value = self.query_dict.get(query) or ''
return return_value + '\n'
Expand Down Expand Up @@ -249,7 +258,10 @@ def add_resource_signature(self, account_name, account_key, path, user_delegatio
self.get_value_to_append(QueryStringConstants.SIGNED_KEY_START) +
self.get_value_to_append(QueryStringConstants.SIGNED_KEY_EXPIRY) +
self.get_value_to_append(QueryStringConstants.SIGNED_KEY_SERVICE) +
self.get_value_to_append(QueryStringConstants.SIGNED_KEY_VERSION))
self.get_value_to_append(QueryStringConstants.SIGNED_KEY_VERSION) +
self.get_value_to_append(QueryStringConstants.SIGNED_AUTHORIZED_OID) +
self.get_value_to_append(QueryStringConstants.SIGNED_UNAUTHORIZED_OID) +
self.get_value_to_append(QueryStringConstants.SIGNED_CORRELATION_ID))
else:
string_to_sign += self.get_value_to_append(QueryStringConstants.SIGNED_IDENTIFIER)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@ def test_set_blob_properties_with_if_unmodified_fail(self, resource_group, locat
# Assert
self.assertEqual(StorageErrorCode.condition_not_met, e.exception.error_code)

@pytest.mark.playback_test_only
@GlobalStorageAccountPreparer()
def test_get_properties_last_access_time(self, resource_group, location, storage_account, storage_account_key):
bsc = BlobServiceClient(self.account_url(storage_account, "blob"), storage_account_key,
Expand Down
1 change: 1 addition & 0 deletions sdk/storage/azure-storage-blob/tests/test_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,7 @@ def test_list_names(self, resource_group, location, storage_account, storage_acc

self.assertEqual(blobs, ['blob1', 'blob2'])

@pytest.mark.playback_test_only
@GlobalStorageAccountPreparer()
def test_list_blobs_contains_last_access_time(self, resource_group, location, storage_account, storage_account_key):
bsc = BlobServiceClient(self.account_url(storage_account, "blob"), storage_account_key)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ def rename_directory(self, new_name, # type: str
"""
new_name = new_name.strip('/')
new_file_system = new_name.split('/')[0]
new_path_and_token = new_name[len(new_file_system):].split('?')
new_path_and_token = new_name[len(new_file_system):].strip('/').split('?')
new_path = new_path_and_token[0]
try:
new_dir_sas = new_path_and_token[1] or self._query_str.strip('?')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ def rename_file(self, new_name, # type: str
"""
new_name = new_name.strip('/')
new_file_system = new_name.split('/')[0]
new_path_and_token = new_name[len(new_file_system):].split('?')
new_path_and_token = new_name[len(new_file_system):].strip('/').split('?')
new_path = new_path_and_token[0]
try:
new_file_sas = new_path_and_token[1] or self._query_str.strip('?')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from azure.storage.blob import ResourceTypes as BlobResourceTypes
from azure.storage.blob import UserDelegationKey as BlobUserDelegationKey
from azure.storage.blob import ContentSettings as BlobContentSettings
from azure.storage.blob import ContainerSasPermissions, BlobSasPermissions
from azure.storage.blob import AccessPolicy as BlobAccessPolicy
from azure.storage.blob import DelimitedTextDialect as BlobDelimitedTextDialect
from azure.storage.blob import DelimitedJsonDialect as BlobDelimitedJSON
Expand Down Expand Up @@ -294,7 +293,7 @@ def __init__(self, read=False, write=False, delete=False, list=False, # pylint:
)


class FileSystemSasPermissions(ContainerSasPermissions):
class FileSystemSasPermissions(object):
"""FileSystemSasPermissions class to be used with the
:func:`~azure.storage.filedatalake.generate_file_system_sas` function.

Expand All @@ -306,16 +305,72 @@ class FileSystemSasPermissions(ContainerSasPermissions):
Delete the file system.
:param bool list:
List paths in the file system.
:keyword bool move:
Move any file in the directory to a new location.
Note the move operation can optionally be restricted to the child file or directory owner or
the parent directory owner if the saoid parameter is included in the token and the sticky bit is set
on the parent directory.
:keyword bool execute:
Get the status (system defined properties) and ACL of any file in the directory.
If the caller is the owner, set access control on any file in the directory.
:keyword bool manage_ownership:
Allows the user to set owner, owning group, or act as the owner when renaming or deleting a file or directory
within a folder that has the sticky bit set.
:keyword bool manage_access_control:
Allows the user to set permissions and POSIX ACLs on files and directories.
"""

def __init__(self, read=False, write=False, delete=False, list=False # pylint: disable=redefined-builtin
):
super(FileSystemSasPermissions, self).__init__(
read=read, write=write, delete=delete, list=list
)

def __init__(self, read=False, write=False, delete=False, list=False, # pylint: disable=redefined-builtin
**kwargs):
self.read = read
self.write = write
self.delete = delete
self.list = list
self.move = kwargs.pop('move', None)
self.execute = kwargs.pop('execute', None)
self.manage_ownership = kwargs.pop('manage_ownership', None)
self.manage_access_control = kwargs.pop('manage_access_control', None)
self._str = (('r' if self.read else '') +
('w' if self.write else '') +
('d' if self.delete else '') +
('l' if self.list else '') +
('m' if self.move else '') +
('e' if self.execute else '') +
('o' if self.manage_ownership else '') +
('p' if self.manage_access_control else ''))

def __str__(self):
return self._str

class DirectorySasPermissions(BlobSasPermissions):
@classmethod
def from_string(cls, permission):
"""Create a FileSystemSasPermissions from a string.

To specify read, write, or delete permissions you need only to
include the first letter of the word in the string. E.g. For read and
write permissions, you would provide a string "rw".

:param str permission: The string which dictates the read, add, create,
write, or delete permissions.
:return: A FileSystemSasPermissions object
:rtype: ~azure.storage.fildatalake.FileSystemSasPermissions
"""
p_read = 'r' in permission
p_write = 'w' in permission
p_delete = 'd' in permission
p_list = 'l' in permission
p_move = 'm' in permission
p_execute = 'e' in permission
p_manage_ownership = 'o' in permission
p_manage_access_control = 'p' in permission

parsed = cls(read=p_read, write=p_write, delete=p_delete,
list=p_list, move=p_move, execute=p_execute, manage_ownership=p_manage_ownership,
manage_access_control=p_manage_access_control)
return parsed


class DirectorySasPermissions(object):
"""DirectorySasPermissions class to be used with the
:func:`~azure.storage.filedatalake.generate_directory_sas` function.

Expand All @@ -327,17 +382,77 @@ class DirectorySasPermissions(BlobSasPermissions):
Create or write content, properties, metadata. Lease the directory.
:param bool delete:
Delete the directory.
:keyword bool list:
List any files in the directory. Implies Execute.
:keyword bool move:
Move any file in the directory to a new location.
Note the move operation can optionally be restricted to the child file or directory owner or
the parent directory owner if the saoid parameter is included in the token and the sticky bit is set
on the parent directory.
:keyword bool execute:
Get the status (system defined properties) and ACL of any file in the directory.
If the caller is the owner, set access control on any file in the directory.
:keyword bool manage_ownership:
Allows the user to set owner, owning group, or act as the owner when renaming or deleting a file or directory
within a folder that has the sticky bit set.
:keyword bool manage_access_control:
Allows the user to set permissions and POSIX ACLs on files and directories.
"""

def __init__(self, read=False, create=False, write=False,
delete=False):
super(DirectorySasPermissions, self).__init__(
read=read, create=create, write=write,
delete=delete
)
delete=False, **kwargs):
self.read = read
self.create = create
self.write = write
self.delete = delete
self.list = kwargs.pop('list', None)
self.move = kwargs.pop('move', None)
self.execute = kwargs.pop('execute', None)
self.manage_ownership = kwargs.pop('manage_ownership', None)
self.manage_access_control = kwargs.pop('manage_access_control', None)
self._str = (('r' if self.read else '') +
('c' if self.create else '') +
('w' if self.write else '') +
('d' if self.delete else '') +
('l' if self.list else '') +
('m' if self.move else '') +
('e' if self.execute else '') +
('o' if self.manage_ownership else '') +
('p' if self.manage_access_control else ''))

def __str__(self):
return self._str


class FileSasPermissions(BlobSasPermissions):
@classmethod
def from_string(cls, permission):
"""Create a DirectorySasPermissions from a string.

To specify read, create, write, or delete permissions you need only to
include the first letter of the word in the string. E.g. For read and
write permissions, you would provide a string "rw".

:param str permission: The string which dictates the read, add, create,
write, or delete permissions.
:return: A DirectorySasPermissions object
:rtype: ~azure.storage.filedatalake.DirectorySasPermissions
"""
p_read = 'r' in permission
p_create = 'c' in permission
p_write = 'w' in permission
p_delete = 'd' in permission
p_list = 'l' in permission
p_move = 'm' in permission
p_execute = 'e' in permission
p_manage_ownership = 'o' in permission
p_manage_access_control = 'p' in permission

parsed = cls(read=p_read, create=p_create, write=p_write, delete=p_delete,
list=p_list, move=p_move, execute=p_execute, manage_ownership=p_manage_ownership,
manage_access_control=p_manage_access_control)
return parsed


class FileSasPermissions(object):
"""FileSasPermissions class to be used with the
:func:`~azure.storage.filedatalake.generate_file_sas` function.

Expand All @@ -350,14 +465,69 @@ class FileSasPermissions(BlobSasPermissions):
Create or write content, properties, metadata. Lease the file.
:param bool delete:
Delete the file.
"""
:keyword bool move:
Move any file in the directory to a new location.
Note the move operation can optionally be restricted to the child file or directory owner or
the parent directory owner if the saoid parameter is included in the token and the sticky bit is set
on the parent directory.
:keyword bool execute:
Get the status (system defined properties) and ACL of any file in the directory.
If the caller is the owner, set access control on any file in the directory.
:keyword bool manage_ownership:
Allows the user to set owner, owning group, or act as the owner when renaming or deleting a file or directory
within a folder that has the sticky bit set.
:keyword bool manage_access_control:
Allows the user to set permissions and POSIX ACLs on files and directories.
"""

def __init__(self, read=False, create=False, write=False, delete=False, **kwargs):
self.read = read
self.create = create
self.write = write
self.delete = delete
self.list = list
self.move = kwargs.pop('move', None)
self.execute = kwargs.pop('execute', None)
self.manage_ownership = kwargs.pop('manage_ownership', None)
self.manage_access_control = kwargs.pop('manage_access_control', None)
self._str = (('r' if self.read else '') +
('c' if self.create else '') +
('w' if self.write else '') +
('d' if self.delete else '') +
('m' if self.move else '') +
('e' if self.execute else '') +
('o' if self.manage_ownership else '') +
('p' if self.manage_access_control else ''))

def __str__(self):
return self._str

def __init__(self, read=False, create=False, write=False,
delete=False):
super(FileSasPermissions, self).__init__(
read=read, create=create, write=write,
delete=delete
)
@classmethod
def from_string(cls, permission):
"""Create a FileSasPermissions from a string.

To specify read, write, or delete permissions you need only to
include the first letter of the word in the string. E.g. For read and
write permissions, you would provide a string "rw".

:param str permission: The string which dictates the read, add, create,
write, or delete permissions.
:return: A FileSasPermissions object
:rtype: ~azure.storage.fildatalake.FileSasPermissions
"""
p_read = 'r' in permission
p_create = 'c' in permission
p_write = 'w' in permission
p_delete = 'd' in permission
p_move = 'm' in permission
p_execute = 'e' in permission
p_manage_ownership = 'o' in permission
p_manage_access_control = 'p' in permission

parsed = cls(read=p_read, create=p_create, write=p_write, delete=p_delete,
move=p_move, execute=p_execute, manage_ownership=p_manage_ownership,
manage_access_control=p_manage_access_control)
return parsed


class AccessPolicy(BlobAccessPolicy):
Expand Down
Loading