ibm-cloud-sdk-core 3.19.0__tar.gz → 3.19.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. {ibm-cloud-sdk-core-3.19.0/ibm_cloud_sdk_core.egg-info → ibm-cloud-sdk-core-3.19.2}/PKG-INFO +2 -2
  2. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/README.md +1 -1
  3. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/base_service.py +15 -3
  4. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/token_managers/iam_request_based_token_manager.py +18 -1
  5. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/token_managers/vpc_instance_token_manager.py +18 -1
  6. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/utils.py +13 -6
  7. ibm-cloud-sdk-core-3.19.2/ibm_cloud_sdk_core/version.py +1 -0
  8. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2/ibm_cloud_sdk_core.egg-info}/PKG-INFO +2 -2
  9. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core.egg-info/SOURCES.txt +2 -1
  10. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/requirements-dev.txt +1 -1
  11. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/setup.py +1 -1
  12. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_container_token_manager.py +37 -12
  13. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_iam_token_manager.py +82 -0
  14. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_vpc_instance_token_manager.py +59 -4
  15. ibm-cloud-sdk-core-3.19.2/test_integration/test_ssl_verification.py +62 -0
  16. ibm-cloud-sdk-core-3.19.0/ibm_cloud_sdk_core/version.py +0 -1
  17. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/LICENSE +0 -0
  18. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/MANIFEST.in +0 -0
  19. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/__init__.py +0 -0
  20. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/api_exception.py +0 -0
  21. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/authenticators/__init__.py +0 -0
  22. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/authenticators/authenticator.py +0 -0
  23. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/authenticators/basic_authenticator.py +0 -0
  24. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/authenticators/bearer_token_authenticator.py +0 -0
  25. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/authenticators/container_authenticator.py +0 -0
  26. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/authenticators/cp4d_authenticator.py +0 -0
  27. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/authenticators/iam_authenticator.py +0 -0
  28. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/authenticators/iam_request_based_authenticator.py +0 -0
  29. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/authenticators/mcsp_authenticator.py +0 -0
  30. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/authenticators/no_auth_authenticator.py +0 -0
  31. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/authenticators/vpc_instance_authenticator.py +0 -0
  32. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/detailed_response.py +0 -0
  33. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/get_authenticator.py +0 -0
  34. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/token_managers/__init__.py +0 -0
  35. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/token_managers/container_token_manager.py +0 -0
  36. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/token_managers/cp4d_token_manager.py +0 -0
  37. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/token_managers/iam_token_manager.py +0 -0
  38. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/token_managers/jwt_token_manager.py +0 -0
  39. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/token_managers/mcsp_token_manager.py +0 -0
  40. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core/token_managers/token_manager.py +0 -0
  41. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core.egg-info/dependency_links.txt +0 -0
  42. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core.egg-info/requires.txt +0 -0
  43. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core.egg-info/top_level.txt +0 -0
  44. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/ibm_cloud_sdk_core.egg-info/zip-safe +0 -0
  45. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/pyproject.toml +0 -0
  46. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/requirements.txt +0 -0
  47. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/setup.cfg +0 -0
  48. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/__init__.py +0 -0
  49. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_api_exception.py +0 -0
  50. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_authenticator.py +0 -0
  51. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_base_service.py +0 -0
  52. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_basic_authenticator.py +0 -0
  53. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_bearer_authenticator.py +0 -0
  54. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_container_authenticator.py +0 -0
  55. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_cp4d_authenticator.py +0 -0
  56. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_cp4d_token_manager.py +0 -0
  57. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_detailed_response.py +0 -0
  58. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_iam_authenticator.py +0 -0
  59. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_jwt_token_manager.py +0 -0
  60. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_mcsp_authenticator.py +0 -0
  61. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_mcsp_token_manager.py +0 -0
  62. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_no_auth_authenticator.py +0 -0
  63. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_token_manager.py +0 -0
  64. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_utils.py +0 -0
  65. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test/test_vpc_instance_authenticator.py +0 -0
  66. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test_integration/__init__.py +0 -0
  67. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test_integration/test_cp4d_authenticator_integration.py +0 -0
  68. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test_integration/test_iam_authenticator_integration.py +0 -0
  69. {ibm-cloud-sdk-core-3.19.0 → ibm-cloud-sdk-core-3.19.2}/test_integration/test_mcsp_authenticator_integration.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ibm-cloud-sdk-core
3
- Version: 3.19.0
3
+ Version: 3.19.2
4
4
  Summary: Core library used by SDKs for IBM Cloud Services
