Skip to content

Commit 10e1302

Browse files
authored
[DelegationSas]support directory sas & add feature for delegation sas (#14206)
* [DelegationSas]support directory sas & add feature for delegation sas * add doc string
1 parent a7907db commit 10e1302

28 files changed

+649
-167
lines changed

sdk/storage/azure-storage-blob/azure/storage/blob/_download.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,7 @@ def readall(self):
457457
"""Download the contents of this blob.
458458
459459
This operation is blocking until all data is downloaded.
460+
460461
:rtype: bytes or str
461462
"""
462463
stream = BytesIO()

sdk/storage/azure-storage-blob/azure/storage/blob/_shared/shared_access_signature.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ class QueryStringConstants(object):
3939
SIGNED_KEY_SERVICE = 'sks'
4040
SIGNED_KEY_VERSION = 'skv'
4141

42+
# for ADLS
43+
SIGNED_AUTHORIZED_OID = 'saoid'
44+
SIGNED_UNAUTHORIZED_OID = 'suoid'
45+
SIGNED_CORRELATION_ID = 'scid'
46+
SIGNED_DIRECTORY_DEPTH = 'sdd'
47+
4248
@staticmethod
4349
def to_list():
4450
return [
@@ -68,6 +74,11 @@ def to_list():
6874
QueryStringConstants.SIGNED_KEY_EXPIRY,
6975
QueryStringConstants.SIGNED_KEY_SERVICE,
7076
QueryStringConstants.SIGNED_KEY_VERSION,
77+
# for ADLS
78+
QueryStringConstants.SIGNED_AUTHORIZED_OID,
79+
QueryStringConstants.SIGNED_UNAUTHORIZED_OID,
80+
QueryStringConstants.SIGNED_CORRELATION_ID,
81+
QueryStringConstants.SIGNED_DIRECTORY_DEPTH,
7182
]
7283

7384

sdk/storage/azure-storage-blob/azure/storage/blob/_shared_access_signature.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def generate_blob(self, container_name, blob_name, snapshot=None, version_id=Non
5555
expiry=None, start=None, policy_id=None, ip=None, protocol=None,
5656
cache_control=None, content_disposition=None,
5757
content_encoding=None, content_language=None,
58-
content_type=None):
58+
content_type=None, **kwargs):
5959
'''
6060
Generates a shared access signature for the blob or one of its snapshots.
6161
Use the returned signature with the sas_token parameter of any BlobService.
@@ -126,12 +126,14 @@ def generate_blob(self, container_name, blob_name, snapshot=None, version_id=Non
126126

127127
resource = 'bs' if snapshot else 'b'
128128
resource = 'bv' if version_id else resource
129+
resource = 'd' if kwargs.pop("is_directory", None) else resource
129130
sas.add_resource(resource)
130131

131132
sas.add_timestamp(snapshot or version_id)
132133
sas.add_override_response_headers(cache_control, content_disposition,
133134
content_encoding, content_language,
134135
content_type)
136+
sas.add_info_for_hns_account(**kwargs)
135137
sas.add_resource_signature(self.account_name, self.account_key, resource_path,
136138
user_delegation_key=self.user_delegation_key)
137139

@@ -141,7 +143,7 @@ def generate_container(self, container_name, permission=None, expiry=None,
141143
start=None, policy_id=None, ip=None, protocol=None,
142144
cache_control=None, content_disposition=None,
143145
content_encoding=None, content_language=None,
144-
content_type=None):
146+
content_type=None, **kwargs):
145147
'''
146148
Generates a shared access signature for the container.
147149
Use the returned signature with the sas_token parameter of any BlobService.
@@ -206,6 +208,7 @@ def generate_container(self, container_name, permission=None, expiry=None,
206208
sas.add_override_response_headers(cache_control, content_disposition,
207209
content_encoding, content_language,
208210
content_type)
211+
sas.add_info_for_hns_account(**kwargs)
209212
sas.add_resource_signature(self.account_name, self.account_key, container_name,
210213
user_delegation_key=self.user_delegation_key)
211214
return sas.get_token()
@@ -216,6 +219,12 @@ class _BlobSharedAccessHelper(_SharedAccessHelper):
216219
def add_timestamp(self, timestamp):
217220
self._add_query(BlobQueryStringConstants.SIGNED_TIMESTAMP, timestamp)
218221

222+
def add_info_for_hns_account(self, **kwargs):
223+
self._add_query(QueryStringConstants.SIGNED_DIRECTORY_DEPTH, kwargs.pop('sdd', None))
224+
self._add_query(QueryStringConstants.SIGNED_AUTHORIZED_OID, kwargs.pop('preauthorized_agent_object_id', None))
225+
self._add_query(QueryStringConstants.SIGNED_UNAUTHORIZED_OID, kwargs.pop('agent_object_id', None))
226+
self._add_query(QueryStringConstants.SIGNED_CORRELATION_ID, kwargs.pop('correlation_id', None))
227+
219228
def get_value_to_append(self, query):
220229
return_value = self.query_dict.get(query) or ''
221230
return return_value + '\n'
@@ -249,7 +258,10 @@ def add_resource_signature(self, account_name, account_key, path, user_delegatio
249258
self.get_value_to_append(QueryStringConstants.SIGNED_KEY_START) +
250259
self.get_value_to_append(QueryStringConstants.SIGNED_KEY_EXPIRY) +
251260
self.get_value_to_append(QueryStringConstants.SIGNED_KEY_SERVICE) +
252-
self.get_value_to_append(QueryStringConstants.SIGNED_KEY_VERSION))
261+
self.get_value_to_append(QueryStringConstants.SIGNED_KEY_VERSION) +
262+
self.get_value_to_append(QueryStringConstants.SIGNED_AUTHORIZED_OID) +
263+
self.get_value_to_append(QueryStringConstants.SIGNED_UNAUTHORIZED_OID) +
264+
self.get_value_to_append(QueryStringConstants.SIGNED_CORRELATION_ID))
253265
else:
254266
string_to_sign += self.get_value_to_append(QueryStringConstants.SIGNED_IDENTIFIER)
255267

