From d77a9e35a9c22dbaae242c2d547df8cf7d43c1b3 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Wed, 7 Oct 2020 10:43:34 -0700 Subject: [PATCH 1/3] Correct expires_on values of tokens from AzureCliCredential --- sdk/identity/azure-identity/CHANGELOG.md | 6 ++++++ .../azure/identity/_credentials/azure_cli.py | 15 ++++++++++----- .../azure-identity/tests/test_cli_credential.py | 7 +++---- .../tests/test_cli_credential_async.py | 7 +++---- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/sdk/identity/azure-identity/CHANGELOG.md b/sdk/identity/azure-identity/CHANGELOG.md index f9efb3545892..6917e5228c0f 100644 --- a/sdk/identity/azure-identity/CHANGELOG.md +++ b/sdk/identity/azure-identity/CHANGELOG.md @@ -1,5 +1,11 @@ # Release History +## 1.4.1 (2020-10-07) +### Fixed +- `AzureCliCredential.get_token` correctly sets token expiration time, + preventing clients from using expired tokens + ([#14331](https://github.com/Azure/azure-sdk-for-python/pull/14331)) + ## 1.4.0 (2020-08-10) ### Added - `DefaultAzureCredential` uses the value of environment variable diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py b/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py index 8e609d83ff05..24d7a48577d1 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py @@ -8,6 +8,7 @@ import platform import re import sys +import time from typing import TYPE_CHECKING import subprocess @@ -68,14 +69,18 @@ def get_token(self, *scopes, **kwargs): # pylint:disable=no-self-use,unused-arg def parse_token(output): """Parse output of 'az account get-access-token' to an AccessToken. - In particular, convert the CLI's "expiresOn" value, the string representation of a naive datetime, to epoch seconds. + In particular, convert the "expiresOn" value to epoch seconds. This value is a naive local datetime as returned by + datetime.fromtimestamp. """ try: token = json.loads(output) - parsed_expires_on = datetime.strptime(token["expiresOn"], "%Y-%m-%d %H:%M:%S.%f") - - # calculate seconds since the epoch; parsed_expires_on is naive - expires_on = (parsed_expires_on - datetime.fromtimestamp(0)).total_seconds() + dt = datetime.strptime(token["expiresOn"], "%Y-%m-%d %H:%M:%S.%f") + if hasattr(dt, "timestamp"): + # Python >= 3.3 + expires_on = dt.timestamp() + else: + # taken from Python 3.5's datetime.timestamp() + expires_on = time.mktime((dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, -1, -1, -1)) return AccessToken(token["accessToken"], int(expires_on)) except (KeyError, ValueError): diff --git a/sdk/identity/azure-identity/tests/test_cli_credential.py b/sdk/identity/azure-identity/tests/test_cli_credential.py index 3467a451356a..fc94ce97f931 100644 --- a/sdk/identity/azure-identity/tests/test_cli_credential.py +++ b/sdk/identity/azure-identity/tests/test_cli_credential.py @@ -51,11 +51,10 @@ def test_get_token(): """The credential should parse the CLI's output to an AccessToken""" access_token = "access token" - valid_seconds = 42 + expected_expires_on = 1602015811 successful_output = json.dumps( { - # expiresOn is a naive datetime representing valid_seconds from the epoch - "expiresOn": datetime.fromtimestamp(valid_seconds).strftime("%Y-%m-%d %H:%M:%S.%f"), + "expiresOn": datetime.fromtimestamp(expected_expires_on).strftime("%Y-%m-%d %H:%M:%S.%f"), "accessToken": access_token, "subscription": "some-guid", "tenant": "some-guid", @@ -68,7 +67,7 @@ def test_get_token(): assert token.token == access_token assert type(token.expires_on) == int - assert token.expires_on == valid_seconds + assert token.expires_on == expected_expires_on def test_cli_not_installed_linux(): diff --git a/sdk/identity/azure-identity/tests/test_cli_credential_async.py b/sdk/identity/azure-identity/tests/test_cli_credential_async.py index ae9dec90a341..947d5370d4a6 100644 --- a/sdk/identity/azure-identity/tests/test_cli_credential_async.py +++ b/sdk/identity/azure-identity/tests/test_cli_credential_async.py @@ -75,11 +75,10 @@ async def test_get_token(): """The credential should parse the CLI's output to an AccessToken""" access_token = "access token" - valid_seconds = 42 + expected_expires_on = 1602015811 successful_output = json.dumps( { - # expiresOn is a naive datetime representing valid_seconds from the epoch - "expiresOn": datetime.fromtimestamp(valid_seconds).strftime("%Y-%m-%d %H:%M:%S.%f"), + "expiresOn": datetime.fromtimestamp(expected_expires_on).strftime("%Y-%m-%d %H:%M:%S.%f"), "accessToken": access_token, "subscription": "some-guid", "tenant": "some-guid", @@ -93,7 +92,7 @@ async def test_get_token(): assert token.token == access_token assert type(token.expires_on) == int - assert token.expires_on == valid_seconds + assert token.expires_on == expected_expires_on async def test_cli_not_installed_linux(): From 7c8737f34eb88f9d1f218d5e478d16f302afd20e Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Wed, 7 Oct 2020 13:59:25 -0700 Subject: [PATCH 2/3] update package version --- sdk/identity/azure-identity/azure/identity/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/identity/azure-identity/azure/identity/_version.py b/sdk/identity/azure-identity/azure/identity/_version.py index c374812dc6f3..a5b3f7bf1cb2 100644 --- a/sdk/identity/azure-identity/azure/identity/_version.py +++ b/sdk/identity/azure-identity/azure/identity/_version.py @@ -2,4 +2,4 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ -VERSION = "1.4.0" +VERSION = "1.4.1" From 7d40be235b53985bb4475eecb71f333dd0928410 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Wed, 7 Oct 2020 14:17:11 -0700 Subject: [PATCH 3/3] changelog links to issue --- sdk/identity/azure-identity/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/identity/azure-identity/CHANGELOG.md b/sdk/identity/azure-identity/CHANGELOG.md index 6917e5228c0f..2edd709843cb 100644 --- a/sdk/identity/azure-identity/CHANGELOG.md +++ b/sdk/identity/azure-identity/CHANGELOG.md @@ -4,7 +4,7 @@ ### Fixed - `AzureCliCredential.get_token` correctly sets token expiration time, preventing clients from using expired tokens - ([#14331](https://github.com/Azure/azure-sdk-for-python/pull/14331)) + ([#14345](https://github.com/Azure/azure-sdk-for-python/issues/14345)) ## 1.4.0 (2020-08-10) ### Added