From d47d29ef307fbe96c744eb89983c4ae03130fdd8 Mon Sep 17 00:00:00 2001 From: Colin-b Date: Thu, 28 Nov 2019 00:58:14 +0100 Subject: [PATCH 1/7] Avoid failure when building on master more than once --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e7a8d22..8f2ddd3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,4 +11,5 @@ deploy: provider: pypi username: __token__ edge: true - distributions: "sdist bdist_wheel" \ No newline at end of file + distributions: "sdist bdist_wheel" + skip_existing: true \ No newline at end of file From e129a6fdfcfbe78c552b5ac131744a8e4b07be09 Mon Sep 17 00:00:00 2001 From: Colin-b Date: Thu, 28 Nov 2019 01:06:02 +0100 Subject: [PATCH 2/7] Link to the travis-ci build and retrieve status --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 176f01d..d2ce498 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@

pypi version -Build status -Coverage +Build status +Coverage Code style: black -Number of tests +Number of tests Number of downloads

From 0659791149380e1dffe439d0d13d06d9c11ecbfb Mon Sep 17 00:00:00 2001 From: Colin-b Date: Thu, 28 Nov 2019 01:07:31 +0100 Subject: [PATCH 3/7] Update the number of test cases (while it's not retrieved from travis-ci) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d2ce498..55e0797 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Build status Coverage Code style: black -Number of tests +Number of tests Number of downloads

From f95ecd833d52341ebe0e2c974d133577ae124dd9 Mon Sep 17 00:00:00 2001 From: Colin-b Date: Thu, 28 Nov 2019 01:20:09 +0100 Subject: [PATCH 4/7] Ensure full coverage --- .travis.yml | 2 +- setup.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8f2ddd3..686c11c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ python: install: - pip install .[testing] script: - - pytest + - pytest --cov=requests_auth --cov-fail-under=100 deploy: provider: pypi username: __token__ diff --git a/setup.py b/setup.py index ae20ee1..f723750 100644 --- a/setup.py +++ b/setup.py @@ -52,6 +52,8 @@ "pyjwt==1.*", # Used to mock responses to requests "pytest-responses==0.4.*", + # Used to check coverage + "pytest-cov==2.*", ] }, python_requires=">=3.6", From 77ef8d3d234fab08adac2ff6308faab736b472a2 Mon Sep 17 00:00:00 2001 From: bottoy Date: Thu, 12 Dec 2019 15:12:40 +0100 Subject: [PATCH 5/7] Bug in ClientCredentials Hi Colin, We have a bug on line 68 => TypeError: unsupported type for timedelta seconds component: str The casting to 'int' is not done so I propose to cast it explicitly to 'int'. Works locally ;-) Bram --- requests_auth/oauth2_tokens.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requests_auth/oauth2_tokens.py b/requests_auth/oauth2_tokens.py index fc14c16..6d33dd8 100644 --- a/requests_auth/oauth2_tokens.py +++ b/requests_auth/oauth2_tokens.py @@ -65,7 +65,7 @@ def add_access_token(self, key: str, token: str, expires_in: int): """ expiry = datetime.datetime.utcnow().replace( tzinfo=datetime.timezone.utc - ) + datetime.timedelta(seconds=expires_in) + ) + datetime.timedelta(seconds=int(expires_in)) self._add_token(key, token, expiry.timestamp()) def _add_token(self, key: str, token: str, expiry: float): From 3a52d66128597ecccf63ec9d991529adca40dba1 Mon Sep 17 00:00:00 2001 From: Colin-b Date: Thu, 12 Dec 2019 18:30:03 +0100 Subject: [PATCH 6/7] Reproduce failure due to expires_in sent as str --- tests/test_oauth2_authorization_code_pkce.py | 35 ++++++++++++++++++++ tests/test_oauth2_client_credential.py | 21 ++++++++++++ tests/test_oauth2_client_credential_okta.py | 21 ++++++++++++ tests/test_oauth2_resource_owner_password.py | 25 ++++++++++++++ 4 files changed, 102 insertions(+) diff --git a/tests/test_oauth2_authorization_code_pkce.py b/tests/test_oauth2_authorization_code_pkce.py index 0af7224..9a171f0 100644 --- a/tests/test_oauth2_authorization_code_pkce.py +++ b/tests/test_oauth2_authorization_code_pkce.py @@ -42,6 +42,41 @@ def test_oauth2_pkce_flow_get_code_is_sent_in_authorization_header_by_default( ) +def test_expires_in_sent_as_str( + token_cache, responses: RequestsMock, monkeypatch, browser_mock: BrowserMock +): + monkeypatch.setattr(requests_auth.authentication.os, "urandom", lambda x: b"1" * 63) + auth = requests_auth.OAuth2AuthorizationCodePKCE( + "http://provide_code", "http://provide_access_token" + ) + tab = browser_mock.add_response( + opened_url="http://provide_code?response_type=code&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F&code_challenge=5C_ph_KZ3DstYUc965SiqmKAA-ShvKF4Ut7daKd3fjc&code_challenge_method=S256", + reply_url="http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de", + ) + responses.add( + responses.POST, + "http://provide_access_token", + json={ + "access_token": "2YotnFZFEjr1zCsicMWpAA", + "token_type": "example", + "expires_in": "3600", + "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", + "example_parameter": "example_value", + }, + ) + assert ( + get_header(responses, auth).get("Authorization") + == "Bearer 2YotnFZFEjr1zCsicMWpAA" + ) + assert ( + get_request(responses, "http://provide_access_token/").body + == "code_verifier=MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx&grant_type=authorization_code&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F&response_type=code&code=SplxlOBeZQQYbYS6WxSbIA" + ) + tab.assert_success( + "You are now authenticated on 163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de. You may close this tab." + ) + + def test_nonce_is_sent_if_provided_in_authorization_url( token_cache, responses: RequestsMock, monkeypatch, browser_mock: BrowserMock ): diff --git a/tests/test_oauth2_client_credential.py b/tests/test_oauth2_client_credential.py index 4d71fac..10c8802 100644 --- a/tests/test_oauth2_client_credential.py +++ b/tests/test_oauth2_client_credential.py @@ -30,6 +30,27 @@ def test_oauth2_client_credentials_flow_token_is_sent_in_authorization_header_by ) +def test_expires_in_sent_as_str(token_cache, responses: RequestsMock): + auth = requests_auth.OAuth2ClientCredentials( + "http://provide_access_token", client_id="test_user", client_secret="test_pwd" + ) + responses.add( + responses.POST, + "http://provide_access_token", + json={ + "access_token": "2YotnFZFEjr1zCsicMWpAA", + "token_type": "example", + "expires_in": "3600", + "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", + "example_parameter": "example_value", + }, + ) + assert ( + get_header(responses, auth).get("Authorization") + == "Bearer 2YotnFZFEjr1zCsicMWpAA" + ) + + def test_with_invalid_grant_request_no_json(token_cache, responses: RequestsMock): auth = requests_auth.OAuth2ClientCredentials( "http://provide_access_token", client_id="test_user", client_secret="test_pwd" diff --git a/tests/test_oauth2_client_credential_okta.py b/tests/test_oauth2_client_credential_okta.py index c1a0101..b9330d6 100644 --- a/tests/test_oauth2_client_credential_okta.py +++ b/tests/test_oauth2_client_credential_okta.py @@ -26,3 +26,24 @@ def test_okta_client_credentials_flow_token_is_sent_in_authorization_header_by_d get_header(responses, auth).get("Authorization") == "Bearer 2YotnFZFEjr1zCsicMWpAA" ) + + +def test_expires_in_sent_as_str(token_cache, responses: RequestsMock): + auth = requests_auth.OktaClientCredentials( + "test_okta", client_id="test_user", client_secret="test_pwd" + ) + responses.add( + responses.POST, + "https://test_okta/oauth2/default/v1/token", + json={ + "access_token": "2YotnFZFEjr1zCsicMWpAA", + "token_type": "example", + "expires_in": "3600", + "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", + "example_parameter": "example_value", + }, + ) + assert ( + get_header(responses, auth).get("Authorization") + == "Bearer 2YotnFZFEjr1zCsicMWpAA" + ) diff --git a/tests/test_oauth2_resource_owner_password.py b/tests/test_oauth2_resource_owner_password.py index 86d6b0f..852056e 100644 --- a/tests/test_oauth2_resource_owner_password.py +++ b/tests/test_oauth2_resource_owner_password.py @@ -34,6 +34,31 @@ def test_oauth2_password_credentials_flow_token_is_sent_in_authorization_header_ ) +def test_expires_in_sent_as_str(token_cache, responses: RequestsMock): + auth = requests_auth.OAuth2ResourceOwnerPasswordCredentials( + "http://provide_access_token", username="test_user", password="test_pwd" + ) + responses.add( + responses.POST, + "http://provide_access_token", + json={ + "access_token": "2YotnFZFEjr1zCsicMWpAA", + "token_type": "example", + "expires_in": "3600", + "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", + "example_parameter": "example_value", + }, + ) + assert ( + get_header(responses, auth).get("Authorization") + == "Bearer 2YotnFZFEjr1zCsicMWpAA" + ) + assert ( + get_request(responses, "http://provide_access_token/").body + == "grant_type=password&username=test_user&password=test_pwd" + ) + + def test_scope_is_sent_as_is_when_provided_as_str(token_cache, responses: RequestsMock): auth = requests_auth.OAuth2ResourceOwnerPasswordCredentials( "http://provide_access_token", From 01d9500b6600a833b74f7369afaeb8b8dff551df Mon Sep 17 00:00:00 2001 From: Colin-b Date: Thu, 12 Dec 2019 18:33:00 +0100 Subject: [PATCH 7/7] Report build status from master branch Bump version to 5.0.2 Keep number of tests up to date --- CHANGELOG.md | 7 ++++++- README.md | 4 ++-- requests_auth/version.py | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8f57bf..c378f6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [5.0.2] - 2019-12-12 +### Fixed +- Handle expires_in sent as str instead of int. + ## [5.0.1] - 2019-11-28 ### Added - Allow to use & between authentication classes. @@ -114,7 +118,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Public release -[Unreleased]: https://github.com/Colin-b/requests_auth/compare/v5.0.1...HEAD +[Unreleased]: https://github.com/Colin-b/requests_auth/compare/v5.0.2...HEAD +[5.0.2]: https://github.com/Colin-b/requests_auth/compare/v5.0.1...v5.0.2 [5.0.1]: https://github.com/Colin-b/requests_auth/compare/v5.0.0...v5.0.1 [5.0.0]: https://github.com/Colin-b/requests_auth/compare/v4.1.0...v5.0.0 [4.1.0]: https://github.com/Colin-b/requests_auth/compare/v4.0.1...v4.1.0 diff --git a/README.md b/README.md index 55e0797..9e5a981 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@

pypi version -Build status +Build status Coverage Code style: black -Number of tests +Number of tests Number of downloads

diff --git a/requests_auth/version.py b/requests_auth/version.py index f309563..5269aff 100644 --- a/requests_auth/version.py +++ b/requests_auth/version.py @@ -3,4 +3,4 @@ # Major should be incremented in case there is a breaking change. (eg: 2.5.8 -> 3.0.0) # Minor should be incremented in case there is an enhancement. (eg: 2.5.8 -> 2.6.0) # Patch should be incremented in case there is a bug fix. (eg: 2.5.8 -> 2.5.9) -__version__ = "5.0.1" +__version__ = "5.0.2"