sdk/storage/azure-storage-blob/tests/test_blob_access_conditions.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,7 @@ def test_set_blob_properties_with_if_unmodified_fail(self, resource_group, locat
621621
# Assert
622622
self.assertEqual(StorageErrorCode.condition_not_met, e.exception.error_code)
623623

624+
@pytest.mark.playback_test_only
624625
@GlobalStorageAccountPreparer()
625626
def test_get_properties_last_access_time(self, resource_group, location, storage_account, storage_account_key):
626627
bsc = BlobServiceClient(self.account_url(storage_account, "blob"), storage_account_key,

sdk/storage/azure-storage-blob/tests/test_container.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,7 @@ def test_list_names(self, resource_group, location, storage_account, storage_acc
827827

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

830+
@pytest.mark.playback_test_only
830831
@GlobalStorageAccountPreparer()
831832
def test_list_blobs_contains_last_access_time(self, resource_group, location, storage_account, storage_account_key):
832833
bsc = BlobServiceClient(self.account_url(storage_account, "blob"), storage_account_key)

sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_data_lake_directory_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ def rename_directory(self, new_name, # type: str
304304
"""
305305
new_name = new_name.strip('/')
306306
new_file_system = new_name.split('/')[0]
307-
new_path_and_token = new_name[len(new_file_system):].split('?')
307+
new_path_and_token = new_name[len(new_file_system):].strip('/').split('?')
308308
new_path = new_path_and_token[0]
309309
try:
310310
new_dir_sas = new_path_and_token[1] or self._query_str.strip('?')

sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_data_lake_file_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ def rename_file(self, new_name, # type: str
661661
"""
662662
new_name = new_name.strip('/')
663663
new_file_system = new_name.split('/')[0]
664-
new_path_and_token = new_name[len(new_file_system):].split('?')
664+
new_path_and_token = new_name[len(new_file_system):].strip('/').split('?')
665665
new_path = new_path_and_token[0]
666666
try:
667667
new_file_sas = new_path_and_token[1] or self._query_str.strip('?')

sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_models.py

Lines changed: 193 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from azure.storage.blob import ResourceTypes as BlobResourceTypes
1313
from azure.storage.blob import UserDelegationKey as BlobUserDelegationKey
1414
from azure.storage.blob import ContentSettings as BlobContentSettings
15-
from azure.storage.blob import ContainerSasPermissions, BlobSasPermissions
1615
from azure.storage.blob import AccessPolicy as BlobAccessPolicy
1716
from azure.storage.blob import DelimitedTextDialect as BlobDelimitedTextDialect
1817
from azure.storage.blob import DelimitedJsonDialect as BlobDelimitedJSON
@@ -294,7 +293,7 @@ def __init__(self, read=False, write=False, delete=False, list=False, # pylint:
294293
)
295294

296295

297-
class FileSystemSasPermissions(ContainerSasPermissions):
296+
class FileSystemSasPermissions(object):
298297
"""FileSystemSasPermissions class to be used with the
299298
:func:`~azure.storage.filedatalake.generate_file_system_sas` function.
300299
@@ -306,16 +305,72 @@ class FileSystemSasPermissions(ContainerSasPermissions):
306305
Delete the file system.
307306
:param bool list:
308307
List paths in the file system.
308+
:keyword bool move:
309+
Move any file in the directory to a new location.
310+
Note the move operation can optionally be restricted to the child file or directory owner or
311+
the parent directory owner if the saoid parameter is included in the token and the sticky bit is set
312+
on the parent directory.
313+
:keyword bool execute:
314+
Get the status (system defined properties) and ACL of any file in the directory.
315+
If the caller is the owner, set access control on any file in the directory.
316+
:keyword bool manage_ownership:
317+
Allows the user to set owner, owning group, or act as the owner when renaming or deleting a file or directory
318+
within a folder that has the sticky bit set.
319+
:keyword bool manage_access_control:
320+
Allows the user to set permissions and POSIX ACLs on files and directories.
309321
"""
310322

311-
def __init__(self, read=False, write=False, delete=False, list=False # pylint: disable=redefined-builtin
312-
):
313-
super(FileSystemSasPermissions, self).__init__(
314-
read=read, write=write, delete=delete, list=list
315-
)
316-
323+
def __init__(self, read=False, write=False, delete=False, list=False, # pylint: disable=redefined-builtin
324+
**kwargs):
325+
self.read = read
326+
self.write = write
327+
self.delete = delete
328+
self.list = list
329+
self.move = kwargs.pop('move', None)
330+
self.execute = kwargs.pop('execute', None)
331+
self.manage_ownership = kwargs.pop('manage_ownership', None)
332+
self.manage_access_control = kwargs.pop('manage_access_control', None)
333+
self._str = (('r' if self.read else '') +
334+
('w' if self.write else '') +
335+
('d' if self.delete else '') +
336+
('l' if self.list else '') +
337+
('m' if self.move else '') +
338+
('e' if self.execute else '') +
339+
('o' if self.manage_ownership else '') +
340+
('p' if self.manage_access_control else ''))
341+
342+
def __str__(self):
343+
return self._str
317344

318-
class DirectorySasPermissions(BlobSasPermissions):
345+
@classmethod
346+
def from_string(cls, permission):
347+
"""Create a FileSystemSasPermissions from a string.
348+
349+
To specify read, write, or delete permissions you need only to
350+
include the first letter of the word in the string. E.g. For read and
351+
write permissions, you would provide a string "rw".
352+
353+
:param str permission: The string which dictates the read, add, create,
354+
write, or delete permissions.
355+
:return: A FileSystemSasPermissions object
356+
:rtype: ~azure.storage.fildatalake.FileSystemSasPermissions
357+
"""
358+
p_read = 'r' in permission
359+
p_write = 'w' in permission
360+
p_delete = 'd' in permission
361+
p_list = 'l' in permission
362+
p_move = 'm' in permission
363+
p_execute = 'e' in permission
364+
p_manage_ownership = 'o' in permission
365+
p_manage_access_control = 'p' in permission
366+
367+
parsed = cls(read=p_read, write=p_write, delete=p_delete,
368+
list=p_list, move=p_move, execute=p_execute, manage_ownership=p_manage_ownership,
369+
manage_access_control=p_manage_access_control)
370+
return parsed
371+
372+
373+
class DirectorySasPermissions(object):
319374
"""DirectorySasPermissions class to be used with the
320375
:func:`~azure.storage.filedatalake.generate_directory_sas` function.
321376
@@ -327,17 +382,77 @@ class DirectorySasPermissions(BlobSasPermissions):
327382
Create or write content, properties, metadata. Lease the directory.
328383
:param bool delete:
329384
Delete the directory.
385+
:keyword bool list:
386+
List any files in the directory. Implies Execute.
387+
:keyword bool move:
388+
Move any file in the directory to a new location.
389+
Note the move operation can optionally be restricted to the child file or directory owner or
390+
the parent directory owner if the saoid parameter is included in the token and the sticky bit is set
391+
on the parent directory.
392+
:keyword bool execute:
393+
Get the status (system defined properties) and ACL of any file in the directory.
394+
If the caller is the owner, set access control on any file in the directory.
395+
:keyword bool manage_ownership:
396+
Allows the user to set owner, owning group, or act as the owner when renaming or deleting a file or directory
397+
within a folder that has the sticky bit set.
398+
:keyword bool manage_access_control:
399+
Allows the user to set permissions and POSIX ACLs on files and directories.
330400
"""
331401

332402
def __init__(self, read=False, create=False, write=False,
333-
delete=False):
334-
super(DirectorySasPermissions, self).__init__(
335-
read=read, create=create, write=write,
336-
delete=delete
337-
)
403+
delete=False, **kwargs):
404+
self.read = read
405+
self.create = create
406+
self.write = write
407+
self.delete = delete
408+
self.list = kwargs.pop('list', None)
409+
self.move = kwargs.pop('move', None)
410+
self.execute = kwargs.pop('execute', None)
411+
self.manage_ownership = kwargs.pop('manage_ownership', None)
412+
self.manage_access_control = kwargs.pop('manage_access_control', None)
413+
self._str = (('r' if self.read else '') +
414+
('c' if self.create else '') +
415+
('w' if self.write else '') +
416+
('d' if self.delete else '') +
417+
('l' if self.list else '') +
418+
('m' if self.move else '') +
419+
('e' if self.execute else '') +
420+
('o' if self.manage_ownership else '') +
421+
('p' if self.manage_access_control else ''))
422+
423+
def __str__(self):
424+
return self._str
338425

339-
340-
class FileSasPermissions(BlobSasPermissions):
426+
@classmethod
427+
def from_string(cls, permission):
428+
"""Create a DirectorySasPermissions from a string.
429+
430+
To specify read, create, write, or delete permissions you need only to
431+
include the first letter of the word in the string. E.g. For read and
432+
write permissions, you would provide a string "rw".
433+
434+
:param str permission: The string which dictates the read, add, create,
435+
write, or delete permissions.
436+
:return: A DirectorySasPermissions object
437+
:rtype: ~azure.storage.filedatalake.DirectorySasPermissions
438+
"""
439+
p_read = 'r' in permission
440+
p_create = 'c' in permission
441+
p_write = 'w' in permission
442+
p_delete = 'd' in permission
443+
p_list = 'l' in permission
444+
p_move = 'm' in permission
445+
p_execute = 'e' in permission
446+
p_manage_ownership = 'o' in permission
447+
p_manage_access_control = 'p' in permission
448+
449+
parsed = cls(read=p_read, create=p_create, write=p_write, delete=p_delete,
450+
list=p_list, move=p_move, execute=p_execute, manage_ownership=p_manage_ownership,
451+
manage_access_control=p_manage_access_control)
452+
return parsed
453+
454+
455+
class FileSasPermissions(object):
341456
"""FileSasPermissions class to be used with the
342457
:func:`~azure.storage.filedatalake.generate_file_sas` function.
343458
@@ -350,14 +465,69 @@ class FileSasPermissions(BlobSasPermissions):
350465
Create or write content, properties, metadata. Lease the file.
351466
:param bool delete:
352467
Delete the file.
353-
"""
468+
:keyword bool move:
469+
Move any file in the directory to a new location.
470+
Note the move operation can optionally be restricted to the child file or directory owner or
471+
the parent directory owner if the saoid parameter is included in the token and the sticky bit is set
472+
on the parent directory.
473+
:keyword bool execute:
474+
Get the status (system defined properties) and ACL of any file in the directory.
475+
If the caller is the owner, set access control on any file in the directory.
476+
:keyword bool manage_ownership:
477+
Allows the user to set owner, owning group, or act as the owner when renaming or deleting a file or directory
478+
within a folder that has the sticky bit set.
479+
:keyword bool manage_access_control:
480+
Allows the user to set permissions and POSIX ACLs on files and directories.
481+
"""
482+
483+
def __init__(self, read=False, create=False, write=False, delete=False, **kwargs):
484+
self.read = read
485+
self.create = create
486+
self.write = write
487+
self.delete = delete
488+
self.list = list
489+
self.move = kwargs.pop('move', None)
490+
self.execute = kwargs.pop('execute', None)
491+
self.manage_ownership = kwargs.pop('manage_ownership', None)
492+
self.manage_access_control = kwargs.pop('manage_access_control', None)
493+
self._str = (('r' if self.read else '') +
494+
('c' if self.create else '') +
495+
('w' if self.write else '') +
496+
('d' if self.delete else '') +
497+
('m' if self.move else '') +
498+
('e' if self.execute else '') +
499+
('o' if self.manage_ownership else '') +
500+
('p' if self.manage_access_control else ''))
501+
502+
def __str__(self):
503+
return self._str
354504

355-
def __init__(self, read=False, create=False, write=False,
356-
delete=False):
357-
super(FileSasPermissions, self).__init__(
358-
read=read, create=create, write=write,
359-
delete=delete
360-
)
505+
@classmethod
506+
def from_string(cls, permission):
507+
"""Create a FileSasPermissions from a string.
508+
509+
To specify read, write, or delete permissions you need only to
510+
include the first letter of the word in the string. E.g. For read and
511+
write permissions, you would provide a string "rw".
512+
513+
:param str permission: The string which dictates the read, add, create,
514+
write, or delete permissions.
515+
:return: A FileSasPermissions object
516+
:rtype: ~azure.storage.fildatalake.FileSasPermissions
517+
"""
518+
p_read = 'r' in permission
519+
p_create = 'c' in permission
520+
p_write = 'w' in permission
521+
p_delete = 'd' in permission
522+
p_move = 'm' in permission
523+
p_execute = 'e' in permission
524+
p_manage_ownership = 'o' in permission
525+
p_manage_access_control = 'p' in permission
526+
527+
parsed = cls(read=p_read, create=p_create, write=p_write, delete=p_delete,
528+
move=p_move, execute=p_execute, manage_ownership=p_manage_ownership,
529+
manage_access_control=p_manage_access_control)
530+
return parsed
361531

362532

363533
class AccessPolicy(BlobAccessPolicy):

0 commit comments

Comments
 (0)