azure-storage-blob 12.22.0__py3-none-any.whl → 12.23.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- azure/storage/blob/_container_client.py +6 -0
- azure/storage/blob/_container_client_helpers.py +7 -2
- azure/storage/blob/_generated/_azure_blob_storage.py +2 -1
- azure/storage/blob/_generated/_serialization.py +2 -0
- azure/storage/blob/_generated/aio/_azure_blob_storage.py +2 -1
- azure/storage/blob/_generated/aio/operations/_append_blob_operations.py +1 -7
- azure/storage/blob/_generated/aio/operations/_blob_operations.py +21 -47
- azure/storage/blob/_generated/aio/operations/_block_blob_operations.py +2 -10
- azure/storage/blob/_generated/aio/operations/_container_operations.py +13 -26
- azure/storage/blob/_generated/aio/operations/_page_blob_operations.py +3 -14
- azure/storage/blob/_generated/aio/operations/_service_operations.py +14 -17
- azure/storage/blob/_generated/operations/_append_blob_operations.py +1 -7
- azure/storage/blob/_generated/operations/_blob_operations.py +21 -47
- azure/storage/blob/_generated/operations/_block_blob_operations.py +2 -10
- azure/storage/blob/_generated/operations/_container_operations.py +13 -26
- azure/storage/blob/_generated/operations/_page_blob_operations.py +3 -14
- azure/storage/blob/_generated/operations/_service_operations.py +14 -17
- azure/storage/blob/_serialize.py +1 -0
- azure/storage/blob/_shared/base_client.py +2 -0
- azure/storage/blob/_shared/policies.py +8 -9
- azure/storage/blob/_shared/policies_async.py +18 -5
- azure/storage/blob/_shared/shared_access_signature.py +20 -2
- azure/storage/blob/_shared_access_signature.py +42 -1
- azure/storage/blob/_version.py +1 -1
- azure/storage/blob/aio/_container_client_async.py +6 -0
- azure/storage/blob/aio/_download_async.py +94 -71
- {azure_storage_blob-12.22.0.dist-info → azure_storage_blob-12.23.0.dist-info}/METADATA +3 -3
- {azure_storage_blob-12.22.0.dist-info → azure_storage_blob-12.23.0.dist-info}/RECORD +31 -32
- {azure_storage_blob-12.22.0.dist-info → azure_storage_blob-12.23.0.dist-info}/WHEEL +1 -1
- azure/storage/blob/_generated/_vendor.py +0 -16
- {azure_storage_blob-12.22.0.dist-info → azure_storage_blob-12.23.0.dist-info}/LICENSE +0 -0
- {azure_storage_blob-12.22.0.dist-info → azure_storage_blob-12.23.0.dist-info}/top_level.txt +0 -0
@@ -15,17 +15,17 @@ from azure.core.exceptions import (
|
|
15
15
|
ResourceExistsError,
|
16
16
|
ResourceNotFoundError,
|
17
17
|
ResourceNotModifiedError,
|
18
|
+
StreamClosedError,
|
19
|
+
StreamConsumedError,
|
18
20
|
map_error,
|
19
21
|
)
|
20
22
|
from azure.core.pipeline import PipelineResponse
|
21
|
-
from azure.core.
|
22
|
-
from azure.core.rest import HttpRequest
|
23
|
+
from azure.core.rest import HttpRequest, HttpResponse
|
23
24
|
from azure.core.tracing.decorator import distributed_trace
|
24
25
|
from azure.core.utils import case_insensitive_dict
|
25
26
|
|
26
27
|
from .. import models as _models
|
27
28
|
from .._serialization import Serializer
|
28
|
-
from .._vendor import _convert_request
|
29
29
|
|
30
30
|
if sys.version_info >= (3, 9):
|
31
31
|
from collections.abc import MutableMapping
|
@@ -427,7 +427,6 @@ class ServiceOperations:
|
|
427
427
|
headers=_headers,
|
428
428
|
params=_params,
|
429
429
|
)
|
430
|
-
_request = _convert_request(_request)
|
431
430
|
_request.url = self._client.format_url(_request.url)
|
432
431
|
|
433
432
|
_stream = False
|
@@ -497,7 +496,6 @@ class ServiceOperations:
|
|
497
496
|
headers=_headers,
|
498
497
|
params=_params,
|
499
498
|
)
|
500
|
-
_request = _convert_request(_request)
|
501
499
|
_request.url = self._client.format_url(_request.url)
|
502
500
|
|
503
501
|
_stream = False
|
@@ -519,7 +517,7 @@ class ServiceOperations:
|
|
519
517
|
response_headers["x-ms-request-id"] = self._deserialize("str", response.headers.get("x-ms-request-id"))
|
520
518
|
response_headers["x-ms-version"] = self._deserialize("str", response.headers.get("x-ms-version"))
|
521
519
|
|
522
|
-
deserialized = self._deserialize("StorageServiceProperties", pipeline_response)
|
520
|
+
deserialized = self._deserialize("StorageServiceProperties", pipeline_response.http_response)
|
523
521
|
|
524
522
|
if cls:
|
525
523
|
return cls(pipeline_response, deserialized, response_headers) # type: ignore
|
@@ -572,7 +570,6 @@ class ServiceOperations:
|
|
572
570
|
headers=_headers,
|
573
571
|
params=_params,
|
574
572
|
)
|
575
|
-
_request = _convert_request(_request)
|
576
573
|
_request.url = self._client.format_url(_request.url)
|
577
574
|
|
578
575
|
_stream = False
|
@@ -595,7 +592,7 @@ class ServiceOperations:
|
|
595
592
|
response_headers["x-ms-version"] = self._deserialize("str", response.headers.get("x-ms-version"))
|
596
593
|
response_headers["Date"] = self._deserialize("rfc-1123", response.headers.get("Date"))
|
597
594
|
|
598
|
-
deserialized = self._deserialize("StorageServiceStats", pipeline_response)
|
595
|
+
deserialized = self._deserialize("StorageServiceStats", pipeline_response.http_response)
|
599
596
|
|
600
597
|
if cls:
|
601
598
|
return cls(pipeline_response, deserialized, response_headers) # type: ignore
|
@@ -676,7 +673,6 @@ class ServiceOperations:
|
|
676
673
|
headers=_headers,
|
677
674
|
params=_params,
|
678
675
|
)
|
679
|
-
_request = _convert_request(_request)
|
680
676
|
_request.url = self._client.format_url(_request.url)
|
681
677
|
|
682
678
|
_stream = False
|
@@ -698,7 +694,7 @@ class ServiceOperations:
|
|
698
694
|
response_headers["x-ms-request-id"] = self._deserialize("str", response.headers.get("x-ms-request-id"))
|
699
695
|
response_headers["x-ms-version"] = self._deserialize("str", response.headers.get("x-ms-version"))
|
700
696
|
|
701
|
-
deserialized = self._deserialize("ListContainersSegmentResponse", pipeline_response)
|
697
|
+
deserialized = self._deserialize("ListContainersSegmentResponse", pipeline_response.http_response)
|
702
698
|
|
703
699
|
if cls:
|
704
700
|
return cls(pipeline_response, deserialized, response_headers) # type: ignore
|
@@ -761,7 +757,6 @@ class ServiceOperations:
|
|
761
757
|
headers=_headers,
|
762
758
|
params=_params,
|
763
759
|
)
|
764
|
-
_request = _convert_request(_request)
|
765
760
|
_request.url = self._client.format_url(_request.url)
|
766
761
|
|
767
762
|
_stream = False
|
@@ -784,7 +779,7 @@ class ServiceOperations:
|
|
784
779
|
response_headers["x-ms-version"] = self._deserialize("str", response.headers.get("x-ms-version"))
|
785
780
|
response_headers["Date"] = self._deserialize("rfc-1123", response.headers.get("Date"))
|
786
781
|
|
787
|
-
deserialized = self._deserialize("UserDelegationKey", pipeline_response)
|
782
|
+
deserialized = self._deserialize("UserDelegationKey", pipeline_response.http_response)
|
788
783
|
|
789
784
|
if cls:
|
790
785
|
return cls(pipeline_response, deserialized, response_headers) # type: ignore
|
@@ -835,7 +830,6 @@ class ServiceOperations:
|
|
835
830
|
headers=_headers,
|
836
831
|
params=_params,
|
837
832
|
)
|
838
|
-
_request = _convert_request(_request)
|
839
833
|
_request.url = self._client.format_url(_request.url)
|
840
834
|
|
841
835
|
_stream = False
|
@@ -923,9 +917,9 @@ class ServiceOperations:
|
|
923
917
|
headers=_headers,
|
924
918
|
params=_params,
|
925
919
|
)
|
926
|
-
_request = _convert_request(_request)
|
927
920
|
_request.url = self._client.format_url(_request.url)
|
928
921
|
|
922
|
+
_decompress = kwargs.pop("decompress", True)
|
929
923
|
_stream = True
|
930
924
|
pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access
|
931
925
|
_request, stream=_stream, **kwargs
|
@@ -934,6 +928,10 @@ class ServiceOperations:
|
|
934
928
|
response = pipeline_response.http_response
|
935
929
|
|
936
930
|
if response.status_code not in [200]:
|
931
|
+
try:
|
932
|
+
response.read() # Load the body in memory and close the socket
|
933
|
+
except (StreamConsumedError, StreamClosedError):
|
934
|
+
pass
|
937
935
|
map_error(status_code=response.status_code, response=response, error_map=error_map)
|
938
936
|
error = self._deserialize.failsafe_deserialize(_models.StorageError, pipeline_response)
|
939
937
|
raise HttpResponseError(response=response, model=error)
|
@@ -943,7 +941,7 @@ class ServiceOperations:
|
|
943
941
|
response_headers["x-ms-request-id"] = self._deserialize("str", response.headers.get("x-ms-request-id"))
|
944
942
|
response_headers["x-ms-version"] = self._deserialize("str", response.headers.get("x-ms-version"))
|
945
943
|
|
946
|
-
deserialized = response.stream_download(self._client._pipeline)
|
944
|
+
deserialized = response.stream_download(self._client._pipeline, decompress=_decompress)
|
947
945
|
|
948
946
|
if cls:
|
949
947
|
return cls(pipeline_response, deserialized, response_headers) # type: ignore
|
@@ -1025,7 +1023,6 @@ class ServiceOperations:
|
|
1025
1023
|
headers=_headers,
|
1026
1024
|
params=_params,
|
1027
1025
|
)
|
1028
|
-
_request = _convert_request(_request)
|
1029
1026
|
_request.url = self._client.format_url(_request.url)
|
1030
1027
|
|
1031
1028
|
_stream = False
|
@@ -1048,7 +1045,7 @@ class ServiceOperations:
|
|
1048
1045
|
response_headers["x-ms-version"] = self._deserialize("str", response.headers.get("x-ms-version"))
|
1049
1046
|
response_headers["Date"] = self._deserialize("rfc-1123", response.headers.get("Date"))
|
1050
1047
|
|
1051
|
-
deserialized = self._deserialize("FilterBlobSegment", pipeline_response)
|
1048
|
+
deserialized = self._deserialize("FilterBlobSegment", pipeline_response.http_response)
|
1052
1049
|
|
1053
1050
|
if cls:
|
1054
1051
|
return cls(pipeline_response, deserialized, response_headers) # type: ignore
|
azure/storage/blob/_serialize.py
CHANGED
@@ -76,6 +76,7 @@ class StorageAccountHostsMixin(object): # pylint: disable=too-many-instance-att
|
|
76
76
|
self._location_mode = kwargs.get("_location_mode", LocationMode.PRIMARY)
|
77
77
|
self._hosts = kwargs.get("_hosts")
|
78
78
|
self.scheme = parsed_url.scheme
|
79
|
+
self._is_localhost = False
|
79
80
|
|
80
81
|
if service not in ["blob", "queue", "file-share", "dfs"]:
|
81
82
|
raise ValueError(f"Invalid service: {service}")
|
@@ -85,6 +86,7 @@ class StorageAccountHostsMixin(object): # pylint: disable=too-many-instance-att
|
|
85
86
|
self.account_name = account[0] if len(account) > 1 else None
|
86
87
|
if not self.account_name and parsed_url.netloc.startswith("localhost") \
|
87
88
|
or parsed_url.netloc.startswith("127.0.0.1"):
|
89
|
+
self._is_localhost = True
|
88
90
|
self.account_name = parsed_url.path.strip("/")
|
89
91
|
|
90
92
|
self.credential = _format_shared_key_credential(self.account_name, credential)
|
@@ -35,11 +35,6 @@ from .authentication import AzureSigningError, StorageHttpChallenge
|
|
35
35
|
from .constants import DEFAULT_OAUTH_SCOPE
|
36
36
|
from .models import LocationMode
|
37
37
|
|
38
|
-
try:
|
39
|
-
_unicode_type = unicode # type: ignore
|
40
|
-
except NameError:
|
41
|
-
_unicode_type = str
|
42
|
-
|
43
38
|
if TYPE_CHECKING:
|
44
39
|
from azure.core.credentials import TokenCredential
|
45
40
|
from azure.core.pipeline.transport import ( # pylint: disable=non-abstract-transport-import
|
@@ -52,7 +47,7 @@ _LOGGER = logging.getLogger(__name__)
|
|
52
47
|
|
53
48
|
|
54
49
|
def encode_base64(data):
|
55
|
-
if isinstance(data,
|
50
|
+
if isinstance(data, str):
|
56
51
|
data = data.encode('utf-8')
|
57
52
|
encoded = base64.b64encode(data)
|
58
53
|
return encoded.decode('utf-8')
|
@@ -95,10 +90,14 @@ def is_retry(response, mode): # pylint: disable=too-many-return-statements
|
|
95
90
|
if status in [501, 505]:
|
96
91
|
return False
|
97
92
|
return True
|
93
|
+
return False
|
94
|
+
|
95
|
+
|
96
|
+
def is_checksum_retry(response):
|
98
97
|
# retry if invalid content md5
|
99
98
|
if response.context.get('validate_content', False) and response.http_response.headers.get('content-md5'):
|
100
99
|
computed_md5 = response.http_request.headers.get('content-md5', None) or \
|
101
|
-
|
100
|
+
encode_base64(StorageContentValidation.get_content_md5(response.http_response.body()))
|
102
101
|
if response.http_response.headers['content-md5'] != computed_md5:
|
103
102
|
return True
|
104
103
|
return False
|
@@ -301,7 +300,7 @@ class StorageResponseHook(HTTPPolicy):
|
|
301
300
|
|
302
301
|
response = self.next.send(request)
|
303
302
|
|
304
|
-
will_retry = is_retry(response, request.context.options.get('mode'))
|
303
|
+
will_retry = is_retry(response, request.context.options.get('mode')) or is_checksum_retry(response)
|
305
304
|
# Auth error could come from Bearer challenge, in which case this request will be made again
|
306
305
|
is_auth_error = response.http_response.status_code == 401
|
307
306
|
should_update_counts = not (will_retry or is_auth_error)
|
@@ -527,7 +526,7 @@ class StorageRetryPolicy(HTTPPolicy):
|
|
527
526
|
while retries_remaining:
|
528
527
|
try:
|
529
528
|
response = self.next.send(request)
|
530
|
-
if is_retry(response, retry_settings['mode']):
|
529
|
+
if is_retry(response, retry_settings['mode']) or is_checksum_retry(response):
|
531
530
|
retries_remaining = self.increment(
|
532
531
|
retry_settings,
|
533
532
|
request=request.http_request,
|
@@ -10,12 +10,12 @@ import logging
|
|
10
10
|
import random
|
11
11
|
from typing import Any, Dict, TYPE_CHECKING
|
12
12
|
|
13
|
-
from azure.core.exceptions import AzureError
|
13
|
+
from azure.core.exceptions import AzureError, StreamClosedError, StreamConsumedError
|
14
14
|
from azure.core.pipeline.policies import AsyncBearerTokenCredentialPolicy, AsyncHTTPPolicy
|
15
15
|
|
16
16
|
from .authentication import AzureSigningError, StorageHttpChallenge
|
17
17
|
from .constants import DEFAULT_OAUTH_SCOPE
|
18
|
-
from .policies import is_retry, StorageRetryPolicy
|
18
|
+
from .policies import encode_base64, is_retry, StorageContentValidation, StorageRetryPolicy
|
19
19
|
|
20
20
|
if TYPE_CHECKING:
|
21
21
|
from azure.core.credentials_async import AsyncTokenCredential
|
@@ -42,6 +42,20 @@ async def retry_hook(settings, **kwargs):
|
|
42
42
|
**kwargs)
|
43
43
|
|
44
44
|
|
45
|
+
async def is_checksum_retry(response):
|
46
|
+
# retry if invalid content md5
|
47
|
+
if response.context.get('validate_content', False) and response.http_response.headers.get('content-md5'):
|
48
|
+
try:
|
49
|
+
await response.http_response.read() # Load the body in memory and close the socket
|
50
|
+
except (StreamClosedError, StreamConsumedError):
|
51
|
+
pass
|
52
|
+
computed_md5 = response.http_request.headers.get('content-md5', None) or \
|
53
|
+
encode_base64(StorageContentValidation.get_content_md5(response.http_response.content))
|
54
|
+
if response.http_response.headers['content-md5'] != computed_md5:
|
55
|
+
return True
|
56
|
+
return False
|
57
|
+
|
58
|
+
|
45
59
|
class AsyncStorageResponseHook(AsyncHTTPPolicy):
|
46
60
|
|
47
61
|
def __init__(self, **kwargs): # pylint: disable=unused-argument
|
@@ -64,9 +78,8 @@ class AsyncStorageResponseHook(AsyncHTTPPolicy):
|
|
64
78
|
request.context.options.pop('raw_response_hook', self._response_callback)
|
65
79
|
|
66
80
|
response = await self.next.send(request)
|
67
|
-
|
81
|
+
will_retry = is_retry(response, request.context.options.get('mode')) or await is_checksum_retry(response)
|
68
82
|
|
69
|
-
will_retry = is_retry(response, request.context.options.get('mode'))
|
70
83
|
# Auth error could come from Bearer challenge, in which case this request will be made again
|
71
84
|
is_auth_error = response.http_response.status_code == 401
|
72
85
|
should_update_counts = not (will_retry or is_auth_error)
|
@@ -112,7 +125,7 @@ class AsyncStorageRetryPolicy(StorageRetryPolicy):
|
|
112
125
|
while retries_remaining:
|
113
126
|
try:
|
114
127
|
response = await self.next.send(request)
|
115
|
-
if is_retry(response, retry_settings['mode']):
|
128
|
+
if is_retry(response, retry_settings['mode']) or await is_checksum_retry(response):
|
116
129
|
retries_remaining = self.increment(
|
117
130
|
retry_settings,
|
118
131
|
request=request.http_request,
|
@@ -107,8 +107,17 @@ class SharedAccessSignature(object):
|
|
107
107
|
self.account_key = account_key
|
108
108
|
self.x_ms_version = x_ms_version
|
109
109
|
|
110
|
-
def generate_account(
|
111
|
-
|
110
|
+
def generate_account(
|
111
|
+
self, services,
|
112
|
+
resource_types,
|
113
|
+
permission,
|
114
|
+
expiry,
|
115
|
+
start=None,
|
116
|
+
ip=None,
|
117
|
+
protocol=None,
|
118
|
+
sts_hook=None,
|
119
|
+
**kwargs
|
120
|
+
) -> str:
|
112
121
|
'''
|
113
122
|
Generates a shared access signature for the account.
|
114
123
|
Use the returned signature with the sas_token parameter of the service
|
@@ -152,6 +161,10 @@ class SharedAccessSignature(object):
|
|
152
161
|
:keyword str encryption_scope:
|
153
162
|
Optional. If specified, this is the encryption scope to use when sending requests
|
154
163
|
authorized with this SAS URI.
|
164
|
+
:param sts_hook:
|
165
|
+
For debugging purposes only. If provided, the hook is called with the string to sign
|
166
|
+
that was used to generate the SAS.
|
167
|
+
:type sts_hook: Optional[Callable[[str], None]]
|
155
168
|
:returns: The generated SAS token for the account.
|
156
169
|
:rtype: str
|
157
170
|
'''
|
@@ -161,12 +174,16 @@ class SharedAccessSignature(object):
|
|
161
174
|
sas.add_encryption_scope(**kwargs)
|
162
175
|
sas.add_account_signature(self.account_name, self.account_key)
|
163
176
|
|
177
|
+
if sts_hook is not None:
|
178
|
+
sts_hook(sas.string_to_sign)
|
179
|
+
|
164
180
|
return sas.get_token()
|
165
181
|
|
166
182
|
|
167
183
|
class _SharedAccessHelper(object):
|
168
184
|
def __init__(self):
|
169
185
|
self.query_dict = {}
|
186
|
+
self.string_to_sign = ""
|
170
187
|
|
171
188
|
def _add_query(self, name, val):
|
172
189
|
if val:
|
@@ -229,6 +246,7 @@ class _SharedAccessHelper(object):
|
|
229
246
|
|
230
247
|
self._add_query(QueryStringConstants.SIGNED_SIGNATURE,
|
231
248
|
sign_string(account_key, string_to_sign))
|
249
|
+
self.string_to_sign = string_to_sign
|
232
250
|
|
233
251
|
def get_token(self) -> str:
|
234
252
|
return '&'.join([f'{n}={url_quote(v)}' for n, v in self.query_dict.items() if v is not None])
|
@@ -5,7 +5,10 @@
|
|
5
5
|
# --------------------------------------------------------------------------
|
6
6
|
# pylint: disable=docstring-keyword-should-match-keyword-only
|
7
7
|
|
8
|
-
from typing import
|
8
|
+
from typing import (
|
9
|
+
Any, Callable, Optional, Union,
|
10
|
+
TYPE_CHECKING
|
11
|
+
)
|
9
12
|
from urllib.parse import parse_qs
|
10
13
|
|
11
14
|
from ._shared import sign_string, url_quote
|
@@ -64,6 +67,7 @@ class BlobSharedAccessSignature(SharedAccessSignature):
|
|
64
67
|
content_encoding: Optional[str] = None,
|
65
68
|
content_language: Optional[str] = None,
|
66
69
|
content_type: Optional[str] = None,
|
70
|
+
sts_hook: Optional[Callable[[str], None]] = None,
|
67
71
|
**kwargs: Any
|
68
72
|
) -> str:
|
69
73
|
'''
|
@@ -132,6 +136,10 @@ class BlobSharedAccessSignature(SharedAccessSignature):
|
|
132
136
|
:param str content_type:
|
133
137
|
Response header value for Content-Type when resource is accessed
|
134
138
|
using this shared access signature.
|
139
|
+
:param sts_hook:
|
140
|
+
For debugging purposes only. If provided, the hook is called with the string to sign
|
141
|
+
that was used to generate the SAS.
|
142
|
+
:type sts_hook: Optional[Callable[[str], None]]
|
135
143
|
:return: A Shared Access Signature (sas) token.
|
136
144
|
:rtype: str
|
137
145
|
'''
|
@@ -155,6 +163,9 @@ class BlobSharedAccessSignature(SharedAccessSignature):
|
|
155
163
|
sas.add_resource_signature(self.account_name, self.account_key, resource_path,
|
156
164
|
user_delegation_key=self.user_delegation_key)
|
157
165
|
|
166
|
+
if sts_hook is not None:
|
167
|
+
sts_hook(sas.string_to_sign)
|
168
|
+
|
158
169
|
return sas.get_token()
|
159
170
|
|
160
171
|
def generate_container(
|
@@ -170,6 +181,7 @@ class BlobSharedAccessSignature(SharedAccessSignature):
|
|
170
181
|
content_encoding: Optional[str] = None,
|
171
182
|
content_language: Optional[str] = None,
|
172
183
|
content_type: Optional[str] = None,
|
184
|
+
sts_hook: Optional[Callable[[str], None]] = None,
|
173
185
|
**kwargs: Any
|
174
186
|
) -> str:
|
175
187
|
'''
|
@@ -228,6 +240,10 @@ class BlobSharedAccessSignature(SharedAccessSignature):
|
|
228
240
|
:param str content_type:
|
229
241
|
Response header value for Content-Type when resource is accessed
|
230
242
|
using this shared access signature.
|
243
|
+
:param sts_hook:
|
244
|
+
For debugging purposes only. If provided, the hook is called with the string to sign
|
245
|
+
that was used to generate the SAS.
|
246
|
+
:type sts_hook: Optional[Callable[[str], None]]
|
231
247
|
:return: A Shared Access Signature (sas) token.
|
232
248
|
:rtype: str
|
233
249
|
'''
|
@@ -242,6 +258,10 @@ class BlobSharedAccessSignature(SharedAccessSignature):
|
|
242
258
|
sas.add_info_for_hns_account(**kwargs)
|
243
259
|
sas.add_resource_signature(self.account_name, self.account_key, container_name,
|
244
260
|
user_delegation_key=self.user_delegation_key)
|
261
|
+
|
262
|
+
if sts_hook is not None:
|
263
|
+
sts_hook(sas.string_to_sign)
|
264
|
+
|
245
265
|
return sas.get_token()
|
246
266
|
|
247
267
|
|
@@ -316,6 +336,7 @@ class _BlobSharedAccessHelper(_SharedAccessHelper):
|
|
316
336
|
self._add_query(QueryStringConstants.SIGNED_SIGNATURE,
|
317
337
|
sign_string(account_key if user_delegation_key is None else user_delegation_key.value,
|
318
338
|
string_to_sign))
|
339
|
+
self.string_to_sign = string_to_sign
|
319
340
|
|
320
341
|
def get_token(self) -> str:
|
321
342
|
# a conscious decision was made to exclude the timestamp in the generated token
|
@@ -335,6 +356,7 @@ def generate_account_sas(
|
|
335
356
|
ip: Optional[str] = None,
|
336
357
|
*,
|
337
358
|
services: Union[Services, str] = Services(blob=True),
|
359
|
+
sts_hook: Optional[Callable[[str], None]] = None,
|
338
360
|
**kwargs: Any
|
339
361
|
) -> str:
|
340
362
|
"""Generates a shared access signature for the blob service.
|
@@ -376,6 +398,10 @@ def generate_account_sas(
|
|
376
398
|
Specifies the protocol permitted for a request made. The default value is https.
|
377
399
|
:keyword str encryption_scope:
|
378
400
|
Specifies the encryption scope for a request made so that all write operations will be service encrypted.
|
401
|
+
:keyword sts_hook:
|
402
|
+
For debugging purposes only. If provided, the hook is called with the string to sign
|
403
|
+
that was used to generate the SAS.
|
404
|
+
:paramtype sts_hook: Optional[Callable[[str], None]]
|
379
405
|
:return: A Shared Access Signature (sas) token.
|
380
406
|
:rtype: str
|
381
407
|
|
@@ -396,6 +422,7 @@ def generate_account_sas(
|
|
396
422
|
expiry=expiry,
|
397
423
|
start=start,
|
398
424
|
ip=ip,
|
425
|
+
sts_hook=sts_hook,
|
399
426
|
**kwargs
|
400
427
|
)
|
401
428
|
|
@@ -410,6 +437,8 @@ def generate_container_sas(
|
|
410
437
|
start: Optional[Union["datetime", str]] = None,
|
411
438
|
policy_id: Optional[str] = None,
|
412
439
|
ip: Optional[str] = None,
|
440
|
+
*,
|
441
|
+
sts_hook: Optional[Callable[[str], None]] = None,
|
413
442
|
**kwargs: Any
|
414
443
|
) -> str:
|
415
444
|
"""Generates a shared access signature for a container.
|
@@ -483,6 +512,10 @@ def generate_container_sas(
|
|
483
512
|
:keyword str correlation_id:
|
484
513
|
The correlation id to correlate the storage audit logs with the audit logs used by the principal
|
485
514
|
generating and distributing the SAS. This can only be used when generating a SAS with delegation key.
|
515
|
+
:keyword sts_hook:
|
516
|
+
For debugging purposes only. If provided, the hook is called with the string to sign
|
517
|
+
that was used to generate the SAS.
|
518
|
+
:paramtype sts_hook: Optional[Callable[[str], None]]
|
486
519
|
:return: A Shared Access Signature (sas) token.
|
487
520
|
:rtype: str
|
488
521
|
|
@@ -515,6 +548,7 @@ def generate_container_sas(
|
|
515
548
|
start=start,
|
516
549
|
policy_id=policy_id,
|
517
550
|
ip=ip,
|
551
|
+
sts_hook=sts_hook,
|
518
552
|
**kwargs
|
519
553
|
)
|
520
554
|
|
@@ -531,6 +565,8 @@ def generate_blob_sas(
|
|
531
565
|
start: Optional[Union["datetime", str]] = None,
|
532
566
|
policy_id: Optional[str] = None,
|
533
567
|
ip: Optional[str] = None,
|
568
|
+
*,
|
569
|
+
sts_hook: Optional[Callable[[str], None]] = None,
|
534
570
|
**kwargs: Any
|
535
571
|
) -> str:
|
536
572
|
"""Generates a shared access signature for a blob.
|
@@ -616,6 +652,10 @@ def generate_blob_sas(
|
|
616
652
|
:keyword str correlation_id:
|
617
653
|
The correlation id to correlate the storage audit logs with the audit logs used by the principal
|
618
654
|
generating and distributing the SAS. This can only be used when generating a SAS with delegation key.
|
655
|
+
:keyword sts_hook:
|
656
|
+
For debugging purposes only. If provided, the hook is called with the string to sign
|
657
|
+
that was used to generate the SAS.
|
658
|
+
:paramtype sts_hook: Optional[Callable[[str], None]]
|
619
659
|
:return: A Shared Access Signature (sas) token.
|
620
660
|
:rtype: str
|
621
661
|
"""
|
@@ -645,6 +685,7 @@ def generate_blob_sas(
|
|
645
685
|
start=start,
|
646
686
|
policy_id=policy_id,
|
647
687
|
ip=ip,
|
688
|
+
sts_hook=sts_hook,
|
648
689
|
**kwargs
|
649
690
|
)
|
650
691
|
|
azure/storage/blob/_version.py
CHANGED
@@ -1406,6 +1406,8 @@ class ContainerClient(AsyncStorageAccountHostsMixin, StorageAccountHostsMixin, S
|
|
1406
1406
|
"""
|
1407
1407
|
if len(blobs) == 0:
|
1408
1408
|
return AsyncList([])
|
1409
|
+
if self._is_localhost:
|
1410
|
+
kwargs['url_prepend'] = self.account_name
|
1409
1411
|
|
1410
1412
|
reqs, options = _generate_delete_blobs_options(
|
1411
1413
|
self._query_str,
|
@@ -1485,6 +1487,8 @@ class ContainerClient(AsyncStorageAccountHostsMixin, StorageAccountHostsMixin, S
|
|
1485
1487
|
:return: An async iterator of responses, one for each blob in order
|
1486
1488
|
:rtype: asynciterator[~azure.core.pipeline.transport.AsyncHttpResponse]
|
1487
1489
|
"""
|
1490
|
+
if self._is_localhost:
|
1491
|
+
kwargs['url_prepend'] = self.account_name
|
1488
1492
|
reqs, options = _generate_set_tiers_options(
|
1489
1493
|
self._query_str,
|
1490
1494
|
self.container_name,
|
@@ -1544,6 +1548,8 @@ class ContainerClient(AsyncStorageAccountHostsMixin, StorageAccountHostsMixin, S
|
|
1544
1548
|
:return: An async iterator of responses, one for each blob in order
|
1545
1549
|
:rtype: asynciterator[~azure.core.pipeline.transport.AsyncHttpResponse]
|
1546
1550
|
"""
|
1551
|
+
if self._is_localhost:
|
1552
|
+
kwargs['url_prepend'] = self.account_name
|
1547
1553
|
reqs, options = _generate_set_tiers_options(
|
1548
1554
|
self._query_str,
|
1549
1555
|
self.container_name,
|