Skip to content

Commit 4aaa88a

Browse files
[Tables] Adds support for AzureNamedKeyCredential (#18456)
#18441
1 parent 6bc810e commit 4aaa88a

File tree

53 files changed

+1146
-1009
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1146
-1009
lines changed

sdk/tables/azure-data-tables/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
* Changed optional `value` and `type` arguments of `EntityProperty` to required.
1616
* Renamed `EntityProperty.type` to `EntityProperty.edm_type`.
1717
* `BatchErrorException` has been renamed to `TableTransactionError`.
18+
* The only supported credentials are `AzureNamedKeyCredential`, `AzureSasCredential`, or authentication by connection string
1819
* `EntityProperty` is now a tuple.
1920
* Removed `date` and `api_version` from the `TableItem` class.
2021

22+
2123
**Fixes**
2224
* Fixed issue with Cosmos merge operations.
2325
* Removed legacy Storage policies from pipeline.

sdk/tables/azure-data-tables/azure/data/tables/_authentication.py

+13-12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
# --------------------------------------------------------------------------
66

77
import logging
8+
import sys
9+
from typing import Union
810

911
try:
1012
from urllib.parse import urlparse
@@ -32,6 +34,9 @@
3234
_wrap_exception,
3335
)
3436

37+
if sys.version_info > (3, 5):
38+
from typing import Awaitable # pylint: disable=ungrouped-imports
39+
3540
logger = logging.getLogger(__name__)
3641

3742

@@ -45,11 +50,8 @@ class AzureSigningError(ClientAuthenticationError):
4550

4651
# pylint: disable=no-self-use
4752
class SharedKeyCredentialPolicy(SansIOHTTPPolicy):
48-
def __init__(
49-
self, account_name, account_key, is_emulated=False
50-
):
51-
self.account_name = account_name
52-
self.account_key = account_key
53+
def __init__(self, credential, is_emulated=False):
54+
self._credential = credential
5355
self.is_emulated = is_emulated
5456

5557
def _get_headers(self, request, headers_to_sign):
@@ -82,10 +84,10 @@ def _get_canonicalized_resource(self, request):
8284
)
8385
):
8486
uri_path = URL(uri_path)
85-
return "/" + self.account_name + str(uri_path)
87+
return "/" + self._credential.named_key.name + str(uri_path)
8688
except TypeError:
8789
pass
88-
return "/" + self.account_name + uri_path
90+
return "/" + self._credential.named_key.name + uri_path
8991

9092
def _get_canonicalized_headers(self, request):
9193
string_to_sign = ""
@@ -101,17 +103,16 @@ def _get_canonicalized_headers(self, request):
101103

102104
def _add_authorization_header(self, request, string_to_sign):
103105
try:
104-
signature = _sign_string(self.account_key, string_to_sign)
105-
auth_string = "SharedKey " + self.account_name + ":" + signature
106+
signature = _sign_string(self._credential.named_key.key, string_to_sign)
107+
auth_string = "SharedKey " + self._credential.named_key.name + ":" + signature
106108
request.headers["Authorization"] = auth_string
107109
except Exception as ex:
108110
# Wrap any error that occurred as signing error
109111
# Doing so will clarify/locate the source of problem
110112
raise _wrap_exception(ex, AzureSigningError)
111113

112-
def on_request(
113-
self, request
114-
): # type: (PipelineRequest) -> Union[None, Awaitable[None]]
114+
def on_request(self, request):
115+
# type: (PipelineRequest) -> Union[None, Awaitable[None]]
115116
self.sign_request(request)
116117

117118
def sign_request(self, request):

sdk/tables/azure-data-tables/azure/data/tables/_base_client.py

+6-23
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from urllib2 import quote # type: ignore
1414

1515
import six
16-
from azure.core.credentials import AzureSasCredential
16+
from azure.core.credentials import AzureSasCredential, AzureNamedKeyCredential
1717
from azure.core.utils import parse_connection_string
1818
from azure.core.pipeline.transport import (
1919
HttpTransport,
@@ -30,7 +30,7 @@
3030
AzureSasCredentialPolicy,
3131
NetworkTraceLoggingPolicy,
3232
CustomHookPolicy,
33-
RequestIdPolicy
33+
RequestIdPolicy,
3434
)
3535