5
5
  Home-page: https://github.com/IBM/python-sdk-core
6
6
  Author: IBM
@@ -29,7 +29,7 @@ License-File: LICENSE
29
29
  [![CLA assistant](https://cla-assistant.io/readme/badge/ibm/python-sdk-core)](https://cla-assistant.io/ibm/python-sdk-core)
30
30
  [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
31
31
 
32
- # IBM Python SDK Core Version 3.19.0
32
+ # IBM Python SDK Core Version 3.19.2
33
33
  This project contains core functionality required by Python code generated by the IBM Cloud OpenAPI SDK Generator
34
34
  (openapi-sdkgen).
35
35
 
@@ -4,7 +4,7 @@
4
4
  [![CLA assistant](https://cla-assistant.io/readme/badge/ibm/python-sdk-core)](https://cla-assistant.io/ibm/python-sdk-core)
5
5
  [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
6
6
 
7
- # IBM Python SDK Core Version 3.19.0
7
+ # IBM Python SDK Core Version 3.19.2
8
8
  This project contains core functionality required by Python code generated by the IBM Cloud OpenAPI SDK Generator
9
9
  (openapi-sdkgen).
10
10
 
@@ -108,7 +108,7 @@ class BaseService:
108
108
  self.enable_gzip_compression = enable_gzip_compression
109
109
  self._set_user_agent_header(self._build_user_agent())
110
110
  self.retry_config = None
111
- self.http_adapter = SSLHTTPAdapter()
111
+ self.http_adapter = SSLHTTPAdapter(_disable_ssl_verification=self.disable_ssl_verification)
112
112
  if not self.authenticator:
113
113
  raise ValueError('authenticator must be provided')
114
114
  if not isinstance(self.authenticator, Authenticator):
@@ -138,14 +138,16 @@ class BaseService:
138
138
  # Omitting this will default to all methods except POST
139
139
  allowed_methods=['HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE', 'POST'],
140
140
  )
141
- self.http_adapter = SSLHTTPAdapter(max_retries=self.retry_config)
141
+ self.http_adapter = SSLHTTPAdapter(
142
+ max_retries=self.retry_config, _disable_ssl_verification=self.disable_ssl_verification
143
+ )
142
144
  self.http_client.mount('http://', self.http_adapter)
143
145
  self.http_client.mount('https://', self.http_adapter)
144
146
 
145
147
  def disable_retries(self):
146
148
  """Remove retry config from http_adapter"""
147
149
  self.retry_config = None
148
- self.http_adapter = SSLHTTPAdapter()
150
+ self.http_adapter = SSLHTTPAdapter(_disable_ssl_verification=self.disable_ssl_verification)
149
151
  self.http_client.mount('http://', self.http_adapter)
150
152
  self.http_client.mount('https://', self.http_adapter)
151
153
 
@@ -223,8 +225,18 @@ class BaseService:
223
225
  Keyword Arguments:
224
226
  status: set to true to disable ssl verification (default: {False})
225
227
  """
228
+ if self.disable_ssl_verification == status:
229
+ # Do nothing if the state doesn't change.
230
+ return
231
+
226
232
  self.disable_ssl_verification = status
227
233
 
234
+ self.http_adapter = SSLHTTPAdapter(
235
+ max_retries=self.retry_config, _disable_ssl_verification=self.disable_ssl_verification
236
+ )
237
+ self.http_client.mount('http://', self.http_adapter)
238
+ self.http_client.mount('https://', self.http_adapter)
239
+
228
240
  def set_service_url(self, service_url: str) -> None:
229
241
  """Set the url the service will make HTTP requests too.
230
242
 
@@ -1,6 +1,6 @@
1
1
  # coding: utf-8
2
2
 
3
- # Copyright 2019 IBM All Rights Reserved.
3
+ # Copyright 2019, 2024 IBM All Rights Reserved.
4
4
  #
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
6
6
  # you may not use this file except in compliance with the License.
@@ -63,6 +63,7 @@ class IAMRequestBasedTokenManager(JWTTokenManager):
63
63
 
64
64
  DEFAULT_IAM_URL = 'https://iam.cloud.ibm.com'
65
65
  OPERATION_PATH = "/identity/token"
66
+ IAM_EXPIRATION_WINDOW = 10
66
67
 
67
68
  def __init__(
68
69
  self,
@@ -167,3 +168,19 @@ class IAMRequestBasedTokenManager(JWTTokenManager):
167
168
  value: A space seperated string that makes up the scope parameter.
168
169
  """
169
170
  self.scope = value
171
+
172
+ def _is_token_expired(self) -> bool:
173
+ """
174
+ Returns true iff the current cached token is expired.
175
+ We'll consider an access token as expired when we reach its IAM server-reported expiration time
176
+ minus our expiration window (10 secs).
177
+ We do this to avoid using an access token that might expire in the middle of a long-running transaction
178
+ within an IBM Cloud service.
179
+
180
+ Returns
181
+ -------
182
+ bool
183
+ True if token is expired; False otherwise
184
+ """
185
+ current_time = self._get_current_time()
186
+ return current_time >= (self.expire_time - self.IAM_EXPIRATION_WINDOW)
@@ -1,6 +1,6 @@
1
1
  # coding: utf-8
2
2
 
3
- # Copyright 2021, 2023 IBM All Rights Reserved.
3
+ # Copyright 2021, 2024 IBM All Rights Reserved.
4
4
  #
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
6
6
  # you may not use this file except in compliance with the License.
@@ -55,6 +55,7 @@ class VPCInstanceTokenManager(JWTTokenManager):
55
55
  METADATA_SERVICE_VERSION = '2022-03-01'
56
56
  DEFAULT_IMS_ENDPOINT = 'http://169.254.169.254'
57
57
  TOKEN_NAME = 'access_token'
58
+ IAM_EXPIRATION_WINDOW = 10
58
59
 
59
60
  def __init__(
60
61
  self, iam_profile_crn: Optional[str] = None, iam_profile_id: Optional[str] = None, url: Optional[str] = None
@@ -152,3 +153,19 @@ class VPCInstanceTokenManager(JWTTokenManager):
152
153
  logger.debug('Returned from VPC \'create_access_token\' operation."')
153
154
 
154
155
  return response['access_token']
156
+
157
+ def _is_token_expired(self) -> bool:
158
+ """
159
+ Returns true iff the current cached token is expired.
160
+ We'll consider an access token as expired when we reach its IAM server-reported expiration time
161
+ minus our expiration window (10 secs).
162
+ We do this to avoid using an access token that might expire in the middle of a long-running transaction
163
+ within an IBM Cloud service.
164
+
165
+ Returns
166
+ -------
167
+ bool
168
+ True if token is expired; False otherwise
169
+ """
170
+ current_time = self._get_current_time()
171
+ return current_time >= (self.expire_time - self.IAM_EXPIRATION_WINDOW)
@@ -25,7 +25,7 @@ from os.path import isfile, join, expanduser
25
25
  from typing import List, Union
26
26
  from urllib.parse import urlparse, parse_qs
27
27
 
28
- from requests.adapters import HTTPAdapter
28
+ from requests.adapters import HTTPAdapter, DEFAULT_POOLBLOCK
29
29
  from urllib3.util.ssl_ import create_urllib3_context
30
30
 
31
31
  import dateutil.parser as date_parser
@@ -35,14 +35,21 @@ class SSLHTTPAdapter(HTTPAdapter):
35
35
  """Wraps the original HTTP adapter and adds additional SSL context."""
36
36
 
37
37
  def __init__(self, *args, **kwargs):
38
+ self._disable_ssl_verification = kwargs.pop('_disable_ssl_verification', None)
39
+
38
40
  super().__init__(*args, **kwargs)
39
41
 
40
- # pylint: disable=arguments-differ
41
- def init_poolmanager(self, connections, maxsize, block):
42
- """Extends the parent's method by adding minimum SSL version to the args."""
42
+ def init_poolmanager(self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs):
43
+ """Create and use custom SSL configuration."""
44
+
43
45
  ssl_context = create_urllib3_context()
44
46
  ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
45
- super().init_poolmanager(connections, maxsize, block, ssl_context=ssl_context)
47
+
48
+ if self._disable_ssl_verification:
49
+ ssl_context.check_hostname = False
50
+ ssl_context.verify_mode = ssl.CERT_NONE
51
+
52
+ super().init_poolmanager(connections, maxsize, block, ssl_context=ssl_context, **pool_kwargs)
46
53
 
47
54
 
48
55
  class GzipStream(io.RawIOBase):
@@ -60,7 +67,7 @@ class GzipStream(io.RawIOBase):
60
67
  It can be a file-like object, bytes or string.
61
68
  """
62
69
 
63
- def __init__(self, source: Union[io.IOBase, bytes, str]) -> 'GzipStream':
70
+ def __init__(self, source: Union[io.IOBase, bytes, str]):
64
71
  self.buffer = b''
65
72
 
66
73
  if isinstance(source, io.IOBase):
@@ -0,0 +1 @@
1
+ __version__ = '3.19.2'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ibm-cloud-sdk-core
3
- Version: 3.19.0
3
+ Version: 3.19.2
4
4
  Summary: Core library used by SDKs for IBM Cloud Services
5
5
  Home-page: https://github.com/IBM/python-sdk-core
6
6
  Author: IBM
@@ -29,7 +29,7 @@ License-File: LICENSE
29
29
  [![CLA assistant](https://cla-assistant.io/readme/badge/ibm/python-sdk-core)](https://cla-assistant.io/ibm/python-sdk-core)
30
30
  [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
31
31
 
32
- # IBM Python SDK Core Version 3.19.0
32
+ # IBM Python SDK Core Version 3.19.2
33
33
  This project contains core functionality required by Python code generated by the IBM Cloud OpenAPI SDK Generator
34
34
  (openapi-sdkgen).
35
35
 
@@ -62,4 +62,5 @@ test/test_vpc_instance_token_manager.py
62
62
  test_integration/__init__.py
63
63
  test_integration/test_cp4d_authenticator_integration.py
64
64
  test_integration/test_iam_authenticator_integration.py
65
- test_integration/test_mcsp_authenticator_integration.py
65
+ test_integration/test_mcsp_authenticator_integration.py
66
+ test_integration/test_ssl_verification.py
@@ -3,4 +3,4 @@ pylint>=3.0.0,<4.0.0
3
3
  pytest>=7.4.2,<8.0.0
4
4
  pytest-cov>=4.1.0,<5.0.0
5
5
  responses>=0.23.3,<1.0.0
6
- black>=23.9.1
6
+ black>=24.0.0,<25.0.0
@@ -19,7 +19,7 @@ import pkg_resources
19
19
  from setuptools import setup, find_packages
20
20
  from setuptools.command.test import test as TestCommand
21
21
 
22
- __version__ = '3.19.0'
22
+ __version__ = '3.19.2'
23
23
 
24
24
  if sys.argv[-1] == 'publish':
25
25
  # test server
@@ -1,3 +1,19 @@
1
+ # coding: utf-8
2
+
3
+ # Copyright 2021, 2024 IBM All Rights Reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
1
17
  # pylint: disable=missing-docstring
2
18
  import json
3
19
  import os
@@ -17,6 +33,7 @@ TEST_REFRESH_TOKEN = 'Xj7Gle500MachEOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI
17
33
  MOCK_IAM_PROFILE_NAME = 'iam-user-123'
18
34
  MOCK_CLIENT_ID = 'client-id-1'
19
35
  MOCK_CLIENT_SECRET = 'client-secret-1'
36
+ EXPIRATION_WINDOW = 10
20
37
 
21
38
  cr_token_file = os.path.join(os.path.dirname(__file__), '../resources/cr-token.txt')
22
39
 
@@ -169,18 +186,22 @@ def test_get_token_success():
169
186
  assert access_token == TEST_ACCESS_TOKEN_1
170
187
  assert token_manager.access_token == TEST_ACCESS_TOKEN_1
171
188
 
172
- # Verify the token manager return the cached value.
173
- # Before we call the `get_token` again, set the expiration and time.
174
- # This is necessary because we are using a fix JWT response.
175
- token_manager.expire_time = _get_current_time() + 3600
176
- token_manager.refresh_time = _get_current_time() + 3600
189
+ # Verify that the token manager returns the cached value.
190
+ # Before we call `get_token` again, set the expiration and refresh time
191
+ # so that we do not fetch a new access token.
192
+ # This is necessary because we are using a fixed JWT response.
193
+ token_manager.expire_time = _get_current_time() + 1000
194
+ token_manager.refresh_time = _get_current_time() + 1000
177
195
  token_manager.set_scope('send-second-token')
178
196
  access_token = token_manager.get_token()
179
197
  assert access_token == TEST_ACCESS_TOKEN_1
180
198
  assert token_manager.access_token == TEST_ACCESS_TOKEN_1
181
199
 
182
200
  # Force expiration to get the second token.
183
- token_manager.expire_time = _get_current_time() - 1
201
+ # We'll set the expiration time to be current-time + EXPIRATION_WINDOW (10 secs)
202
+ # because we want the access token to be considered as "expired"
203
+ # when we reach the IAM-server reported expiration time minus 10 secs.
204
+ token_manager.expire_time = _get_current_time() + EXPIRATION_WINDOW
184
205
  access_token = token_manager.get_token()
185
206
  assert access_token == TEST_ACCESS_TOKEN_2
186
207
  assert token_manager.access_token == TEST_ACCESS_TOKEN_2
@@ -206,17 +227,21 @@ def test_authenticate_success():
206
227
  authenticator.authenticate(request)
207
228
  assert request['headers']['Authorization'] == 'Bearer ' + TEST_ACCESS_TOKEN_1
208
229
 
209
- # Verify the token manager return the cached value.
210
- # Before we call the `get_token` again, set the expiration and time.
211
- # This is necessary because we are using a fix JWT response.
212
- authenticator.token_manager.expire_time = _get_current_time() + 3600
213
- authenticator.token_manager.refresh_time = _get_current_time() + 3600
230
+ # Verify that the token manager returns the cached value.
231
+ # Before we call `get_token` again, set the expiration and refresh time
232
+ # so that we do not fetch a new access token.
233
+ # This is necessary because we are using a fixed JWT response.
234
+ authenticator.token_manager.expire_time = _get_current_time() + 1000
235
+ authenticator.token_manager.refresh_time = _get_current_time() + 1000
214
236
  authenticator.token_manager.set_scope('send-second-token')
215
237
  authenticator.authenticate(request)
216
238
  assert request['headers']['Authorization'] == 'Bearer ' + TEST_ACCESS_TOKEN_1
217
239
 
218
240
  # Force expiration to get the second token.
219
- authenticator.token_manager.expire_time = _get_current_time() - 1
241
+ # We'll set the expiration time to be current-time + EXPIRATION_WINDOW (10 secs)
242
+ # because we want the access token to be considered as "expired"
243
+ # when we reach the IAM-server reported expiration time minus 10 secs.
244
+ authenticator.token_manager.expire_time = _get_current_time() + EXPIRATION_WINDOW
220
245
  authenticator.authenticate(request)
221
246
  assert request['headers']['Authorization'] == 'Bearer ' + TEST_ACCESS_TOKEN_2
222
247
 
@@ -1,3 +1,19 @@
1
+ # coding: utf-8
2
+
3
+ # Copyright 2021, 2024 IBM All Rights Reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
1
17
  # pylint: disable=missing-docstring
2
18
  import os
3
19
  import time
@@ -8,6 +24,16 @@ import responses
8
24
 
9
25
  from ibm_cloud_sdk_core import IAMTokenManager, ApiException, get_authenticator_from_environment
10
26
 
27
+ # pylint: disable=line-too-long
28
+ TEST_ACCESS_TOKEN_1 = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImhlbGxvIiwicm9sZSI6InVzZXIiLCJwZXJtaXNzaW9ucyI6WyJhZG1pbmlzdHJhdG9yIiwiZGVwbG95bWVudF9hZG1pbiJdLCJzdWIiOiJoZWxsbyIsImlzcyI6IkpvaG4iLCJhdWQiOiJEU1giLCJ1aWQiOiI5OTkiLCJpYXQiOjE1NjAyNzcwNTEsImV4cCI6MTU2MDI4MTgxOSwianRpIjoiMDRkMjBiMjUtZWUyZC00MDBmLTg2MjMtOGNkODA3MGI1NDY4In0.cIodB4I6CCcX8vfIImz7Cytux3GpWyObt9Gkur5g1QI'
29
+ TEST_ACCESS_TOKEN_2 = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjIzMDQ5ODE1MWMyMTRiNzg4ZGQ5N2YyMmI4NTQxMGE1In0.eyJ1c2VybmFtZSI6ImR1bW15Iiwicm9sZSI6IkFkbWluIiwicGVybWlzc2lvbnMiOlsiYWRtaW5pc3RyYXRvciIsIm1hbmFnZV9jYXRhbG9nIl0sInN1YiI6ImFkbWluIiwiaXNzIjoic3NzIiwiYXVkIjoic3NzIiwidWlkIjoic3NzIiwiaWF0IjozNjAwLCJleHAiOjE2MjgwMDcwODF9.zvUDpgqWIWs7S1CuKv40ERw1IZ5FqSFqQXsrwZJyfRM'
30
+ TEST_REFRESH_TOKEN = 'Xj7Gle500MachEOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImhlbGxvIiwicm9sZSI6InVzZXIiLCJwZXJtaXNzaW9ucyI6WyJhZG1pbmlzdHJhdG9yIiwiZGVwbG95bWVudF9hZG1pbiJdLCJzdWIiOiJoZWxsbyIsImlzcyI6IkpvaG4iLCJhdWQiOiJEU1giLCJ1aWQiOiI5OTkiLCJpYXQiOjE1NjAyNzcwNTEsImV4cCI6MTU2MDI4MTgxOSwianRpIjoiMDRkMjBiMjUtZWUyZC00MDBmLTg2MjMtOGNkODA3MGI1NDY4In0.cIodB4I6CCcX8vfIImz7Cytux3GpWyObt9Gkur5g1QI'
31
+ EXPIRATION_WINDOW = 10
32
+
33
+
34
+ def _get_current_time() -> int:
35
+ return int(time.time())
36
+
11
37
 
12
38
  def get_access_token() -> str:
13
39
  access_token_layout = {
@@ -268,6 +294,62 @@ def test_request_token_auth_in_setter_scope():
268
294
  assert 'scope=john+snow' in responses.calls[0].response.request.body
269
295
 
270
296
 
297
+ @responses.activate
298
+ def test_get_token_success():
299
+ iam_url = "https://iam.cloud.ibm.com/identity/token"
300
+
301
+ # Create two mock responses with different access tokens.
302
+ response1 = """{
303
+ "access_token": "%s",
304
+ "token_type": "Bearer",
305
+ "expires_in": 3600,
306
+ "expiration": 1600003600,
307
+ "refresh_token": "jy4gl91BQ"
308
+ }""" % (
309
+ TEST_ACCESS_TOKEN_1
310
+ )
311
+ response2 = """{
312
+ "access_token": "%s",
313
+ "token_type": "Bearer",
314
+ "expires_in": 3600,
315
+ "expiration": 1600007200,
316
+ "refresh_token": "jy4gl91BQ"
317
+ }""" % (
318
+ TEST_ACCESS_TOKEN_2
319
+ )
320
+
321
+ token_manager = IAMTokenManager("iam_apikey")
322
+
323
+ access_token = token_manager.access_token
324
+ assert access_token is None
325
+
326
+ responses.add(responses.POST, url=iam_url, body=response1, status=200)
327
+ access_token = token_manager.get_token()
328
+ assert access_token == TEST_ACCESS_TOKEN_1
329
+ assert token_manager.access_token == TEST_ACCESS_TOKEN_1
330
+
331
+ # Verify that the token manager returns the cached value.
332
+ # Before we call `get_token` again, set the expiration and refresh time
333
+ # so that we do not fetch a new access token.
334
+ # This is necessary because we are using a fixed JWT response.
335
+ token_manager.expire_time = _get_current_time() + 1000
336
+ token_manager.refresh_time = _get_current_time() + 1000
337
+ access_token = token_manager.get_token()
338
+ assert access_token == TEST_ACCESS_TOKEN_1
339
+ assert token_manager.access_token == TEST_ACCESS_TOKEN_1
340
+
341
+ # Force expiration to get the second token.
342
+ # We'll set the expiration time to be current-time + EXPIRATION_WINDOW (10 secs)
343
+ # because we want the access token to be considered as "expired"
344
+ # when we reach the IAM-server reported expiration time minus 10 secs.
345
+ responses.add(responses.POST, url=iam_url, body=response2, status=200)
346
+ token_manager.expire_time = _get_current_time() + EXPIRATION_WINDOW
347
+ token_manager.refresh_time = _get_current_time() + 1000
348
+ access_token = token_manager.get_token()
349
+ assert access_token == TEST_ACCESS_TOKEN_2
350
+ assert token_manager.access_token == TEST_ACCESS_TOKEN_2
351
+
352
+
271
353
  @responses.activate
272
354
  def test_get_refresh_token():
273
355
  iam_url = "https://iam.cloud.ibm.com/identity/token"
@@ -1,6 +1,6 @@
1
1
  # coding: utf-8
2
2
 
3
- # Copyright 2021 IBM All Rights Reserved.
3
+ # Copyright 2021, 2024 IBM All Rights Reserved.
4
4
  #
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
6
6
  # you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
17
17
  # pylint: disable=missing-docstring
18
18
  import json
19
19
  import logging
20
+ import time
20
21
 
21
22
  import pytest
22
23
  import responses
@@ -25,11 +26,17 @@ from ibm_cloud_sdk_core import ApiException, VPCInstanceTokenManager
25
26
 
26
27
 
27
28
  # pylint: disable=line-too-long
28
- TEST_ACCESS_TOKEN = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImhlbGxvIiwicm9sZSI6InVzZXIiLCJwZXJtaXNzaW9ucyI6WyJhZG1pbmlzdHJhdG9yIiwiZGVwbG95bWVudF9hZG1pbiJdLCJzdWIiOiJoZWxsbyIsImlzcyI6IkpvaG4iLCJhdWQiOiJEU1giLCJ1aWQiOiI5OTkiLCJpYXQiOjE1NjAyNzcwNTEsImV4cCI6MTU2MDI4MTgxOSwianRpIjoiMDRkMjBiMjUtZWUyZC00MDBmLTg2MjMtOGNkODA3MGI1NDY4In0.cIodB4I6CCcX8vfIImz7Cytux3GpWyObt9Gkur5g1QI'
29
+ TEST_ACCESS_TOKEN_1 = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImhlbGxvIiwicm9sZSI6InVzZXIiLCJwZXJtaXNzaW9ucyI6WyJhZG1pbmlzdHJhdG9yIiwiZGVwbG95bWVudF9hZG1pbiJdLCJzdWIiOiJoZWxsbyIsImlzcyI6IkpvaG4iLCJhdWQiOiJEU1giLCJ1aWQiOiI5OTkiLCJpYXQiOjE1NjAyNzcwNTEsImV4cCI6MTU2MDI4MTgxOSwianRpIjoiMDRkMjBiMjUtZWUyZC00MDBmLTg2MjMtOGNkODA3MGI1NDY4In0.cIodB4I6CCcX8vfIImz7Cytux3GpWyObt9Gkur5g1QI'
30
+ TEST_ACCESS_TOKEN_2 = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjIzMDQ5ODE1MWMyMTRiNzg4ZGQ5N2YyMmI4NTQxMGE1In0.eyJ1c2VybmFtZSI6ImR1bW15Iiwicm9sZSI6IkFkbWluIiwicGVybWlzc2lvbnMiOlsiYWRtaW5pc3RyYXRvciIsIm1hbmFnZV9jYXRhbG9nIl0sInN1YiI6ImFkbWluIiwiaXNzIjoic3NzIiwiYXVkIjoic3NzIiwidWlkIjoic3NzIiwiaWF0IjozNjAwLCJleHAiOjE2MjgwMDcwODF9.zvUDpgqWIWs7S1CuKv40ERw1IZ5FqSFqQXsrwZJyfRM'
29
31
  TEST_TOKEN = 'abc123'
30
32
  TEST_IAM_TOKEN = 'iam-abc123'
31
33
  TEST_IAM_PROFILE_CRN = 'crn:iam-profile:123'
32
34
  TEST_IAM_PROFILE_ID = 'iam-id-123'
35
+ EXPIRATION_WINDOW = 10
36
+
37
+
38
+ def _get_current_time() -> int:
39
+ return int(time.time())
33
40
 
34
41
 
35
42
  def test_constructor():
@@ -272,7 +279,7 @@ def test_access_token():
272
279
  'access_token': TEST_TOKEN,
273
280
  }
274
281
  response_iam = {
275
- 'access_token': TEST_ACCESS_TOKEN,
282
+ 'access_token': TEST_ACCESS_TOKEN_1,
276
283
  }
277
284
 
278
285
  responses.add(
@@ -290,6 +297,54 @@ def test_access_token():
290
297
  assert token_manager.refresh_time == 0
291
298
 
292
299
  token_manager.get_token()
293
- assert token_manager.access_token == TEST_ACCESS_TOKEN
300
+ assert token_manager.access_token == TEST_ACCESS_TOKEN_1
294
301
  assert token_manager.expire_time > 0
295
302
  assert token_manager.refresh_time > 0
303
+
304
+
305
+ @responses.activate
306
+ def test_get_token_success():
307
+ token_manager = VPCInstanceTokenManager()
308
+
309
+ # Mock the retrieve instance identity token method.
310
+ def mock_retrieve_instance_identity_token():
311
+ return TEST_TOKEN
312
+
313
+ token_manager.retrieve_instance_identity_token = mock_retrieve_instance_identity_token
314
+
315
+ response1 = {
316
+ 'access_token': TEST_ACCESS_TOKEN_1,
317
+ }
318
+ response2 = {
319
+ 'access_token': TEST_ACCESS_TOKEN_2,
320
+ }
321
+
322
+ responses.add(
323
+ responses.POST, 'http://169.254.169.254/instance_identity/v1/iam_token', body=json.dumps(response1), status=200
324
+ )
325
+
326
+ access_token = token_manager.get_token()
327
+ assert access_token == TEST_ACCESS_TOKEN_1
328
+ assert token_manager.access_token == TEST_ACCESS_TOKEN_1
329
+
330
+ # Verify that the token manager returns the cached value.
331
+ # Before we call `get_token` again, set the expiration and refresh time
332
+ # so that we do not fetch a new access token.
333
+ # This is necessary because we are using a fixed JWT response.
334
+ token_manager.expire_time = _get_current_time() + 1000
335
+ token_manager.refresh_time = _get_current_time() + 1000
336
+ access_token = token_manager.get_token()
337
+ assert access_token == TEST_ACCESS_TOKEN_1
338
+ assert token_manager.access_token == TEST_ACCESS_TOKEN_1
339
+
340
+ # Force expiration to get the second token.
341
+ # We'll set the expiration time to be current-time + EXPIRATION_WINDOW (10 secs)
342
+ # because we want the access token to be considered as "expired"
343
+ # when we reach the IAM-server reported expiration time minus 10 secs.
344
+ responses.add(
345
+ responses.POST, 'http://169.254.169.254/instance_identity/v1/iam_token', body=json.dumps(response2), status=200
346
+ )
347
+ token_manager.expire_time = _get_current_time() + EXPIRATION_WINDOW
348
+ access_token = token_manager.get_token()
349
+ assert access_token == TEST_ACCESS_TOKEN_2
350
+ assert token_manager.access_token == TEST_ACCESS_TOKEN_2
@@ -0,0 +1,62 @@
1
+ # pylint: disable=missing-docstring
2
+ import os
3
+ import threading
4
+ from http.server import HTTPServer, SimpleHTTPRequestHandler
5
+ from ssl import PROTOCOL_TLS_SERVER, SSLContext
6
+
7
+ import pytest
8
+ from requests.exceptions import SSLError
9
+
10
+ from ibm_cloud_sdk_core.base_service import BaseService
11
+ from ibm_cloud_sdk_core.authenticators import NoAuthAuthenticator
12
+
13
+
14
+ # The certificate files that are used in this tests are generated by this command:
15
+ # openssl req \
16
+ # -new \
17
+ # -newkey rsa:4096 \
18
+ # -days 36500 \
19
+ # -nodes \
20
+ # -x509 \
21
+ # -subj "/C=US/CN=localhost" \
22
+ # -keyout test_ssl.key \
23
+ # -out test_ssl.cert
24
+
25
+
26
+ def test_ssl_verification():
27
+ # Load the certificate and the key files.
28
+ cert = os.path.join(os.path.dirname(__file__), '../resources/test_ssl.cert')
29
+ key = os.path.join(os.path.dirname(__file__), '../resources/test_ssl.key')
30
+
31
+ # Build the SSL context for the server.
32
+ ssl_context = SSLContext(PROTOCOL_TLS_SERVER)
33
+ ssl_context.load_cert_chain(certfile=cert, keyfile=key)
34
+
35
+ # Create and start the server on a separate thread.
36
+ server = HTTPServer(('127.0.0.1', 3333), SimpleHTTPRequestHandler)
37
+ server.socket = ssl_context.wrap_socket(server.socket, server_side=True)
38
+ t = threading.Thread(target=server.serve_forever)
39
+ t.start()
40
+
41
+ # We run everything in a big try-except-finally block to make sure we always
42
+ # shutdown the HTTP server gracefully.
43
+ try:
44
+ service = BaseService(service_url='https://127.0.0.1:3333', authenticator=NoAuthAuthenticator())
45
+ #
46
+ # First call the server with the default configuration.
47
+ # It should fail due to the invalid SSL cert.
48
+ assert service.disable_ssl_verification is False
49
+ prepped = service.prepare_request('GET', url='/')
50
+ with pytest.raises(SSLError):
51
+ res = service.send(prepped)
52
+
53
+ # Now disable the SSL verification. The request shouldn't raise any issue.
54
+ service.set_disable_ssl_verification(True)
55
+ assert service.disable_ssl_verification is True
56
+ prepped = service.prepare_request('GET', url='/')
57
+ res = service.send(prepped)
58
+ assert res is not None
59
+ except Exception: # pylint: disable=try-except-raise
60
+ raise
61
+ finally:
62
+ server.shutdown()
@@ -1 +0,0 @@
1
- __version__ = '3.19.0'