From 1844ef98a0f8c38ac45bed634ff9022d9a41711a Mon Sep 17 00:00:00 2001 From: vincenttran-msft <101599632+vincenttran-msft@users.noreply.github.com> Date: Fri, 22 Jul 2022 13:43:34 -0700 Subject: [PATCH] [Storage] Handling AzureNamedKeyCredential in Python SDK (#24967) --- .../blob/changefeed/_change_feed_client.py | 16 +- sdk/storage/azure-storage-blob/CHANGELOG.md | 1 + .../azure/storage/blob/__init__.py | 14 +- .../azure/storage/blob/_blob_client.py | 31 ++- .../storage/blob/_blob_service_client.py | 16 +- .../azure/storage/blob/_container_client.py | 30 ++- .../azure/storage/blob/_shared/base_client.py | 13 +- .../azure/storage/blob/aio/__init__.py | 16 +- .../storage/blob/aio/_blob_client_async.py | 8 +- .../blob/aio/_blob_service_client_async.py | 8 +- .../blob/aio/_container_client_async.py | 8 +- ...est_azure_named_key_credential_access.yaml | 40 ++++ ...est_azure_named_key_credential_access.yaml | 29 +++ .../tests/test_common_blob.py | 15 +- .../tests/test_common_blob_async.py | 16 +- .../azure-storage-file-datalake/CHANGELOG.md | 1 + .../_data_lake_directory_client.py | 16 +- .../filedatalake/_data_lake_file_client.py | 16 +- .../filedatalake/_data_lake_service_client.py | 6 +- .../filedatalake/_file_system_client.py | 16 +- .../storage/filedatalake/_path_client.py | 8 +- .../filedatalake/_shared/base_client.py | 9 +- .../aio/_data_lake_directory_client_async.py | 8 +- .../aio/_data_lake_file_client_async.py | 8 +- .../aio/_data_lake_service_client_async.py | 8 +- .../aio/_file_system_client_async.py | 8 +- .../filedatalake/aio/_path_client_async.py | 8 +- .../azure-storage-file-datalake/setup.py | 2 +- ...est_azure_named_key_credential_access.yaml | 37 +++ ...est_azure_named_key_credential_access.yaml | 29 +++ .../tests/test_datalake_service_client.py | 12 + .../test_datalake_service_client_async.py | 12 + .../azure-storage-file-share/CHANGELOG.md | 1 + .../storage/fileshare/_directory_client.py | 38 +-- .../azure/storage/fileshare/_file_client.py | 36 ++- .../azure/storage/fileshare/_share_client.py | 36 ++- .../fileshare/_share_service_client.py | 24 +- .../storage/fileshare/_shared/base_client.py | 9 +- .../fileshare/aio/_directory_client_async.py | 12 +- .../fileshare/aio/_file_client_async.py | 12 +- .../fileshare/aio/_share_client_async.py | 12 +- .../aio/_share_service_client_async.py | 12 +- ...est_azure_named_key_credential_access.yaml | 217 ++++++++++++++++++ ...est_azure_named_key_credential_access.yaml | 153 ++++++++++++ .../tests/test_file.py | 21 +- .../tests/test_file_async.py | 24 ++ sdk/storage/azure-storage-queue/CHANGELOG.md | 1 + .../azure/storage/queue/_queue_client.py | 29 ++- .../storage/queue/_queue_service_client.py | 19 +- .../storage/queue/_shared/base_client.py | 10 +- .../storage/queue/aio/_queue_client_async.py | 10 +- .../queue/aio/_queue_service_client_async.py | 10 +- ...est_azure_named_key_credential_access.yaml | 117 ++++++++++ ...est_azure_named_key_credential_access.yaml | 91 ++++++++ .../azure-storage-queue/tests/test_queue.py | 18 +- .../tests/test_queue_async.py | 18 +- shared_requirements.txt | 2 +- 57 files changed, 1209 insertions(+), 188 deletions(-) create mode 100644 sdk/storage/azure-storage-blob/tests/recordings/test_common_blob.test_azure_named_key_credential_access.yaml create mode 100644 sdk/storage/azure-storage-blob/tests/recordings/test_common_blob_async.test_azure_named_key_credential_access.yaml create mode 100644 sdk/storage/azure-storage-file-datalake/tests/recordings/test_datalake_service_client.test_azure_named_key_credential_access.yaml create mode 100644 sdk/storage/azure-storage-file-datalake/tests/recordings/test_datalake_service_client_async.test_azure_named_key_credential_access.yaml create mode 100644 sdk/storage/azure-storage-file-share/tests/recordings/test_file.test_azure_named_key_credential_access.yaml create mode 100644 sdk/storage/azure-storage-file-share/tests/recordings/test_file_async.test_azure_named_key_credential_access.yaml create mode 100644 sdk/storage/azure-storage-queue/tests/recordings/test_queue.test_azure_named_key_credential_access.yaml create mode 100644 sdk/storage/azure-storage-queue/tests/recordings/test_queue_async.test_azure_named_key_credential_access.yaml diff --git a/sdk/storage/azure-storage-blob-changefeed/azure/storage/blob/changefeed/_change_feed_client.py b/sdk/storage/azure-storage-blob-changefeed/azure/storage/blob/changefeed/_change_feed_client.py index 9072aa760ad2..c91eccc5f53b 100644 --- a/sdk/storage/azure-storage-blob-changefeed/azure/storage/blob/changefeed/_change_feed_client.py +++ b/sdk/storage/azure-storage-blob-changefeed/azure/storage/blob/changefeed/_change_feed_client.py @@ -25,10 +25,12 @@ class ChangeFeedClient(object): # pylint: disable=too-many-public-methods :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str secondary_hostname: The hostname of the secondary endpoint. :keyword int max_single_get_size: @@ -51,7 +53,7 @@ class ChangeFeedClient(object): # pylint: disable=too-many-public-methods """ def __init__( self, account_url, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None @@ -60,7 +62,7 @@ def __init__( @classmethod def from_connection_string( cls, conn_str, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> ChangeFeedClient """Create ChangeFeedClient from a Connection String. @@ -71,9 +73,11 @@ def from_connection_string( The credentials with which to authenticate. This is optional if the account URL already has a SAS token, or the connection string already has shared access key values. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account shared access - key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. Credentials provided here will take precedence over those in the connection string. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :returns: A change feed client. :rtype: ~azure.storage.blob.changefeed.ChangeFeedClient diff --git a/sdk/storage/azure-storage-blob/CHANGELOG.md b/sdk/storage/azure-storage-blob/CHANGELOG.md index 487f189a149d..5709caa3f2e8 100644 --- a/sdk/storage/azure-storage-blob/CHANGELOG.md +++ b/sdk/storage/azure-storage-blob/CHANGELOG.md @@ -5,6 +5,7 @@ This version and all future versions will require Python 3.7+. Python 3.6 is no longer supported. ### Features Added +- Added support for `AzureNamedKeyCredential` as a valid `credential` type. ### Bugs Fixed - Adjusted type hints for `upload_blob` and `StorageStreamDownloader.readall`. diff --git a/sdk/storage/azure-storage-blob/azure/storage/blob/__init__.py b/sdk/storage/azure-storage-blob/azure/storage/blob/__init__.py index 58442edc91ea..15f20338c80f 100644 --- a/sdk/storage/azure-storage-blob/azure/storage/blob/__init__.py +++ b/sdk/storage/azure-storage-blob/azure/storage/blob/__init__.py @@ -70,7 +70,7 @@ def upload_blob_to_url( blob_url, # type: str data, # type: Union[Iterable[AnyStr], IO[AnyStr]] - credential=None, # type: Any + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs): # type: (...) -> Dict[str, Any] """Upload data to a given URL @@ -85,10 +85,12 @@ def upload_blob_to_url( :param credential: The credentials with which to authenticate. This is optional if the blob URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword bool overwrite: Whether the blob to be uploaded should overwrite the current data. If True, upload_blob_to_url will overwrite any existing data. If set to False, the @@ -127,7 +129,7 @@ def _download_to_stream(client, handle, **kwargs): def download_blob_from_url( blob_url, # type: str output, # type: str - credential=None, # type: Any + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs): # type: (...) -> None """Download the contents of a blob to a local file or stream. @@ -141,10 +143,12 @@ def download_blob_from_url( :param credential: The credentials with which to authenticate. This is optional if the blob URL already has a SAS token or the blob is public. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword bool overwrite: Whether the local file should be overwritten if it already exists. The default value is `False` - in which case a ValueError will be raised if the file already exists. If set to diff --git a/sdk/storage/azure-storage-blob/azure/storage/blob/_blob_client.py b/sdk/storage/azure-storage-blob/azure/storage/blob/_blob_client.py index 62a08feb976e..30684bc1dcdd 100644 --- a/sdk/storage/azure-storage-blob/azure/storage/blob/_blob_client.py +++ b/sdk/storage/azure-storage-blob/azure/storage/blob/_blob_client.py @@ -104,10 +104,12 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -151,7 +153,7 @@ def __init__( container_name, # type: str blob_name, # type: str snapshot=None, # type: Optional[Union[str, Dict[str, Any]]] - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None @@ -210,8 +212,13 @@ def _encode_source_url(self, source_url): return '?'.join(result) @classmethod - def from_blob_url(cls, blob_url, credential=None, snapshot=None, **kwargs): - # type: (Type[ClassType], str, Optional[Any], Optional[Union[str, Dict[str, Any]]], Any) -> ClassType + def from_blob_url( + cls, # type: Type[ClassType] + blob_url, # type: str + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long + snapshot=None, # type: Optional[Union[str, Dict[str, Any]]] + **kwargs # type: Any + ): # type: (...) -> ClassType """Create BlobClient from a blob url. This doesn't support customized blob url with '/' in blob name. :param str blob_url: @@ -222,10 +229,12 @@ def from_blob_url(cls, blob_url, credential=None, snapshot=None, **kwargs): The credentials with which to authenticate. This is optional if the account URL already has a SAS token, or the connection string already has shared access key values. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account shared access - key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :param str snapshot: The optional blob snapshot on which to operate. This can be the snapshot ID string or the response returned from :func:`create_snapshot`. If specified, this will override @@ -291,7 +300,7 @@ def from_connection_string( container_name, # type: str blob_name, # type: str snapshot=None, # type: Optional[str] - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> ClassType """Create BlobClient from a Connection String. @@ -309,9 +318,11 @@ def from_connection_string( The credentials with which to authenticate. This is optional if the account URL already has a SAS token, or the connection string already has shared access key values. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account shared access - key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. Credentials provided here will take precedence over those in the connection string. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :returns: A Blob client. :rtype: ~azure.storage.blob.BlobClient diff --git a/sdk/storage/azure-storage-blob/azure/storage/blob/_blob_service_client.py b/sdk/storage/azure-storage-blob/azure/storage/blob/_blob_service_client.py index 30907ef457ec..89abab31b4c7 100644 --- a/sdk/storage/azure-storage-blob/azure/storage/blob/_blob_service_client.py +++ b/sdk/storage/azure-storage-blob/azure/storage/blob/_blob_service_client.py @@ -73,10 +73,12 @@ class BlobServiceClient(StorageAccountHostsMixin, StorageEncryptionMixin): :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -118,7 +120,7 @@ class BlobServiceClient(StorageAccountHostsMixin, StorageEncryptionMixin): def __init__( self, account_url, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None @@ -148,7 +150,7 @@ def _format_url(self, hostname): def from_connection_string( cls, # type: Type[ClassType] conn_str, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> ClassType """Create BlobServiceClient from a Connection String. @@ -159,9 +161,11 @@ def from_connection_string( The credentials with which to authenticate. This is optional if the account URL already has a SAS token, or the connection string already has shared access key values. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account shared access - key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. Credentials provided here will take precedence over those in the connection string. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :returns: A Blob service client. :rtype: ~azure.storage.blob.BlobServiceClient diff --git a/sdk/storage/azure-storage-blob/azure/storage/blob/_container_client.py b/sdk/storage/azure-storage-blob/azure/storage/blob/_container_client.py index a3fca708d043..39df3da26e2d 100644 --- a/sdk/storage/azure-storage-blob/azure/storage/blob/_container_client.py +++ b/sdk/storage/azure-storage-blob/azure/storage/blob/_container_client.py @@ -88,10 +88,12 @@ class ContainerClient(StorageAccountHostsMixin, StorageEncryptionMixin): # py :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -133,7 +135,7 @@ class ContainerClient(StorageAccountHostsMixin, StorageEncryptionMixin): # py def __init__( self, account_url, # type: str container_name, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None @@ -169,8 +171,12 @@ def _format_url(self, hostname): self._query_str) @classmethod - def from_container_url(cls, container_url, credential=None, **kwargs): - # type: (Type[ClassType], str, Optional[Any], Any) -> ClassType + def from_container_url( + cls, # type: Type[ClassType] + container_url, # type: str + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long + **kwargs # type: Any + ): # type: (...) -> ClassType """Create ContainerClient from a container url. :param str container_url: @@ -181,10 +187,12 @@ def from_container_url(cls, container_url, credential=None, **kwargs): The credentials with which to authenticate. This is optional if the account URL already has a SAS token, or the connection string already has shared access key values. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account shared access - key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :returns: A container client. :rtype: ~azure.storage.blob.ContainerClient """ @@ -216,7 +224,7 @@ def from_connection_string( cls, # type: Type[ClassType] conn_str, # type: str container_name, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> ClassType """Create ContainerClient from a Connection String. @@ -230,9 +238,11 @@ def from_connection_string( The credentials with which to authenticate. This is optional if the account URL already has a SAS token, or the connection string already has shared access key values. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account shared access - key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. Credentials provided here will take precedence over those in the connection string. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :returns: A container client. :rtype: ~azure.storage.blob.ContainerClient diff --git a/sdk/storage/azure-storage-blob/azure/storage/blob/_shared/base_client.py b/sdk/storage/azure-storage-blob/azure/storage/blob/_shared/base_client.py index 6365e1688e49..a9ea706d92f6 100644 --- a/sdk/storage/azure-storage-blob/azure/storage/blob/_shared/base_client.py +++ b/sdk/storage/azure-storage-blob/azure/storage/blob/_shared/base_client.py @@ -6,9 +6,12 @@ import logging import uuid from typing import ( # pylint: disable=unused-import - Optional, Any, + Dict, Tuple, + Union, + Optional, + TYPE_CHECKING ) try: @@ -20,7 +23,7 @@ import six from azure.core.configuration import Configuration -from azure.core.credentials import AzureSasCredential +from azure.core.credentials import AzureSasCredential, AzureNamedKeyCredential from azure.core.exceptions import HttpResponseError from azure.core.pipeline import Pipeline from azure.core.pipeline.transport import RequestsTransport, HttpTransport @@ -53,6 +56,8 @@ from .._version import VERSION from .response_handlers import process_storage_error, PartialBatchErrorException +if TYPE_CHECKING: + from azure.core.credentials import TokenCredential _LOGGER = logging.getLogger(__name__) _SERVICE_PARAMS = { @@ -68,7 +73,7 @@ def __init__( self, parsed_url, # type: Any service, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None @@ -353,6 +358,8 @@ def _format_shared_key_credential(account_name, credential): if "account_key" not in credential: raise ValueError("Shared key credential missing 'account_key") return SharedKeyCredentialPolicy(**credential) + if isinstance(credential, AzureNamedKeyCredential): + return SharedKeyCredentialPolicy(credential.named_key.name, credential.named_key.key) return credential diff --git a/sdk/storage/azure-storage-blob/azure/storage/blob/aio/__init__.py b/sdk/storage/azure-storage-blob/azure/storage/blob/aio/__init__.py index cfd991e34507..e8286fe217b1 100644 --- a/sdk/storage/azure-storage-blob/azure/storage/blob/aio/__init__.py +++ b/sdk/storage/azure-storage-blob/azure/storage/blob/aio/__init__.py @@ -19,7 +19,7 @@ async def upload_blob_to_url( blob_url, # type: str data, # type: Union[Iterable[AnyStr], IO[AnyStr]] - credential=None, # type: Any + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs): # type: (...) -> dict[str, Any] """Upload data to a given URL @@ -31,13 +31,15 @@ async def upload_blob_to_url( :param data: The data to upload. This can be bytes, text, an iterable or a file-like object. :type data: bytes or str or Iterable - :param credential: + :param credential: The credentials with which to authenticate. This is optional if the blob URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword bool overwrite: Whether the blob to be uploaded should overwrite the current data. If True, upload_blob_to_url will overwrite any existing data. If set to False, the @@ -76,7 +78,7 @@ async def _download_to_stream(client, handle, **kwargs): async def download_blob_from_url( blob_url, # type: str output, # type: str - credential=None, # type: Any + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs): # type: (...) -> None """Download the contents of a blob to a local file or stream. @@ -90,10 +92,12 @@ async def download_blob_from_url( :param credential: The credentials with which to authenticate. This is optional if the blob URL already has a SAS token or the blob is public. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword bool overwrite: Whether the local file should be overwritten if it already exists. The default value is `False` - in which case a ValueError will be raised if the file already exists. If set to diff --git a/sdk/storage/azure-storage-blob/azure/storage/blob/aio/_blob_client_async.py b/sdk/storage/azure-storage-blob/azure/storage/blob/aio/_blob_client_async.py index 850ea689ae7f..3566ec7c7feb 100644 --- a/sdk/storage/azure-storage-blob/azure/storage/blob/aio/_blob_client_async.py +++ b/sdk/storage/azure-storage-blob/azure/storage/blob/aio/_blob_client_async.py @@ -70,10 +70,12 @@ class BlobClient(AsyncStorageAccountHostsMixin, BlobClientBase, StorageEncryptio :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -117,7 +119,7 @@ def __init__( container_name, # type: str blob_name, # type: str snapshot=None, # type: Optional[Union[str, Dict[str, Any]]] - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None diff --git a/sdk/storage/azure-storage-blob/azure/storage/blob/aio/_blob_service_client_async.py b/sdk/storage/azure-storage-blob/azure/storage/blob/aio/_blob_service_client_async.py index f10f8b1dd0a4..5fd5301e027e 100644 --- a/sdk/storage/azure-storage-blob/azure/storage/blob/aio/_blob_service_client_async.py +++ b/sdk/storage/azure-storage-blob/azure/storage/blob/aio/_blob_service_client_async.py @@ -69,10 +69,12 @@ class BlobServiceClient(AsyncStorageAccountHostsMixin, BlobServiceClientBase, St :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -114,7 +116,7 @@ class BlobServiceClient(AsyncStorageAccountHostsMixin, BlobServiceClientBase, St def __init__( self, account_url, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None diff --git a/sdk/storage/azure-storage-blob/azure/storage/blob/aio/_container_client_async.py b/sdk/storage/azure-storage-blob/azure/storage/blob/aio/_container_client_async.py index fc3c267037ff..a139e3f4bf3e 100644 --- a/sdk/storage/azure-storage-blob/azure/storage/blob/aio/_container_client_async.py +++ b/sdk/storage/azure-storage-blob/azure/storage/blob/aio/_container_client_async.py @@ -64,10 +64,12 @@ class ContainerClient(AsyncStorageAccountHostsMixin, ContainerClientBase, Storag :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -109,7 +111,7 @@ class ContainerClient(AsyncStorageAccountHostsMixin, ContainerClientBase, Storag def __init__( self, account_url, # type: str container_name, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None diff --git a/sdk/storage/azure-storage-blob/tests/recordings/test_common_blob.test_azure_named_key_credential_access.yaml b/sdk/storage/azure-storage-blob/tests/recordings/test_common_blob.test_azure_named_key_credential_access.yaml new file mode 100644 index 000000000000..00ddc7cfd9e6 --- /dev/null +++ b/sdk/storage/azure-storage-blob/tests/recordings/test_common_blob.test_azure_named_key_credential_access.yaml @@ -0,0 +1,40 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - azsdk-python-storage-blob/12.13.0 Python/3.10.2 (Windows-10-10.0.19044-SP0) + x-ms-date: + - Thu, 23 Jun 2022 23:44:15 GMT + x-ms-version: + - '2021-08-06' + method: PUT + uri: https://storagename.blob.core.windows.net/container76851672?restype=container + response: + body: + string: '' + headers: + content-length: + - '0' + date: + - Thu, 23 Jun 2022 23:44:16 GMT + etag: + - '"0x8DA557248FF6AA9"' + last-modified: + - Thu, 23 Jun 2022 23:44:16 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + x-ms-version: + - '2021-08-06' + status: + code: 201 + message: Created +version: 1 diff --git a/sdk/storage/azure-storage-blob/tests/recordings/test_common_blob_async.test_azure_named_key_credential_access.yaml b/sdk/storage/azure-storage-blob/tests/recordings/test_common_blob_async.test_azure_named_key_credential_access.yaml new file mode 100644 index 000000000000..c83ff83bf5fc --- /dev/null +++ b/sdk/storage/azure-storage-blob/tests/recordings/test_common_blob_async.test_azure_named_key_credential_access.yaml @@ -0,0 +1,29 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/xml + User-Agent: + - azsdk-python-storage-blob/12.13.0 Python/3.10.2 (Windows-10-10.0.19044-SP0) + x-ms-date: + - Thu, 23 Jun 2022 23:47:19 GMT + x-ms-version: + - '2021-08-06' + method: PUT + uri: https://storagename.blob.core.windows.net/container81e18ef?restype=container + response: + body: + string: '' + headers: + content-length: '0' + date: Thu, 23 Jun 2022 23:47:19 GMT + etag: '"0x8DA5572B65BCF75"' + last-modified: Thu, 23 Jun 2022 23:47:20 GMT + server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + x-ms-version: '2021-08-06' + status: + code: 201 + message: Created + url: https://vincenttranstock.blob.core.windows.net/container81e18ef?restype=container +version: 1 diff --git a/sdk/storage/azure-storage-blob/tests/test_common_blob.py b/sdk/storage/azure-storage-blob/tests/test_common_blob.py index b497d382ac38..1a53f36e80d5 100644 --- a/sdk/storage/azure-storage-blob/tests/test_common_blob.py +++ b/sdk/storage/azure-storage-blob/tests/test_common_blob.py @@ -18,7 +18,7 @@ from azure.core import MatchConditions -from azure.core.credentials import AzureSasCredential +from azure.core.credentials import AzureSasCredential, AzureNamedKeyCredential from azure.core.exceptions import ( HttpResponseError, ResourceNotFoundError, @@ -1995,6 +1995,19 @@ def test_account_sas_credential(self, storage_account_name, storage_account_key) self.assertEqual(blob_name, blob_properties.name) self.assertEqual(self.container_name, container_properties.name) + @BlobPreparer() + def test_azure_named_key_credential_access(self, storage_account_name, storage_account_key): + named_key = AzureNamedKeyCredential(storage_account_name, storage_account_key) + bsc = BlobServiceClient(self.account_url(storage_account_name, "blob"), named_key) + container_name = self._get_container_reference() + + # Act + container = bsc.get_container_client(container_name) + created = container.create_container() + + # Assert + self.assertTrue(created) + @BlobPreparer() def test_get_user_delegation_key(self, storage_account_name, storage_account_key): # Act diff --git a/sdk/storage/azure-storage-blob/tests/test_common_blob_async.py b/sdk/storage/azure-storage-blob/tests/test_common_blob_async.py index c945a18d49da..8dfb89a47c5a 100644 --- a/sdk/storage/azure-storage-blob/tests/test_common_blob_async.py +++ b/sdk/storage/azure-storage-blob/tests/test_common_blob_async.py @@ -19,7 +19,7 @@ from azure.mgmt.storage.aio import StorageManagementClient from azure.core import MatchConditions -from azure.core.credentials import AzureSasCredential +from azure.core.credentials import AzureSasCredential, AzureNamedKeyCredential from azure.core.exceptions import ( HttpResponseError, ResourceNotFoundError, @@ -2037,6 +2037,20 @@ async def test_account_sas_credential(self, storage_account_name, storage_accoun self.assertEqual(blob_name, blob_properties.name) self.assertEqual(self.container_name, container_properties.name) + @BlobPreparer() + @AsyncStorageTestCase.await_prepared_test + async def test_azure_named_key_credential_access(self, storage_account_name, storage_account_key): + named_key = AzureNamedKeyCredential(storage_account_name, storage_account_key) + bsc = BlobServiceClient(self.account_url(storage_account_name, "blob"), named_key) + container_name = self._get_container_reference() + + # Act + container = bsc.get_container_client(container_name) + created = await container.create_container() + + # Assert + self.assertTrue(created) + @pytest.mark.live_test_only @BlobPreparer() @AsyncStorageTestCase.await_prepared_test diff --git a/sdk/storage/azure-storage-file-datalake/CHANGELOG.md b/sdk/storage/azure-storage-file-datalake/CHANGELOG.md index c5546c2a0149..143e02067c94 100644 --- a/sdk/storage/azure-storage-file-datalake/CHANGELOG.md +++ b/sdk/storage/azure-storage-file-datalake/CHANGELOG.md @@ -5,6 +5,7 @@ This version and all future versions will require Python 3.7+. Python 3.6 is no longer supported. ### Features Added +- Added support for `AzureNamedKeyCredential` as a valid `credential` type. ### Bugs Fixed diff --git a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_data_lake_directory_client.py b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_data_lake_directory_client.py index 19231fa3beca..2a93df0eae88 100644 --- a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_data_lake_directory_client.py +++ b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_data_lake_directory_client.py @@ -47,10 +47,12 @@ class DataLakeDirectoryClient(PathClient): :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, and account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -68,7 +70,7 @@ def __init__( self, account_url, # type: str file_system_name, # type: str directory_name, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None @@ -81,7 +83,7 @@ def from_connection_string( conn_str, # type: str file_system_name, # type: str directory_name, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> ClassType """ @@ -99,9 +101,11 @@ def from_connection_string( The credentials with which to authenticate. This is optional if the account URL already has a SAS token, or the connection string already has shared access key values. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, and account shared access - key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. Credentials provided here will take precedence over those in the connection string. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :return: a DataLakeDirectoryClient :rtype: ~azure.storage.filedatalake.DataLakeDirectoryClient """ diff --git a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_data_lake_file_client.py b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_data_lake_file_client.py index 54552e2c0c46..f433965541d9 100644 --- a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_data_lake_file_client.py +++ b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_data_lake_file_client.py @@ -57,10 +57,12 @@ class DataLakeFileClient(PathClient): :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -78,7 +80,7 @@ def __init__( self, account_url, # type: str file_system_name, # type: str file_path, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None @@ -91,7 +93,7 @@ def from_connection_string( conn_str, # type: str file_system_name, # type: str file_path, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> ClassType """ @@ -109,9 +111,11 @@ def from_connection_string( The credentials with which to authenticate. This is optional if the account URL already has a SAS token, or the connection string already has shared access key values. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account shared access - key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. Credentials provided here will take precedence over those in the connection string. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :return a DataLakeFileClient :rtype ~azure.storage.filedatalake.DataLakeFileClient """ diff --git a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_data_lake_service_client.py b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_data_lake_service_client.py index a5bfff3c531c..d55f13b89fe6 100644 --- a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_data_lake_service_client.py +++ b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_data_lake_service_client.py @@ -47,10 +47,12 @@ class DataLakeServiceClient(StorageAccountHostsMixin): :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. diff --git a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_file_system_client.py b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_file_system_client.py index 79ac40de8c28..ab25f4c176f2 100644 --- a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_file_system_client.py +++ b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_file_system_client.py @@ -58,10 +58,12 @@ class FileSystemClient(StorageAccountHostsMixin): :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -78,7 +80,7 @@ class FileSystemClient(StorageAccountHostsMixin): def __init__( self, account_url, # type: str file_system_name, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None @@ -150,7 +152,7 @@ def from_connection_string( cls, # type: Type[ClassType] conn_str, # type: str file_system_name, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> ClassType """ @@ -164,9 +166,11 @@ def from_connection_string( The credentials with which to authenticate. This is optional if the account URL already has a SAS token, or the connection string already has shared access key values. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account shared access - key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. Credentials provided here will take precedence over those in the connection string. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :return a FileSystemClient :rtype ~azure.storage.filedatalake.FileSystemClient diff --git a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_path_client.py b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_path_client.py index 36dddb60cacc..2d8c14cbee12 100644 --- a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_path_client.py +++ b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_path_client.py @@ -47,10 +47,12 @@ class PathClient(StorageAccountHostsMixin): :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -59,7 +61,7 @@ def __init__( self, account_url, # type: str file_system_name, # type: str path_name, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None diff --git a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_shared/base_client.py b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_shared/base_client.py index 3af10b496f22..8ccc285ca329 100644 --- a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_shared/base_client.py +++ b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_shared/base_client.py @@ -9,6 +9,7 @@ Optional, Any, Tuple, + TYPE_CHECKING ) try: @@ -20,7 +21,7 @@ import six from azure.core.configuration import Configuration -from azure.core.credentials import AzureSasCredential +from azure.core.credentials import AzureSasCredential, AzureNamedKeyCredential from azure.core.exceptions import HttpResponseError from azure.core.pipeline import Pipeline from azure.core.pipeline.transport import RequestsTransport, HttpTransport @@ -53,6 +54,8 @@ from .._version import VERSION from .response_handlers import process_storage_error, PartialBatchErrorException +if TYPE_CHECKING: + from azure.core.credentials import TokenCredential _LOGGER = logging.getLogger(__name__) _SERVICE_PARAMS = { @@ -67,7 +70,7 @@ def __init__( self, parsed_url, # type: Any service, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None @@ -352,6 +355,8 @@ def _format_shared_key_credential(account_name, credential): if "account_key" not in credential: raise ValueError("Shared key credential missing 'account_key") return SharedKeyCredentialPolicy(**credential) + if isinstance(credential, AzureNamedKeyCredential): + return SharedKeyCredentialPolicy(credential.named_key.name, credential.named_key.key) return credential diff --git a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_data_lake_directory_client_async.py b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_data_lake_directory_client_async.py index 923fafec280f..5c0fa8a061be 100644 --- a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_data_lake_directory_client_async.py +++ b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_data_lake_directory_client_async.py @@ -47,10 +47,12 @@ class DataLakeDirectoryClient(PathClient, DataLakeDirectoryClientBase): :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -69,7 +71,7 @@ def __init__( self, account_url, # type: str file_system_name, # type: str directory_name, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None diff --git a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_data_lake_file_client_async.py b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_data_lake_file_client_async.py index 5ac4840e36b7..39ddcb1cc85f 100644 --- a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_data_lake_file_client_async.py +++ b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_data_lake_file_client_async.py @@ -48,10 +48,12 @@ class DataLakeFileClient(PathClient, DataLakeFileClientBase): :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -70,7 +72,7 @@ def __init__( self, account_url, # type: str file_system_name, # type: str file_path, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None diff --git a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_data_lake_service_client_async.py b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_data_lake_service_client_async.py index c4c48dddad9f..c71160695f37 100644 --- a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_data_lake_service_client_async.py +++ b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_data_lake_service_client_async.py @@ -44,10 +44,12 @@ class DataLakeServiceClient(AsyncStorageAccountHostsMixin, DataLakeServiceClient :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -71,7 +73,7 @@ class DataLakeServiceClient(AsyncStorageAccountHostsMixin, DataLakeServiceClient def __init__( self, account_url, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None diff --git a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_file_system_client_async.py b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_file_system_client_async.py index a837085b48c1..46b847d7ae5b 100644 --- a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_file_system_client_async.py +++ b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_file_system_client_async.py @@ -61,10 +61,12 @@ class FileSystemClient(AsyncStorageAccountHostsMixin, FileSystemClientBase): :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -82,7 +84,7 @@ class FileSystemClient(AsyncStorageAccountHostsMixin, FileSystemClientBase): def __init__( self, account_url, # type: str file_system_name, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None diff --git a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_path_client_async.py b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_path_client_async.py index 604d19c83fec..bff06eb9523f 100644 --- a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_path_client_async.py +++ b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/aio/_path_client_async.py @@ -39,10 +39,12 @@ class PathClient(AsyncStorageAccountHostsMixin, PathClientBase): :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -51,7 +53,7 @@ def __init__( self, account_url, # type: str file_system_name, # type: str path_name, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None diff --git a/sdk/storage/azure-storage-file-datalake/setup.py b/sdk/storage/azure-storage-file-datalake/setup.py index 5d3a1d9e576a..43ccc5d1452b 100644 --- a/sdk/storage/azure-storage-file-datalake/setup.py +++ b/sdk/storage/azure-storage-file-datalake/setup.py @@ -77,6 +77,6 @@ install_requires=[ "azure-core<2.0.0,>=1.23.1", "msrest>=0.6.21", - "azure-storage-blob<13.0.0,>=12.13.0" + "azure-storage-blob<13.0.0,>=12.14.0b1" ], ) diff --git a/sdk/storage/azure-storage-file-datalake/tests/recordings/test_datalake_service_client.test_azure_named_key_credential_access.yaml b/sdk/storage/azure-storage-file-datalake/tests/recordings/test_datalake_service_client.test_azure_named_key_credential_access.yaml new file mode 100644 index 000000000000..56be9ab4cc92 --- /dev/null +++ b/sdk/storage/azure-storage-file-datalake/tests/recordings/test_datalake_service_client.test_azure_named_key_credential_access.yaml @@ -0,0 +1,37 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-storage-blob/12.13.0 Python/3.10.2 (Windows-10-10.0.19044-SP0) + x-ms-date: + - Tue, 12 Jul 2022 00:28:51 GMT + x-ms-version: + - '2021-08-06' + method: GET + uri: https://storagename.blob.core.windows.net/?restype=service&comp=properties + response: + body: + string: "\uFEFF1.0falsefalsefalsefalse1.0truetruetrue71.0falsefalsefalsefalsefalse" + headers: + content-type: + - application/xml + date: + - Tue, 12 Jul 2022 00:28:51 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + transfer-encoding: + - chunked + x-ms-version: + - '2021-08-06' + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/storage/azure-storage-file-datalake/tests/recordings/test_datalake_service_client_async.test_azure_named_key_credential_access.yaml b/sdk/storage/azure-storage-file-datalake/tests/recordings/test_datalake_service_client_async.test_azure_named_key_credential_access.yaml new file mode 100644 index 000000000000..17aa5e7ad1c3 --- /dev/null +++ b/sdk/storage/azure-storage-file-datalake/tests/recordings/test_datalake_service_client_async.test_azure_named_key_credential_access.yaml @@ -0,0 +1,29 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/xml + User-Agent: + - azsdk-python-storage-blob/12.13.0 Python/3.10.2 (Windows-10-10.0.19044-SP0) + x-ms-date: + - Tue, 12 Jul 2022 00:29:02 GMT + x-ms-version: + - '2021-08-06' + method: GET + uri: https://storagename.blob.core.windows.net/?restype=service&comp=properties + response: + body: + string: "\uFEFF1.0falsefalsefalsefalse1.0truetruetrue71.0falsefalsefalsefalsefalse" + headers: + content-type: application/xml + date: Tue, 12 Jul 2022 00:29:01 GMT + server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + transfer-encoding: chunked + x-ms-version: '2021-08-06' + status: + code: 200 + message: OK + url: https://vincenttranhnscanary.blob.core.windows.net/?restype=service&comp=properties +version: 1 diff --git a/sdk/storage/azure-storage-file-datalake/tests/test_datalake_service_client.py b/sdk/storage/azure-storage-file-datalake/tests/test_datalake_service_client.py index 7fc281eb5dd9..e3b39925712e 100644 --- a/sdk/storage/azure-storage-file-datalake/tests/test_datalake_service_client.py +++ b/sdk/storage/azure-storage-file-datalake/tests/test_datalake_service_client.py @@ -19,6 +19,7 @@ from settings.testcase import DataLakePreparer from devtools_testutils.storage import StorageTestCase +from azure.core.credentials import AzureNamedKeyCredential # ------------------------------------------------------------------------------ from azure.storage.filedatalake._models import AnalyticsLogging, Metrics, RetentionPolicy, \ @@ -348,3 +349,14 @@ def test_connectionstring_without_secondary(self): assert client.url == 'https://foo.dfs.core.windows.net/fsname/dname' assert client.primary_hostname == 'foo.dfs.core.windows.net' assert not client.secondary_hostname + + @DataLakePreparer() + def test_azure_named_key_credential_access(self, datalake_storage_account_name, datalake_storage_account_key): + named_key = AzureNamedKeyCredential(datalake_storage_account_name, datalake_storage_account_key) + dsc = DataLakeServiceClient(self.account_url(datalake_storage_account_name, "blob"), named_key) + + # Act + props = dsc.get_service_properties() + + # Assert + self.assertIsNotNone(props) diff --git a/sdk/storage/azure-storage-file-datalake/tests/test_datalake_service_client_async.py b/sdk/storage/azure-storage-file-datalake/tests/test_datalake_service_client_async.py index 84d05e467c73..2d8fec0910d4 100644 --- a/sdk/storage/azure-storage-file-datalake/tests/test_datalake_service_client_async.py +++ b/sdk/storage/azure-storage-file-datalake/tests/test_datalake_service_client_async.py @@ -19,6 +19,7 @@ ) from devtools_testutils.storage.aio import AsyncStorageTestCase as StorageTestCase from settings.testcase import DataLakePreparer +from azure.core.credentials import AzureNamedKeyCredential # ------------------------------------------------------------------------------ from azure.storage.filedatalake._models import AnalyticsLogging, Metrics, RetentionPolicy, \ @@ -343,3 +344,14 @@ async def test_connectionstring_without_secondary(self): assert client.url == 'https://foo.dfs.core.windows.net/fsname/dname' assert client.primary_hostname == 'foo.dfs.core.windows.net' assert not client.secondary_hostname + + @DataLakePreparer() + async def test_azure_named_key_credential_access(self, datalake_storage_account_name, datalake_storage_account_key): + named_key = AzureNamedKeyCredential(datalake_storage_account_name, datalake_storage_account_key) + dsc = DataLakeServiceClient(self.account_url(datalake_storage_account_name, "blob"), named_key) + + # Act + props = await dsc.get_service_properties() + + # Assert + self.assertIsNotNone(props) diff --git a/sdk/storage/azure-storage-file-share/CHANGELOG.md b/sdk/storage/azure-storage-file-share/CHANGELOG.md index 0e862987009a..0daccb992e53 100644 --- a/sdk/storage/azure-storage-file-share/CHANGELOG.md +++ b/sdk/storage/azure-storage-file-share/CHANGELOG.md @@ -5,6 +5,7 @@ This version and all future versions will require Python 3.7+. Python 3.6 is no longer supported. ### Features Added +- Added support for `AzureNamedKeyCredential` as a valid `credential` type. ### Bugs Fixed diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_directory_client.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_directory_client.py index 643af020894c..3c4132a59512 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_directory_client.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_directory_client.py @@ -63,10 +63,14 @@ class ShareDirectoryClient(StorageAccountHostsMixin): An optional share snapshot on which to operate. This can be the snapshot ID string or the response returned from :func:`ShareClient.create_snapshot`. :param credential: - The credential with which to authenticate. This is optional if the + The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials or an account - shared access key. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -82,7 +86,7 @@ def __init__( # type: ignore share_name, # type: str directory_path, # type: str snapshot=None, # type: Optional[Union[str, Dict[str, Any]]] - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Optional[Any] ): # type: (...) -> None @@ -123,7 +127,7 @@ def __init__( # type: ignore @classmethod def from_directory_url(cls, directory_url, # type: str snapshot=None, # type: Optional[Union[str, Dict[str, Any]]] - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Optional[Any] ): # type: (...) -> ShareDirectoryClient @@ -135,10 +139,14 @@ def from_directory_url(cls, directory_url, # type: str An optional share snapshot on which to operate. This can be the snapshot ID string or the response returned from :func:`ShareClient.create_snapshot`. :param credential: - The credential with which to authenticate. This is optional if the - account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials or an account - shared access key. + The credentials with which to authenticate. This is optional if the + account URL already has a SAS token. The value can be a SAS token string, + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :returns: A directory client. :rtype: ~azure.storage.fileshare.ShareDirectoryClient """ @@ -185,7 +193,7 @@ def from_connection_string( cls, conn_str, # type: str share_name, # type: str directory_path, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> ShareDirectoryClient @@ -198,10 +206,14 @@ def from_connection_string( :param str directory_path: The directory path. :param credential: - The credential with which to authenticate. This is optional if the + The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials or an account - shared access key. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :returns: A directory client. :rtype: ~azure.storage.fileshare.ShareDirectoryClient """ diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_file_client.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_file_client.py index a2b0fec1abca..c3d3afdd461f 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_file_client.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_file_client.py @@ -123,10 +123,14 @@ class ShareFileClient(StorageAccountHostsMixin): An optional file snapshot on which to operate. This can be the snapshot ID string or the response returned from :func:`ShareClient.create_snapshot`. :param credential: - The credential with which to authenticate. This is optional if the + The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials or an account - shared access key. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -142,7 +146,7 @@ def __init__( # type: ignore share_name, # type: str file_path, # type: str snapshot=None, # type: Optional[Union[str, Dict[str, Any]]] - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None @@ -187,7 +191,7 @@ def __init__( # type: ignore def from_file_url( cls, file_url, # type: str snapshot=None, # type: Optional[Union[str, Dict[str, Any]]] - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> ShareFileClient @@ -198,10 +202,14 @@ def from_file_url( An optional file snapshot on which to operate. This can be the snapshot ID string or the response returned from :func:`ShareClient.create_snapshot`. :param credential: - The credential with which to authenticate. This is optional if the + The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials or an account - shared access key. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :returns: A File client. :rtype: ~azure.storage.fileshare.ShareFileClient """ @@ -243,7 +251,7 @@ def from_connection_string( share_name, # type: str file_path, # type: str snapshot=None, # type: Optional[Union[str, Dict[str, Any]]] - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> ShareFileClient @@ -259,10 +267,14 @@ def from_connection_string( An optional file snapshot on which to operate. This can be the snapshot ID string or the response returned from :func:`ShareClient.create_snapshot`. :param credential: - The credential with which to authenticate. This is optional if the + The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials or an account - shared access key. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :returns: A File client. :rtype: ~azure.storage.fileshare.ShareFileClient diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_client.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_client.py index c5ee3aaed14e..4a699e1c2f67 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_client.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_client.py @@ -62,10 +62,14 @@ class ShareClient(StorageAccountHostsMixin): # pylint: disable=too-many-public-m An optional share snapshot on which to operate. This can be the snapshot ID string or the response returned from :func:`create_snapshot`. :param credential: - The credential with which to authenticate. This is optional if the + The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials or an account - shared access key. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -80,7 +84,7 @@ def __init__( # type: ignore self, account_url, # type: str share_name, # type: str snapshot=None, # type: Optional[Union[str, Dict[str, Any]]] - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None @@ -120,7 +124,7 @@ def __init__( # type: ignore @classmethod def from_share_url(cls, share_url, # type: str snapshot=None, # type: Optional[Union[str, Dict[str, Any]]] - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> ShareClient @@ -130,10 +134,14 @@ def from_share_url(cls, share_url, # type: str An optional share snapshot on which to operate. This can be the snapshot ID string or the response returned from :func:`create_snapshot`. :param credential: - The credential with which to authenticate. This is optional if the + The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials or an account - shared access key. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :returns: A share client. :rtype: ~azure.storage.fileshare.ShareClient """ @@ -189,7 +197,7 @@ def from_connection_string( cls, conn_str, # type: str share_name, # type: str snapshot=None, # type: Optional[str] - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> ShareClient @@ -203,10 +211,14 @@ def from_connection_string( The optional share snapshot on which to operate. This can be the snapshot ID string or the response returned from :func:`create_snapshot`. :param credential: - The credential with which to authenticate. This is optional if the + The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials or an account - shared access key. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :returns: A share client. :rtype: ~azure.storage.fileshare.ShareClient diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_service_client.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_service_client.py index 46721ee9ea86..da9c8a065957 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_service_client.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_service_client.py @@ -58,10 +58,14 @@ class ShareServiceClient(StorageAccountHostsMixin): in the URL path (e.g. share or file) will be discarded. This URL can be optionally authenticated with a SAS token. :param credential: - The credential with which to authenticate. This is optional if the + The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials or an account - shared access key. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -83,7 +87,7 @@ class ShareServiceClient(StorageAccountHostsMixin): """ def __init__( self, account_url, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None @@ -116,7 +120,7 @@ def _format_url(self, hostname): @classmethod def from_connection_string( cls, conn_str, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> ShareServiceClient """Create ShareServiceClient from a Connection String. @@ -124,10 +128,14 @@ def from_connection_string( :param str conn_str: A connection string to an Azure Storage account. :param credential: - The credential with which to authenticate. This is optional if the + The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials or an account - shared access key. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :returns: A File Share service client. :rtype: ~azure.storage.fileshare.ShareServiceClient diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_shared/base_client.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_shared/base_client.py index 3af10b496f22..8ccc285ca329 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_shared/base_client.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_shared/base_client.py @@ -9,6 +9,7 @@ Optional, Any, Tuple, + TYPE_CHECKING ) try: @@ -20,7 +21,7 @@ import six from azure.core.configuration import Configuration -from azure.core.credentials import AzureSasCredential +from azure.core.credentials import AzureSasCredential, AzureNamedKeyCredential from azure.core.exceptions import HttpResponseError from azure.core.pipeline import Pipeline from azure.core.pipeline.transport import RequestsTransport, HttpTransport @@ -53,6 +54,8 @@ from .._version import VERSION from .response_handlers import process_storage_error, PartialBatchErrorException +if TYPE_CHECKING: + from azure.core.credentials import TokenCredential _LOGGER = logging.getLogger(__name__) _SERVICE_PARAMS = { @@ -67,7 +70,7 @@ def __init__( self, parsed_url, # type: Any service, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None @@ -352,6 +355,8 @@ def _format_shared_key_credential(account_name, credential): if "account_key" not in credential: raise ValueError("Shared key credential missing 'account_key") return SharedKeyCredentialPolicy(**credential) + if isinstance(credential, AzureNamedKeyCredential): + return SharedKeyCredentialPolicy(credential.named_key.name, credential.named_key.key) return credential diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_directory_client_async.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_directory_client_async.py index 6b6ed4be57d2..70003f928a76 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_directory_client_async.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_directory_client_async.py @@ -54,10 +54,14 @@ class ShareDirectoryClient(AsyncStorageAccountHostsMixin, ShareDirectoryClientBa An optional share snapshot on which to operate. This can be the snapshot ID string or the response returned from :func:`ShareClient.create_snapshot`. :param credential: - The credential with which to authenticate. This is optional if the + The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials or an account - shared access key. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -75,7 +79,7 @@ def __init__( # type: ignore share_name, # type: str directory_path, # type: str snapshot=None, # type: Optional[Union[str, Dict[str, Any]]] - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Optional[Any] ): # type: (...) -> None diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_file_client_async.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_file_client_async.py index bdccbbdcc82f..a27def34d133 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_file_client_async.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_file_client_async.py @@ -111,10 +111,14 @@ class ShareFileClient(AsyncStorageAccountHostsMixin, ShareFileClientBase): An optional file snapshot on which to operate. This can be the snapshot ID string or the response returned from :func:`ShareClient.create_snapshot`. :param credential: - The credential with which to authenticate. This is optional if the + The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials or an account - shared access key. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -134,7 +138,7 @@ def __init__( # type: ignore share_name, # type: str file_path, # type: str snapshot=None, # type: Optional[Union[str, Dict[str, Any]]] - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_client_async.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_client_async.py index 437fb6d3050e..2fe3850b3270 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_client_async.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_client_async.py @@ -51,10 +51,14 @@ class ShareClient(AsyncStorageAccountHostsMixin, ShareClientBase): An optional share snapshot on which to operate. This can be the snapshot ID string or the response returned from :func:`create_snapshot`. :param credential: - The credential with which to authenticate. This is optional if the + The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials or an account - shared access key. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -71,7 +75,7 @@ def __init__( # type: ignore self, account_url, # type: str share_name, # type: str snapshot=None, # type: Optional[Union[str, Dict[str, Any]]] - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_service_client_async.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_service_client_async.py index 9cf53d32933b..482bebc73793 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_service_client_async.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_service_client_async.py @@ -51,10 +51,14 @@ class ShareServiceClient(AsyncStorageAccountHostsMixin, ShareServiceClientBase): in the URL path (e.g. share or file) will be discarded. This URL can be optionally authenticated with a SAS token. :param credential: - The credential with which to authenticate. This is optional if the + The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials or an account - shared access key. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -78,7 +82,7 @@ class ShareServiceClient(AsyncStorageAccountHostsMixin, ShareServiceClientBase): """ def __init__( self, account_url, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None diff --git a/sdk/storage/azure-storage-file-share/tests/recordings/test_file.test_azure_named_key_credential_access.yaml b/sdk/storage/azure-storage-file-share/tests/recordings/test_file.test_azure_named_key_credential_access.yaml new file mode 100644 index 000000000000..0477b4ee2baa --- /dev/null +++ b/sdk/storage/azure-storage-file-share/tests/recordings/test_file.test_azure_named_key_credential_access.yaml @@ -0,0 +1,217 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - azsdk-python-storage-file-share/12.9.0 Python/3.10.2 (Windows-10-10.0.19044-SP0) + x-ms-date: + - Thu, 30 Jun 2022 01:15:33 GMT + x-ms-version: + - '2021-06-08' + method: PUT + uri: https://storagename.file.core.windows.net/utsharedf1d138b?restype=share + response: + body: + string: "\uFEFFShareAlreadyExistsThe + specified share already exists.\nRequestId:f8bf0e99-201a-0033-411e-8c8569000000\nTime:2022-06-30T01:15:32.9622302Z" + headers: + content-length: + - '222' + content-type: + - application/xml + date: + - Thu, 30 Jun 2022 01:15:32 GMT + server: + - Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0 + x-ms-error-code: + - ShareAlreadyExists + x-ms-version: + - '2021-06-08' + status: + code: 409 + message: The specified share already exists. +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - azsdk-python-storage-file-share/12.9.0 Python/3.10.2 (Windows-10-10.0.19044-SP0) + x-ms-content-length: + - '1024' + x-ms-date: + - Thu, 30 Jun 2022 01:15:34 GMT + x-ms-file-attributes: + - none + x-ms-file-creation-time: + - now + x-ms-file-last-write-time: + - now + x-ms-file-permission: + - Inherit + x-ms-type: + - file + x-ms-version: + - '2021-06-08' + method: PUT + uri: https://storagename.file.core.windows.net/utsharedf1d138b/filedf1d138b + response: + body: + string: '' + headers: + content-length: + - '0' + date: + - Thu, 30 Jun 2022 01:15:32 GMT + etag: + - '"0x8DA5A3607D7398B"' + last-modified: + - Thu, 30 Jun 2022 01:15:33 GMT + server: + - Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0 + x-ms-file-attributes: + - Archive + x-ms-file-change-time: + - '2022-06-30T01:15:33.2625803Z' + x-ms-file-creation-time: + - '2022-06-30T01:15:33.2625803Z' + x-ms-file-id: + - '13835128424026341376' + x-ms-file-last-write-time: + - '2022-06-30T01:15:33.2625803Z' + x-ms-file-parent-id: + - '0' + x-ms-file-permission-key: + - 8723072292820393796*15109150453380773834 + x-ms-request-server-encrypted: + - 'true' + x-ms-version: + - '2021-06-08' + status: + code: 201 + message: Created +- request: + body: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '1024' + Content-Type: + - application/octet-stream + User-Agent: + - azsdk-python-storage-file-share/12.9.0 Python/3.10.2 (Windows-10-10.0.19044-SP0) + x-ms-date: + - Thu, 30 Jun 2022 01:15:34 GMT + x-ms-range: + - bytes=0-1023 + x-ms-version: + - '2021-06-08' + x-ms-write: + - update + method: PUT + uri: https://storagename.file.core.windows.net/utsharedf1d138b/filedf1d138b?comp=range + response: + body: + string: '' + headers: + content-length: + - '0' + content-md5: + - yaNM/IXZgmmMasifdgcavQ== + date: + - Thu, 30 Jun 2022 01:15:32 GMT + etag: + - '"0x8DA5A3607F85154"' + last-modified: + - Thu, 30 Jun 2022 01:15:33 GMT + server: + - Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0 + x-ms-file-last-write-time: + - '2022-06-30T01:15:33.4794580Z' + x-ms-request-server-encrypted: + - 'true' + x-ms-version: + - '2021-06-08' + status: + code: 201 + message: Created +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-storage-file-share/12.9.0 Python/3.10.2 (Windows-10-10.0.19044-SP0) + x-ms-date: + - Thu, 30 Jun 2022 01:15:34 GMT + x-ms-version: + - '2021-06-08' + method: HEAD + uri: https://storagename.file.core.windows.net/utsharedf1d138b/filedf1d138b + response: + body: + string: '' + headers: + content-length: + - '1024' + content-type: + - application/octet-stream + date: + - Thu, 30 Jun 2022 01:15:33 GMT + etag: + - '"0x8DA5A3607F85154"' + last-modified: + - Thu, 30 Jun 2022 01:15:33 GMT + server: + - Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0 + x-ms-file-attributes: + - Archive + x-ms-file-change-time: + - '2022-06-30T01:15:33.4794580Z' + x-ms-file-creation-time: + - '2022-06-30T01:15:33.2625803Z' + x-ms-file-id: + - '13835128424026341376' + x-ms-file-last-write-time: + - '2022-06-30T01:15:33.4794580Z' + x-ms-file-parent-id: + - '0' + x-ms-file-permission-key: + - 8723072292820393796*15109150453380773834 + x-ms-lease-state: + - available + x-ms-lease-status: + - unlocked + x-ms-server-encrypted: + - 'true' + x-ms-type: + - File + x-ms-version: + - '2021-06-08' + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/storage/azure-storage-file-share/tests/recordings/test_file_async.test_azure_named_key_credential_access.yaml b/sdk/storage/azure-storage-file-share/tests/recordings/test_file_async.test_azure_named_key_credential_access.yaml new file mode 100644 index 000000000000..f86ec867e7ca --- /dev/null +++ b/sdk/storage/azure-storage-file-share/tests/recordings/test_file_async.test_azure_named_key_credential_access.yaml @@ -0,0 +1,153 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/xml + User-Agent: + - azsdk-python-storage-file-share/12.9.0 Python/3.10.2 (Windows-10-10.0.19044-SP0) + x-ms-date: + - Thu, 30 Jun 2022 01:16:46 GMT + x-ms-version: + - '2021-06-08' + method: PUT + uri: https://storagename.file.core.windows.net/utshare5f4c1608?restype=share + response: + body: + string: '' + headers: + content-length: '0' + date: Thu, 30 Jun 2022 01:16:44 GMT + etag: '"0x8DA5A3632D1E948"' + last-modified: Thu, 30 Jun 2022 01:16:45 GMT + server: Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0 + x-ms-version: '2021-06-08' + status: + code: 201 + message: Created + url: https://vincenttrancanary.file.core.windows.net/utshare5f4c1608?restype=share +- request: + body: null + headers: + Accept: + - application/xml + User-Agent: + - azsdk-python-storage-file-share/12.9.0 Python/3.10.2 (Windows-10-10.0.19044-SP0) + x-ms-content-length: + - '1024' + x-ms-date: + - Thu, 30 Jun 2022 01:16:46 GMT + x-ms-file-attributes: + - none + x-ms-file-creation-time: + - now + x-ms-file-last-write-time: + - now + x-ms-file-permission: + - Inherit + x-ms-type: + - file + x-ms-version: + - '2021-06-08' + method: PUT + uri: https://storagename.file.core.windows.net/utshare5f4c1608/file5f4c1608 + response: + body: + string: '' + headers: + content-length: '0' + date: Thu, 30 Jun 2022 01:16:45 GMT + etag: '"0x8DA5A36333D6D57"' + last-modified: Thu, 30 Jun 2022 01:16:46 GMT + server: Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0 + x-ms-file-attributes: Archive + x-ms-file-change-time: '2022-06-30T01:16:46.0744023Z' + x-ms-file-creation-time: '2022-06-30T01:16:46.0744023Z' + x-ms-file-id: '13835128424026341376' + x-ms-file-last-write-time: '2022-06-30T01:16:46.0744023Z' + x-ms-file-parent-id: '0' + x-ms-file-permission-key: 8723072292820393796*15109150453380773834 + x-ms-request-server-encrypted: 'true' + x-ms-version: '2021-06-08' + status: + code: 201 + message: Created + url: https://vincenttrancanary.file.core.windows.net/utshare5f4c1608/file5f4c1608 +- request: + body: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + headers: + Accept: + - application/xml + Content-Length: + - '1024' + Content-Type: + - application/octet-stream + User-Agent: + - azsdk-python-storage-file-share/12.9.0 Python/3.10.2 (Windows-10-10.0.19044-SP0) + x-ms-date: + - Thu, 30 Jun 2022 01:16:47 GMT + x-ms-range: + - bytes=0-1023 + x-ms-version: + - '2021-06-08' + x-ms-write: + - update + method: PUT + uri: https://storagename.file.core.windows.net/utshare5f4c1608/file5f4c1608?comp=range + response: + body: + string: '' + headers: + content-length: '0' + content-md5: yaNM/IXZgmmMasifdgcavQ== + date: Thu, 30 Jun 2022 01:16:45 GMT + etag: '"0x8DA5A36334DE5B2"' + last-modified: Thu, 30 Jun 2022 01:16:46 GMT + server: Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0 + x-ms-file-last-write-time: '2022-06-30T01:16:46.1823410Z' + x-ms-request-server-encrypted: 'true' + x-ms-version: '2021-06-08' + status: + code: 201 + message: Created + url: https://vincenttrancanary.file.core.windows.net/utshare5f4c1608/file5f4c1608?comp=range +- request: + body: null + headers: + Accept: + - application/xml + User-Agent: + - azsdk-python-storage-file-share/12.9.0 Python/3.10.2 (Windows-10-10.0.19044-SP0) + x-ms-date: + - Thu, 30 Jun 2022 01:16:47 GMT + x-ms-version: + - '2021-06-08' + method: HEAD + uri: https://storagename.file.core.windows.net/utshare5f4c1608/file5f4c1608 + response: + body: + string: '' + headers: + content-length: '1024' + content-type: application/octet-stream + date: Thu, 30 Jun 2022 01:16:45 GMT + etag: '"0x8DA5A36334DE5B2"' + last-modified: Thu, 30 Jun 2022 01:16:46 GMT + server: Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0 + x-ms-file-attributes: Archive + x-ms-file-change-time: '2022-06-30T01:16:46.1823410Z' + x-ms-file-creation-time: '2022-06-30T01:16:46.0744023Z' + x-ms-file-id: '13835128424026341376' + x-ms-file-last-write-time: '2022-06-30T01:16:46.1823410Z' + x-ms-file-parent-id: '0' + x-ms-file-permission-key: 8723072292820393796*15109150453380773834 + x-ms-lease-state: available + x-ms-lease-status: unlocked + x-ms-server-encrypted: 'true' + x-ms-type: File + x-ms-version: '2021-06-08' + status: + code: 200 + message: OK + url: https://vincenttrancanary.file.core.windows.net/utshare5f4c1608/file5f4c1608 +version: 1 diff --git a/sdk/storage/azure-storage-file-share/tests/test_file.py b/sdk/storage/azure-storage-file-share/tests/test_file.py index c1c96e30c924..ad71cad0a6f5 100644 --- a/sdk/storage/azure-storage-file-share/tests/test_file.py +++ b/sdk/storage/azure-storage-file-share/tests/test_file.py @@ -13,7 +13,7 @@ import requests import uuid from azure.core import MatchConditions -from azure.core.credentials import AzureSasCredential +from azure.core.credentials import AzureSasCredential, AzureNamedKeyCredential from azure.core.exceptions import HttpResponseError, ResourceNotFoundError, ResourceExistsError from azure.storage.blob import BlobServiceClient from azure.storage.fileshare import ( @@ -2148,6 +2148,25 @@ def test_account_sas_credential(self, storage_account_name, storage_account_key) # Assert self.assertIsNotNone(properties) + @FileSharePreparer() + def test_azure_named_key_credential_access(self, storage_account_name, storage_account_key): + + self._setup(storage_account_name, storage_account_key) + file_client = self._create_file() + named_key = AzureNamedKeyCredential(storage_account_name, storage_account_key) + + # Act + file_client = ShareFileClient( + self.account_url(storage_account_name, "file"), + share_name=self.share_name, + file_path=file_client.file_name, + credential=named_key) + + properties = file_client.get_file_properties() + + # Assert + self.assertIsNotNone(properties) + @FileSharePreparer() def test_account_sas_raises_if_sas_already_in_uri(self, storage_account_name, storage_account_key): with self.assertRaises(ValueError): diff --git a/sdk/storage/azure-storage-file-share/tests/test_file_async.py b/sdk/storage/azure-storage-file-share/tests/test_file_async.py index 895ccf2785db..07edab47046c 100644 --- a/sdk/storage/azure-storage-file-share/tests/test_file_async.py +++ b/sdk/storage/azure-storage-file-share/tests/test_file_async.py @@ -9,6 +9,10 @@ import os from datetime import datetime, timedelta +from azure.core.credentials import AzureSasCredential, AzureNamedKeyCredential +from azure.core.pipeline.transport import AioHttpTransport +from multidict import CIMultiDict, CIMultiDictProxy + import requests import pytest import uuid @@ -2323,6 +2327,26 @@ async def test_account_sas_credential_async(self, storage_account_name, storage_ # Assert self.assertIsNotNone(properties) + @FileSharePreparer() + @AsyncStorageTestCase.await_prepared_test + async def test_azure_named_key_credential_access(self, storage_account_name, storage_account_key): + + self._setup(storage_account_name, storage_account_key) + file_client = await self._create_file(storage_account_name, storage_account_key) + named_key = AzureNamedKeyCredential(storage_account_name, storage_account_key) + + # Act + file_client = ShareFileClient( + self.account_url(storage_account_name, "file"), + share_name=self.share_name, + file_path=file_client.file_name, + credential=named_key) + + properties = await file_client.get_file_properties() + + # Assert + self.assertIsNotNone(properties) + @FileSharePreparer() def test_account_sas_raises_if_sas_already_in_uri(self, storage_account_name, storage_account_key): with self.assertRaises(ValueError): diff --git a/sdk/storage/azure-storage-queue/CHANGELOG.md b/sdk/storage/azure-storage-queue/CHANGELOG.md index 29b26de64538..775c6a4167fb 100644 --- a/sdk/storage/azure-storage-queue/CHANGELOG.md +++ b/sdk/storage/azure-storage-queue/CHANGELOG.md @@ -5,6 +5,7 @@ This version and all future versions will require Python 3.7+. Python 3.6 is no longer supported. ### Features Added +- Added support for `AzureNamedKeyCredential` as a valid `credential` type. ### Bugs Fixed diff --git a/sdk/storage/azure-storage-queue/azure/storage/queue/_queue_client.py b/sdk/storage/azure-storage-queue/azure/storage/queue/_queue_client.py index f4bbe136d593..efe95541fa1e 100644 --- a/sdk/storage/azure-storage-queue/azure/storage/queue/_queue_client.py +++ b/sdk/storage/azure-storage-queue/azure/storage/queue/_queue_client.py @@ -49,8 +49,12 @@ class QueueClient(StorageAccountHostsMixin, StorageEncryptionMixin): :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -75,7 +79,7 @@ class QueueClient(StorageAccountHostsMixin, StorageEncryptionMixin): def __init__( self, account_url, # type: str queue_name, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None @@ -120,7 +124,7 @@ def _format_url(self, hostname): @classmethod def from_queue_url(cls, queue_url, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> QueueClient @@ -130,8 +134,12 @@ def from_queue_url(cls, :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :returns: A queue client. :rtype: ~azure.storage.queue.QueueClient """ @@ -163,7 +171,7 @@ def from_queue_url(cls, def from_connection_string( cls, conn_str, # type: str queue_name, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> QueueClient @@ -177,8 +185,11 @@ def from_connection_string( The credentials with which to authenticate. This is optional if the account URL already has a SAS token, or the connection string already has shared access key values. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account shared access - key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + Credentials provided here will take precedence over those in the connection string. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :returns: A queue client. :rtype: ~azure.storage.queue.QueueClient diff --git a/sdk/storage/azure-storage-queue/azure/storage/queue/_queue_service_client.py b/sdk/storage/azure-storage-queue/azure/storage/queue/_queue_service_client.py index f8fcfaee5eb5..a0f090d7eda6 100644 --- a/sdk/storage/azure-storage-queue/azure/storage/queue/_queue_service_client.py +++ b/sdk/storage/azure-storage-queue/azure/storage/queue/_queue_service_client.py @@ -57,8 +57,12 @@ class QueueServiceClient(StorageAccountHostsMixin, StorageEncryptionMixin): :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -84,7 +88,7 @@ class QueueServiceClient(StorageAccountHostsMixin, StorageEncryptionMixin): def __init__( self, account_url, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None @@ -115,7 +119,7 @@ def _format_url(self, hostname): @classmethod def from_connection_string( cls, conn_str, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> QueueServiceClient """Create QueueServiceClient from a Connection String. @@ -126,8 +130,11 @@ def from_connection_string( The credentials with which to authenticate. This is optional if the account URL already has a SAS token, or the connection string already has shared access key values. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account shared access - key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + Credentials provided here will take precedence over those in the connection string. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :returns: A Queue service client. :rtype: ~azure.storage.queue.QueueClient diff --git a/sdk/storage/azure-storage-queue/azure/storage/queue/_shared/base_client.py b/sdk/storage/azure-storage-queue/azure/storage/queue/_shared/base_client.py index 3af10b496f22..5ddaf3eb5c97 100644 --- a/sdk/storage/azure-storage-queue/azure/storage/queue/_shared/base_client.py +++ b/sdk/storage/azure-storage-queue/azure/storage/queue/_shared/base_client.py @@ -9,6 +9,7 @@ Optional, Any, Tuple, + TYPE_CHECKING ) try: @@ -20,7 +21,7 @@ import six from azure.core.configuration import Configuration -from azure.core.credentials import AzureSasCredential +from azure.core.credentials import AzureSasCredential, AzureNamedKeyCredential from azure.core.exceptions import HttpResponseError from azure.core.pipeline import Pipeline from azure.core.pipeline.transport import RequestsTransport, HttpTransport @@ -53,6 +54,9 @@ from .._version import VERSION from .response_handlers import process_storage_error, PartialBatchErrorException +if TYPE_CHECKING: + from azure.core.credentials import TokenCredential + _LOGGER = logging.getLogger(__name__) _SERVICE_PARAMS = { @@ -67,7 +71,7 @@ def __init__( self, parsed_url, # type: Any service, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None @@ -352,6 +356,8 @@ def _format_shared_key_credential(account_name, credential): if "account_key" not in credential: raise ValueError("Shared key credential missing 'account_key") return SharedKeyCredentialPolicy(**credential) + if isinstance(credential, AzureNamedKeyCredential): + return SharedKeyCredentialPolicy(credential.named_key.name, credential.named_key.key) return credential diff --git a/sdk/storage/azure-storage-queue/azure/storage/queue/aio/_queue_client_async.py b/sdk/storage/azure-storage-queue/azure/storage/queue/aio/_queue_client_async.py index cc69d597ac4f..eb0c01017f81 100644 --- a/sdk/storage/azure-storage-queue/azure/storage/queue/aio/_queue_client_async.py +++ b/sdk/storage/azure-storage-queue/azure/storage/queue/aio/_queue_client_async.py @@ -48,8 +48,12 @@ class QueueClient(AsyncStorageAccountHostsMixin, QueueClientBase, StorageEncrypt :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -83,7 +87,7 @@ def __init__( self, account_url, # type: str queue_name, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None diff --git a/sdk/storage/azure-storage-queue/azure/storage/queue/aio/_queue_service_client_async.py b/sdk/storage/azure-storage-queue/azure/storage/queue/aio/_queue_service_client_async.py index 873515006b43..368cee23f26d 100644 --- a/sdk/storage/azure-storage-queue/azure/storage/queue/aio/_queue_service_client_async.py +++ b/sdk/storage/azure-storage-queue/azure/storage/queue/aio/_queue_service_client_async.py @@ -56,8 +56,12 @@ class QueueServiceClient(AsyncStorageAccountHostsMixin, QueueServiceClientBase, :param credential: The credentials with which to authenticate. This is optional if the account URL already has a SAS token. The value can be a SAS token string, - an instance of a AzureSasCredential from azure.core.credentials, an account - shared access key, or an instance of a TokenCredentials class from azure.identity. + an instance of a AzureSasCredential or AzureNamedKeyCredential from azure.core.credentials, + an account shared access key, or an instance of a TokenCredentials class from azure.identity. + If the resource URI already contains a SAS token, this will be ignored in favor of an explicit credential + - except in the case of AzureSasCredential, where the conflicting SAS tokens will raise a ValueError. + If using an instance of AzureNamedKeyCredential, "name" should be the storage account name, and "key" + should be the storage account key. :keyword str api_version: The Storage API version to use for requests. Default value is the most recent service version that is compatible with the current SDK. Setting to an older version may result in reduced feature compatibility. @@ -83,7 +87,7 @@ class QueueServiceClient(AsyncStorageAccountHostsMixin, QueueServiceClientBase, def __init__( self, account_url, # type: str - credential=None, # type: Optional[Any] + credential=None, # type: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "TokenCredential"]] # pylint: disable=line-too-long **kwargs # type: Any ): # type: (...) -> None diff --git a/sdk/storage/azure-storage-queue/tests/recordings/test_queue.test_azure_named_key_credential_access.yaml b/sdk/storage/azure-storage-queue/tests/recordings/test_queue.test_azure_named_key_credential_access.yaml new file mode 100644 index 000000000000..880fc14b7fdc --- /dev/null +++ b/sdk/storage/azure-storage-queue/tests/recordings/test_queue.test_azure_named_key_credential_access.yaml @@ -0,0 +1,117 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - azsdk-python-storage-queue/12.4.0 Python/3.10.2 (Windows-10-10.0.19044-SP0) + x-ms-date: + - Thu, 30 Jun 2022 01:31:30 GMT + x-ms-version: + - '2021-02-12' + method: PUT + uri: https://storagename.queue.core.windows.net/pyqueuesyncf7f71410 + response: + body: + string: '' + headers: + content-length: + - '0' + date: + - Thu, 30 Jun 2022 01:31:29 GMT + server: + - Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0 + x-ms-version: + - '2021-02-12' + status: + code: 201 + message: Created +- request: + body: ' + + message1' + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '103' + Content-Type: + - application/xml + User-Agent: + - azsdk-python-storage-queue/12.4.0 Python/3.10.2 (Windows-10-10.0.19044-SP0) + x-ms-date: + - Thu, 30 Jun 2022 01:31:31 GMT + x-ms-version: + - '2021-02-12' + method: POST + uri: https://storagename.queue.core.windows.net/pyqueuesyncf7f71410/messages + response: + body: + string: "\uFEFFdc87d0b7-05a4-471c-8a0d-5c2ade57397bThu, + 30 Jun 2022 01:31:30 GMTThu, 07 Jul 2022 01:31:30 + GMTAgAAAAMAAAAAAAAAlHXjHyGM2AE=Thu, + 30 Jun 2022 01:31:30 GMT" + headers: + content-type: + - application/xml + date: + - Thu, 30 Jun 2022 01:31:30 GMT + server: + - Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0 + transfer-encoding: + - chunked + x-ms-version: + - '2021-02-12' + status: + code: 201 + message: Created +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-storage-queue/12.4.0 Python/3.10.2 (Windows-10-10.0.19044-SP0) + x-ms-date: + - Thu, 30 Jun 2022 01:31:31 GMT + x-ms-version: + - '2021-02-12' + method: GET + uri: https://storagename.queue.core.windows.net/pyqueuesyncf7f71410/messages?peekonly=true + response: + body: + string: "\uFEFFdc87d0b7-05a4-471c-8a0d-5c2ade57397bThu, + 30 Jun 2022 01:31:30 GMTThu, 07 Jul 2022 01:31:30 + GMT0message1" + headers: + cache-control: + - no-cache + content-type: + - application/xml + date: + - Thu, 30 Jun 2022 01:31:30 GMT + server: + - Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0 + transfer-encoding: + - chunked + x-ms-version: + - '2021-02-12' + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/storage/azure-storage-queue/tests/recordings/test_queue_async.test_azure_named_key_credential_access.yaml b/sdk/storage/azure-storage-queue/tests/recordings/test_queue_async.test_azure_named_key_credential_access.yaml new file mode 100644 index 000000000000..708dfd79c783 --- /dev/null +++ b/sdk/storage/azure-storage-queue/tests/recordings/test_queue_async.test_azure_named_key_credential_access.yaml @@ -0,0 +1,91 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/xml + User-Agent: + - azsdk-python-storage-queue/12.4.0 Python/3.10.2 (Windows-10-10.0.19044-SP0) + x-ms-date: + - Thu, 30 Jun 2022 01:32:54 GMT + x-ms-version: + - '2021-02-12' + method: PUT + uri: https://storagename.queue.core.windows.net/pyqueueasync7b44168d + response: + body: + string: '' + headers: + content-length: '0' + date: Thu, 30 Jun 2022 01:32:53 GMT + server: Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0 + x-ms-version: '2021-02-12' + status: + code: 201 + message: Created + url: https://vincenttranstock.queue.core.windows.net/pyqueueasync7b44168d +- request: + body: ' + + message1' + headers: + Accept: + - application/xml + Content-Length: + - '103' + Content-Type: + - application/xml + User-Agent: + - azsdk-python-storage-queue/12.4.0 Python/3.10.2 (Windows-10-10.0.19044-SP0) + x-ms-date: + - Thu, 30 Jun 2022 01:32:55 GMT + x-ms-version: + - '2021-02-12' + method: POST + uri: https://storagename.queue.core.windows.net/pyqueueasync7b44168d/messages + response: + body: + string: "\uFEFFdee316b0-a564-41da-a385-006dcf1c8e50Thu, + 30 Jun 2022 01:32:54 GMTThu, 07 Jul 2022 01:32:54 + GMTAgAAAAMAAAAAAAAAav7cUSGM2AE=Thu, + 30 Jun 2022 01:32:54 GMT" + headers: + content-type: application/xml + date: Thu, 30 Jun 2022 01:32:53 GMT + server: Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0 + transfer-encoding: chunked + x-ms-version: '2021-02-12' + status: + code: 201 + message: Created + url: https://vincenttranstock.queue.core.windows.net/pyqueueasync7b44168d/messages +- request: + body: null + headers: + Accept: + - application/xml + User-Agent: + - azsdk-python-storage-queue/12.4.0 Python/3.10.2 (Windows-10-10.0.19044-SP0) + x-ms-date: + - Thu, 30 Jun 2022 01:32:55 GMT + x-ms-version: + - '2021-02-12' + method: GET + uri: https://storagename.queue.core.windows.net/pyqueueasync7b44168d/messages?peekonly=true + response: + body: + string: "\uFEFFdee316b0-a564-41da-a385-006dcf1c8e50Thu, + 30 Jun 2022 01:32:54 GMTThu, 07 Jul 2022 01:32:54 + GMT0message1" + headers: + cache-control: no-cache + content-type: application/xml + date: Thu, 30 Jun 2022 01:32:53 GMT + server: Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0 + transfer-encoding: chunked + x-ms-version: '2021-02-12' + status: + code: 200 + message: OK + url: https://vincenttranstock.queue.core.windows.net/pyqueueasync7b44168d/messages?peekonly=true +version: 1 diff --git a/sdk/storage/azure-storage-queue/tests/test_queue.py b/sdk/storage/azure-storage-queue/tests/test_queue.py index 4c8e623ae383..4c2d0f723d61 100644 --- a/sdk/storage/azure-storage-queue/tests/test_queue.py +++ b/sdk/storage/azure-storage-queue/tests/test_queue.py @@ -14,7 +14,7 @@ date, ) -from azure.core.credentials import AzureSasCredential +from azure.core.credentials import AzureSasCredential, AzureNamedKeyCredential from azure.core.pipeline.transport import RequestsTransport from azure.core.exceptions import ( HttpResponseError, @@ -725,6 +725,22 @@ def test_account_sas(self, storage_account_name, storage_account_key): self.assertNotEqual('', message.id) self.assertEqual(u'message1', message.content) + @QueuePreparer() + def test_azure_named_key_credential_access(self, storage_account_name, storage_account_key): + + # Arrange + named_key = AzureNamedKeyCredential(storage_account_name, storage_account_key) + qsc = QueueServiceClient(self.account_url(storage_account_name, "queue"), named_key) + queue_client = self._get_queue_reference(qsc) + queue_client.create_queue() + queue_client.send_message(u'message1') + + # Act + result = queue_client.peek_messages() + + # Assert + self.assertIsNotNone(result) + @QueuePreparer() def test_account_sas_raises_if_sas_already_in_uri(self, storage_account_name, storage_account_key): with self.assertRaises(ValueError): diff --git a/sdk/storage/azure-storage-queue/tests/test_queue_async.py b/sdk/storage/azure-storage-queue/tests/test_queue_async.py index 0a67b04fe15a..4070cdde8bb3 100644 --- a/sdk/storage/azure-storage-queue/tests/test_queue_async.py +++ b/sdk/storage/azure-storage-queue/tests/test_queue_async.py @@ -14,7 +14,7 @@ date, ) -from azure.core.credentials import AzureSasCredential +from azure.core.credentials import AzureSasCredential, AzureNamedKeyCredential from azure.core.pipeline.transport import AioHttpTransport from azure.core.exceptions import ( HttpResponseError, @@ -779,6 +779,22 @@ async def test_account_sas(self, storage_account_name, storage_account_key): self.assertNotEqual('', message.id) self.assertEqual(u'message1', message.content) + @QueuePreparer() + async def test_azure_named_key_credential_access(self, storage_account_name, storage_account_key): + + # Arrange + named_key = AzureNamedKeyCredential(storage_account_name, storage_account_key) + qsc = QueueServiceClient(self.account_url(storage_account_name, "queue"), named_key) + queue_client = self._get_queue_reference(qsc) + await queue_client.create_queue() + await queue_client.send_message(u'message1') + + # Act + result = await queue_client.peek_messages() + + # Assert + self.assertIsNotNone(result) + @QueuePreparer() def test_account_sas_raises_if_sas_already_in_uri(self, storage_account_name, storage_account_key): with self.assertRaises(ValueError): diff --git a/shared_requirements.txt b/shared_requirements.txt index 99968b875cc9..c3fe9ce2b535 100644 --- a/shared_requirements.txt +++ b/shared_requirements.txt @@ -158,7 +158,7 @@ chardet<5,>=3.0.2 #override azure-storage-queue azure-core<2.0.0,>=1.23.1 #override azure-storage-file-share azure-core<2.0.0,>=1.23.1 #override azure-storage-file-datalake azure-core<2.0.0,>=1.23.1 -#override azure-storage-file-datalake azure-storage-blob<13.0.0,>=12.13.0 +#override azure-storage-file-datalake azure-storage-blob<13.0.0,>=12.14.0b1 #override azure-security-attestation azure-core<2.0.0,>=1.8.2 #override azure-data-tables msrest>=0.6.19 #override azure-schemaregistry azure-core<2.0.0,>=1.23.0