3636
from ._generated import AzureTable
@@ -111,7 +111,7 @@ def __init__(
111111
self.account_name = account[0] if len(account) > 1 else None
112112

113113
secondary_hostname = None
114-
self.credential = format_shared_key_credential(account, credential)
114+
self.credential = credential
115115
if self.scheme.lower() != "https" and hasattr(self.credential, "get_token"):
116116
raise ValueError("Token credential is only supported with HTTPS.")
117117
if hasattr(self.credential, "account_name"):
@@ -259,6 +259,8 @@ def _configure_credential(self, credential):
259259
self._credential_policy = credential
260260
elif isinstance(credential, AzureSasCredential):
261261
self._credential_policy = AzureSasCredentialPolicy(credential)
262+
elif isinstance(credential, AzureNamedKeyCredential):
263+
self._credential_policy = SharedKeyCredentialPolicy(credential)
262264
elif credential is not None:
263265
raise TypeError("Unsupported credential: {}".format(credential))
264266

@@ -344,32 +346,13 @@ def __exit__(self, *args): # pylint: disable=arguments-differ
344346
pass
345347

346348

347-
def format_shared_key_credential(account, credential):
348-
if isinstance(credential, six.string_types):
349-
if len(account) < 2:
350-
raise ValueError(
351-
"Unable to determine account name for shared key credential."
352-
)
353-
credential = {"account_name": account[0], "account_key": credential}
354-
if isinstance(credential, dict):
355-
if "account_name" not in credential:
356-
raise ValueError("Shared key credential missing 'account_name")
357-
if "account_key" not in credential:
358-
raise ValueError("Shared key credential missing 'account_key")
359-
return SharedKeyCredentialPolicy(**credential)
360-
return credential
361-
362-
363349
def parse_connection_str(conn_str, credential, keyword_args):
364350
conn_settings = parse_connection_string(conn_str)
365351
primary = None
366352
secondary = None
367353
if not credential:
368354
try:
369-
credential = {
370-
"account_name": conn_settings["accountname"],
371-
"account_key": conn_settings["accountkey"],
372-
}
355+
credential = AzureNamedKeyCredential(name=conn_settings["accountname"], key=conn_settings["accountkey"])
373356
except KeyError:
374357
credential = conn_settings.get("sharedaccesssignature")
375358
# if "sharedaccesssignature" in conn_settings:

sdk/tables/azure-data-tables/azure/data/tables/_shared_access_signature.py

+5-7
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,15 @@ class SharedAccessSignature(object):
2828
generate_*_shared_access_signature method directly.
2929
"""
3030

31-
def __init__(self, account_name, account_key, x_ms_version=DEFAULT_X_MS_VERSION):
31+
def __init__(self, credential, x_ms_version=DEFAULT_X_MS_VERSION):
3232
"""
33-
:param str account_name:
34-
The storage account name used to generate the shared access signatures.
35-
:param str account_key:
36-
The access key to generate the shares access signatures.
33+
:param credential: The credential used for authenticating requests
34+
:type credential: :class:`~azure.core.credentials.NamedKeyCredential`
3735
:param str x_ms_version:
3836
The service version used to generate the shared access signatures.
3937
"""
40-
self.account_name = account_name
41-
self.account_key = account_key
38+
self.account_name = credential.named_key.name
39+
self.account_key = credential.named_key.key
4240
self.x_ms_version = x_ms_version
4341

4442
def generate_account(

sdk/tables/azure-data-tables/azure/data/tables/_table_client.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def __init__(
5050
self,
5151
account_url, # type: str
5252
table_name, # type: str
53-
credential=None, # type: str
53+
credential=None, # type: Union[AzureNamedKeyCredential, AzureSasCredential]
5454
**kwargs # type: Any
5555
):
5656
# type: (...) -> None
@@ -66,8 +66,9 @@ def __init__(
6666
account URL already has a SAS token, or the connection string already has shared
6767
access key values. The value can be a SAS token string or an account shared access
6868
key.
69-
:type credential: str
70-
69+
:type credential:
70+
:class:`~azure.core.credentials.AzureNamedKeyCredential` or
71+
:class:`~azure.core.credentials.AzureSasCredential`
7172
:returns: None
7273
"""
7374
if not table_name:

sdk/tables/azure-data-tables/azure/data/tables/_table_service_client.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ class TableServiceClient(TablesBaseClient):
3636
account URL already has a SAS token, or the connection string already has shared
3737
access key values. The value can be a SAS token string or an account shared access
3838
key.
39-
:type credential: str
39+
:type credential:
40+
:class:`~azure.core.credentials.AzureNamedKeyCredential` or
41+
:class:`~azure.core.credentials.AzureSasCredential`
4042
:returns: None
4143
4244
.. admonition:: Example:

sdk/tables/azure-data-tables/azure/data/tables/_table_shared_access_signature.py

+13-21
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Licensed under the MIT License. See License.txt in the project root for
44
# license information.
55
# --------------------------------------------------------------------------
6-
from typing import Union
6+
from typing import Union, Any
77

88
from ._models import AccountSasPermissions
99
from ._common_conversion import _sign_string
@@ -17,8 +17,7 @@
1717

1818

1919
def generate_account_sas(
20-
account_name, # type:str
21-
account_key, # type:str
20+
credential, # type: AzureNamedKeyCredential
2221
resource_types, # type:ResourceTypes
2322
permission, # type:Union[str,AccountSasPermissions]
2423
expiry, # type:Union[datetime,str]
@@ -29,10 +28,8 @@ def generate_account_sas(
2928
Generates a shared access signature for the table service.
3029
Use the returned signature with the sas_token parameter of TableService.
3130
32-
:param account_name: Account name
33-
:type account_name: str
34-
:param account_key: Account key
35-
:type account_key: str
31+
:param credential: Credential for the Azure account
32+
:type credential: :class:`~azure.core.credentials.AzureNamedKeyCredential`
3633
:param resource_types:
3734
Specifies the resource types that are accessible with the account SAS.
3835
:type resource_types: ResourceTypes
@@ -70,11 +67,11 @@ def generate_account_sas(
7067
:return: A Shared Access Signature (sas) token.
7168
:rtype: str
7269
"""
73-
_validate_not_none("account_name", account_name)
74-
_validate_not_none("account_key", account_key)
70+
_validate_not_none("account_name", credential.named_key.name)
71+
_validate_not_none("account_key", credential.named_key.key)
7572
if permission is str:
7673
permission = AccountSasPermissions.from_string(permission=permission)
77-
sas = TableSharedAccessSignature(account_name, account_key)
74+
sas = TableSharedAccessSignature(credential)
7875
return sas.generate_account(
7976
"t",
8077
resource_types,
@@ -87,8 +84,7 @@ def generate_account_sas(
8784

8885

8986
def generate_table_sas(
90-
account_name, # type: str
91-
account_key, # type: str
87+
credential, # type: AzureNamedKeyCredential
9288
table_name, # type: str
9389
**kwargs # type: Any
9490
): # type: (...) -> str
@@ -143,7 +139,7 @@ def generate_table_sas(
143139
:rtype: str
144140
"""
145141

146-
sas = TableSharedAccessSignature(account_name, account_key)
142+
sas = TableSharedAccessSignature(credential)
147143
return sas.generate_table(
148144
table_name=table_name,
149145
permission=kwargs.pop("permission", None),
@@ -168,17 +164,13 @@ class TableSharedAccessSignature(SharedAccessSignature):
168164
generate_*_shared_access_signature method directly.
169165
"""
170166

171-
def __init__(self, account_name, account_key):
167+
def __init__(self, credential):
172168
"""
173-
:param account_name:
174-
The storage account name used to generate the shared access signatures.
175-
:type account_name: str
176-
:param account_key:
177-
The access key to generate the shares access signatures.
178-
:type account_key: str
169+
:param credential: The credential used for authenticating requests
170+
:type credential: :class:`~azure.core.credentials.NamedKeyCredential`
179171
"""
180172
super(TableSharedAccessSignature, self).__init__(
181-
account_name, account_key, x_ms_version=X_MS_VERSION
173+
credential, x_ms_version=X_MS_VERSION
182174
)
183175

184176
def generate_table(

sdk/tables/azure-data-tables/azure/data/tables/aio/_base_client_async.py

+8-6
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
# license information.
55
# --------------------------------------------------------------------------
66

7-
from typing import Any, List, Mapping
7+
from typing import Any, List, Mapping, Optional, Union
88
from uuid import uuid4
99

10-
from azure.core.credentials import AzureSasCredential
10+
from azure.core.credentials import AzureSasCredential, AzureNamedKeyCredential
1111
from azure.core.pipeline.policies import (
1212
ContentDecodePolicy,
1313
AsyncBearerTokenCredentialPolicy,
@@ -19,7 +19,7 @@
1919
AzureSasCredentialPolicy,
2020
RequestIdPolicy,
2121
CustomHookPolicy,
22-
NetworkTraceLoggingPolicy
22+
NetworkTraceLoggingPolicy,
2323
)
2424
from azure.core.pipeline.transport import (
2525
AsyncHttpTransport,
@@ -40,9 +40,9 @@ class AsyncTablesBaseClient(AccountHostsMixin):
4040

4141
def __init__(
4242
self,
43-
account_url, # type: str
44-
credential=None, # type: str
45-
**kwargs # type: Any
43+
account_url: str,
44+
credential: Optional[Union[AzureSasCredential, AzureNamedKeyCredential]] = None,
45+
**kwargs: Any
4646
):
4747
# type: (...) -> None
4848
super(AsyncTablesBaseClient, self).__init__(account_url, credential=credential, **kwargs)
@@ -77,6 +77,8 @@ def _configure_credential(self, credential):
7777
self._credential_policy = credential
7878
elif isinstance(credential, AzureSasCredential):
7979
self._credential_policy = AzureSasCredentialPolicy(credential)
80+
elif isinstance(credential, AzureNamedKeyCredential):
81+
self._credential_policy = SharedKeyCredentialPolicy(credential)
8082
elif credential is not None:
8183
raise TypeError("Unsupported credential: {}".format(credential))
8284

sdk/tables/azure-data-tables/azure/data/tables/aio/_table_client_async.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from urlparse import urlparse # type: ignore
1212
from urllib2 import unquote # type: ignore
1313

14-
from azure.core.credentials import AzureSasCredential
14+
from azure.core.credentials import AzureNamedKeyCredential, AzureSasCredential
1515
from azure.core.async_paging import AsyncItemPaged
1616
from azure.core.exceptions import ResourceNotFoundError, HttpResponseError
1717
from azure.core.tracing.decorator import distributed_trace
@@ -43,7 +43,7 @@ def __init__(
4343
self,
4444
account_url: str,
4545
table_name: str,
46-
credential: Optional[Union[AzureSasCredential]] = None,
46+
credential: Optional[Union[AzureSasCredential, AzureNamedKeyCredential]] = None,
4747
**kwargs
4848
) -> None:
4949
"""Create TableClient from a Credential.
@@ -58,7 +58,9 @@ def __init__(
5858
account URL already has a SAS token, or the connection string already has shared
5959
access key values. The value can be a SAS token string or an account shared access
6060
key.
61-
:type credential: str
61+
:type credential:
62+
:class:`~azure.core.credentials.AzureNamedKeyCredential` or
63+
:class:`~azure.core.credentials.AzureSasCredential`
6264
6365
:returns: None
6466
"""
@@ -109,7 +111,7 @@ def from_connection_string(
109111
def from_table_url(
110112
cls,
111113
table_url: str,
112-
credential: Optional[Union[AzureSasCredential]] = None,
114+
credential: Optional[Union[AzureSasCredential, AzureNamedKeyCredential]] = None,
113115
**kwargs
114116
) -> 'TableClient':
115117
"""A client to interact with a specific Table.
@@ -120,7 +122,9 @@ def from_table_url(
120122
The credentials with which to authenticate. This is optional if the
121123
account URL already has a SAS token. The value can be a SAS token string, an account
122124
shared access key.
123-
:type credential: str
125+
:type credential:
126+
:class:`~azure.core.credentials.AzureNamedKeyCredential` or
127+
:class:`~azure.core.credentials.AzureSasCredential`
124128
:returns: A table client.
125129
:rtype: :class:`~azure.data.tables.TableClient`
126130
"""

0 commit comments

Comments
 (0)