ibm-cloud-sdk-core 3.16.0__py3-none-any.whl → 3.21.0__py3-none-any.whl

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 (58) hide show
  1. ibm_cloud_sdk_core/__init__.py +1 -0
  2. ibm_cloud_sdk_core/api_exception.py +18 -4
  3. ibm_cloud_sdk_core/authenticators/__init__.py +1 -0
  4. ibm_cloud_sdk_core/authenticators/authenticator.py +2 -1
  5. ibm_cloud_sdk_core/authenticators/basic_authenticator.py +12 -7
  6. ibm_cloud_sdk_core/authenticators/bearer_token_authenticator.py +7 -2
  7. ibm_cloud_sdk_core/authenticators/container_authenticator.py +25 -16
  8. ibm_cloud_sdk_core/authenticators/cp4d_authenticator.py +38 -23
  9. ibm_cloud_sdk_core/authenticators/iam_authenticator.py +22 -13
  10. ibm_cloud_sdk_core/authenticators/iam_request_based_authenticator.py +10 -8
  11. ibm_cloud_sdk_core/authenticators/mcsp_authenticator.py +134 -0
  12. ibm_cloud_sdk_core/authenticators/vpc_instance_authenticator.py +12 -11
  13. ibm_cloud_sdk_core/base_service.py +137 -103
  14. ibm_cloud_sdk_core/detailed_response.py +21 -15
  15. ibm_cloud_sdk_core/get_authenticator.py +35 -17
  16. ibm_cloud_sdk_core/http_adapter.py +28 -0
  17. ibm_cloud_sdk_core/logger.py +85 -0
  18. ibm_cloud_sdk_core/private_helpers.py +34 -0
  19. ibm_cloud_sdk_core/token_managers/container_token_manager.py +63 -33
  20. ibm_cloud_sdk_core/token_managers/cp4d_token_manager.py +35 -22
  21. ibm_cloud_sdk_core/token_managers/iam_request_based_token_manager.py +50 -21
  22. ibm_cloud_sdk_core/token_managers/iam_token_manager.py +24 -13
  23. ibm_cloud_sdk_core/token_managers/jwt_token_manager.py +3 -16
  24. ibm_cloud_sdk_core/token_managers/mcsp_token_manager.py +108 -0
  25. ibm_cloud_sdk_core/token_managers/token_manager.py +20 -23
  26. ibm_cloud_sdk_core/token_managers/vpc_instance_token_manager.py +37 -18
  27. ibm_cloud_sdk_core/utils.py +127 -48
  28. ibm_cloud_sdk_core/version.py +1 -1
  29. ibm_cloud_sdk_core-3.21.0.dist-info/METADATA +159 -0
  30. ibm_cloud_sdk_core-3.21.0.dist-info/RECORD +35 -0
  31. {ibm_cloud_sdk_core-3.16.0.dist-info → ibm_cloud_sdk_core-3.21.0.dist-info}/WHEEL +1 -1
  32. ibm_cloud_sdk_core-3.21.0.dist-info/top_level.txt +1 -0
  33. ibm_cloud_sdk_core-3.16.0.dist-info/METADATA +0 -112
  34. ibm_cloud_sdk_core-3.16.0.dist-info/RECORD +0 -52
  35. ibm_cloud_sdk_core-3.16.0.dist-info/top_level.txt +0 -3
  36. ibm_cloud_sdk_core-3.16.0.dist-info/zip-safe +0 -1
  37. test/__init__.py +0 -0
  38. test/test_api_exception.py +0 -73
  39. test/test_authenticator.py +0 -21
  40. test/test_base_service.py +0 -933
  41. test/test_basic_authenticator.py +0 -36
  42. test/test_bearer_authenticator.py +0 -28
  43. test/test_container_authenticator.py +0 -105
  44. test/test_container_token_manager.py +0 -283
  45. test/test_cp4d_authenticator.py +0 -171
  46. test/test_cp4d_token_manager.py +0 -56
  47. test/test_detailed_response.py +0 -57
  48. test/test_iam_authenticator.py +0 -157
  49. test/test_iam_token_manager.py +0 -362
  50. test/test_jwt_token_manager.py +0 -109
  51. test/test_no_auth_authenticator.py +0 -15
  52. test/test_token_manager.py +0 -84
  53. test/test_utils.py +0 -634
  54. test/test_vpc_instance_authenticator.py +0 -66
  55. test/test_vpc_instance_token_manager.py +0 -266
  56. test_integration/__init__.py +0 -0
  57. test_integration/test_cp4d_authenticator_integration.py +0 -45
  58. {ibm_cloud_sdk_core-3.16.0.dist-info → ibm_cloud_sdk_core-3.21.0.dist-info}/LICENSE +0 -0
@@ -43,6 +43,7 @@ from .token_managers.jwt_token_manager import JWTTokenManager
43
43
  from .token_managers.cp4d_token_manager import CP4DTokenManager
44
44
  from .token_managers.container_token_manager import ContainerTokenManager
45
45
  from .token_managers.vpc_instance_token_manager import VPCInstanceTokenManager
46
+ from .token_managers.mcsp_token_manager import MCSPTokenManager
46
47
  from .api_exception import ApiException
47
48
  from .utils import datetime_to_string, string_to_datetime, read_external_sources
48
49
  from .utils import datetime_to_string_list, string_to_datetime_list
@@ -14,6 +14,7 @@
14
14
  # See the License for the specific language governing permissions and
15
15
  # limitations under the License.
16
16
 
17
+ import warnings
17
18
  from http import HTTPStatus
18
19
  from typing import Optional
19
20
 
@@ -39,24 +40,37 @@ class ApiException(Exception):
39
40
  # Call the base class constructor with the parameters it needs
40
41
  super().__init__(message)
41
42
  self.message = message
42
- self.code = code
43
+ self.status_code = code
43
44
  self.http_response = http_response
44
45
  self.global_transaction_id = None
45
46
  if http_response is not None:
46
47
  self.global_transaction_id = http_response.headers.get('X-Global-Transaction-ID')
47
48
  self.message = self.message if self.message else self._get_error_message(http_response)
48
49
 
50
+ # pylint: disable=fixme
51
+ # TODO: delete this by the end of 2024.
52
+ @property
53
+ def code(self):
54
+ """The old `code` property with a deprecation warning."""
55
+
56
+ warnings.warn(
57
+ 'Using the `code` attribute on the `ApiException` is deprecated and '
58
+ 'will be removed in the future. Use `status_code` instead.',
59
+ DeprecationWarning,
60
+ )
61
+ return self.status_code
62
+
49
63
  def __str__(self) -> str:
50
- msg = 'Error: ' + str(self.message) + ', Code: ' + str(self.code)
64
+ msg = 'Error: ' + str(self.message) + ', Status code: ' + str(self.status_code)
51
65
  if self.global_transaction_id is not None:
52
66
  msg += ' , X-global-transaction-id: ' + str(self.global_transaction_id)
53
- return msg
67
+ return msg
54
68
 
55
69
  @staticmethod
56
70
  def _get_error_message(response: Response) -> str:
57
71
  error_message = 'Unknown error'
58
72
  try:
59
- error_json = response.json()
73
+ error_json = response.json(strict=False)
60
74
  if 'errors' in error_json:
61
75
  if isinstance(error_json['errors'], list):
62
76
  err = error_json['errors'][0]
@@ -41,3 +41,4 @@ from .cp4d_authenticator import CloudPakForDataAuthenticator
41
41
  from .iam_authenticator import IAMAuthenticator
42
42
  from .vpc_instance_authenticator import VPCInstanceAuthenticator
43
43
  from .no_auth_authenticator import NoAuthAuthenticator
44
+ from .mcsp_authenticator import MCSPAuthenticator
@@ -1,6 +1,6 @@
1
1
  # coding: utf-8
2
2
 
3
- # Copyright 2019 IBM All Rights Reserved.
3
+ # Copyright 2019, 2023 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.
@@ -28,6 +28,7 @@ class Authenticator(ABC):
28
28
  AUTHTYPE_CP4D = 'cp4d'
29
29
  AUTHTYPE_VPC = 'vpc'
30
30
  AUTHTYPE_NOAUTH = 'noAuth'
31
+ AUTHTYPE_MCSP = 'mcsp'
31
32
  AUTHTYPE_UNKNOWN = 'unknown'
32
33
 
33
34
  @abstractmethod
@@ -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.
@@ -15,11 +15,15 @@
15
15
  # limitations under the License.
16
16
 
17
17
  import base64
18
+
18
19
  from requests import Request
19
20
 
21
+ from ibm_cloud_sdk_core.logger import get_logger
20
22
  from .authenticator import Authenticator
21
23
  from ..utils import has_bad_first_or_last_char
22
24
 
25
+ logger = get_logger()
26
+
23
27
 
24
28
  class BasicAuthenticator(Authenticator):
25
29
  """The BasicAuthenticator is used to add basic authentication information to requests.
@@ -41,6 +45,7 @@ class BasicAuthenticator(Authenticator):
41
45
  self.password = password
42
46
  self.validate()
43
47
  self.authorization_header = self.__construct_basic_auth_header()
48
+ logger.debug('Created new BasicAuthenticator instance!')
44
49
 
45
50
  def authentication_type(self) -> str:
46
51
  """Returns this authenticator's type ('basic')."""
@@ -57,16 +62,15 @@ class BasicAuthenticator(Authenticator):
57
62
  if self.username is None or self.password is None:
58
63
  raise ValueError('The username and password shouldn\'t be None.')
59
64
 
60
- if has_bad_first_or_last_char(
61
- self.username) or has_bad_first_or_last_char(self.password):
65
+ if has_bad_first_or_last_char(self.username) or has_bad_first_or_last_char(self.password):
62
66
  raise ValueError(
63
67
  'The username and password shouldn\'t start or end with curly brackets or quotes. '
64
- 'Please remove any surrounding {, }, or \" characters.')
68
+ 'Please remove any surrounding {, }, or \" characters.'
69
+ )
65
70
 
66
71
  def __construct_basic_auth_header(self) -> str:
67
72
  authstring = "{0}:{1}".format(self.username, self.password)
68
- base64_authorization = base64.b64encode(
69
- authstring.encode('utf-8')).decode('utf-8')
73
+ base64_authorization = base64.b64encode(authstring.encode('utf-8')).decode('utf-8')
70
74
  return 'Basic {0}'.format(base64_authorization)
71
75
 
72
76
  def authenticate(self, req: Request) -> None:
@@ -77,8 +81,9 @@ class BasicAuthenticator(Authenticator):
77
81
  Authorization: Basic <encoded username and password>
78
82
 
79
83
  Args:
80
- req: The request to add basic auth information too. Must contain a key to a dictionary
84
+ req: The request to add basic auth information to. Must contain a key to a dictionary
81
85
  called headers.
82
86
  """
83
87
  headers = req.get('headers')
84
88
  headers['Authorization'] = self.authorization_header
89
+ logger.debug('Authenticated outbound request (type=%s)', self.authentication_type())
@@ -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.
@@ -16,8 +16,11 @@
16
16
 
17
17
  from requests import Request
18
18
 
19
+ from ibm_cloud_sdk_core.logger import get_logger
19
20
  from .authenticator import Authenticator
20
21
 
22
+ logger = get_logger()
23
+
21
24
 
22
25
  class BearerTokenAuthenticator(Authenticator):
23
26
  """The BearerTokenAuthenticator will add a user-supplied bearer token
@@ -37,6 +40,7 @@ class BearerTokenAuthenticator(Authenticator):
37
40
  def __init__(self, bearer_token: str) -> None:
38
41
  self.bearer_token = bearer_token
39
42
  self.validate()
43
+ logger.debug('Created BearerTokenAuthenticator instance!')
40
44
 
41
45
  def authentication_type(self) -> str:
42
46
  """Returns this authenticator's type ('bearertoken')."""
@@ -61,11 +65,12 @@ class BearerTokenAuthenticator(Authenticator):
61
65
  Authorization: Bearer <bearer-token>
62
66
 
63
67
  Args:
64
- req: The request to add bearer authentication information too. Must contain a key to a dictionary
68
+ req: The request to add bearer authentication information to. Must contain a key to a dictionary
65
69
  called headers.
66
70
  """
67
71
  headers = req.get('headers')
68
72
  headers['Authorization'] = 'Bearer {0}'.format(self.bearer_token)
73
+ logger.debug('Authenticated outbound request (type=%s)', self.authentication_type())
69
74
 
70
75
  def set_bearer_token(self, bearer_token: str) -> None:
71
76
  """Set a new bearer token to be sent in subsequent service operations.
@@ -64,25 +64,35 @@ class ContainerAuthenticator(IAMRequestBasedAuthenticator):
64
64
  or client_id, and/or client_secret are not valid for IAM token requests.
65
65
  """
66
66
 
67
- def __init__(self,
68
- cr_token_filename: Optional[str] = None,
69
- iam_profile_name: Optional[str] = None,
70
- iam_profile_id: Optional[str] = None,
71
- url: Optional[str] = None,
72
- client_id: Optional[str] = None,
73
- client_secret: Optional[str] = None,
74
- disable_ssl_verification: bool = False,
75
- scope: Optional[str] = None,
76
- proxies: Optional[Dict[str, str]] = None,
77
- headers: Optional[Dict[str, str]] = None) -> None:
67
+ def __init__(
68
+ self,
69
+ cr_token_filename: Optional[str] = None,
70
+ iam_profile_name: Optional[str] = None,
71
+ iam_profile_id: Optional[str] = None,
72
+ url: Optional[str] = None,
73
+ client_id: Optional[str] = None,
74
+ client_secret: Optional[str] = None,
75
+ disable_ssl_verification: bool = False,
76
+ scope: Optional[str] = None,
77
+ proxies: Optional[Dict[str, str]] = None,
78
+ headers: Optional[Dict[str, str]] = None,
79
+ ) -> None:
78
80
  # Check the type of `disable_ssl_verification`. Must be a bool.
79
81
  if not isinstance(disable_ssl_verification, bool):
80
82
  raise TypeError('disable_ssl_verification must be a bool')
81
83
 
82
84
  self.token_manager = ContainerTokenManager(
83
- cr_token_filename=cr_token_filename, iam_profile_name=iam_profile_name, iam_profile_id=iam_profile_id,
84
- url=url, client_id=client_id, client_secret=client_secret,
85
- disable_ssl_verification=disable_ssl_verification, scope=scope, proxies=proxies, headers=headers)
85
+ cr_token_filename=cr_token_filename,
86
+ iam_profile_name=iam_profile_name,
87
+ iam_profile_id=iam_profile_id,
88
+ url=url,
89
+ client_id=client_id,
90
+ client_secret=client_secret,
91
+ disable_ssl_verification=disable_ssl_verification,
92
+ scope=scope,
93
+ proxies=proxies,
94
+ headers=headers,
95
+ )
86
96
 
87
97
  self.validate()
88
98
 
@@ -103,8 +113,7 @@ class ContainerAuthenticator(IAMRequestBasedAuthenticator):
103
113
  super().validate()
104
114
 
105
115
  if not self.token_manager.iam_profile_name and not self.token_manager.iam_profile_id:
106
- raise ValueError(
107
- 'At least one of iam_profile_name or iam_profile_id must be specified.')
116
+ raise ValueError('At least one of iam_profile_name or iam_profile_id must be specified.')
108
117
 
109
118
  def set_cr_token_filename(self, cr_token_filename: str) -> None:
110
119
  """Set the location of the compute resource token on the local filesystem.
@@ -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.
@@ -15,13 +15,15 @@
15
15
  # limitations under the License.
16
16
 
17
17
  from typing import Dict, Optional
18
-
19
18
  from requests import Request
20
19
 
20
+ from ibm_cloud_sdk_core.logger import get_logger
21
21
  from .authenticator import Authenticator
22
22
  from ..token_managers.cp4d_token_manager import CP4DTokenManager
23
23
  from ..utils import has_bad_first_or_last_char
24
24
 
25
+ logger = get_logger()
26
+
25
27
 
26
28
  class CloudPakForDataAuthenticator(Authenticator):
27
29
  """The CloudPakForDataAuthenticator utilizes a username and password pair to
@@ -52,23 +54,32 @@ class CloudPakForDataAuthenticator(Authenticator):
52
54
  ValueError: The username, password/apikey, and/or url are not valid for CP4D token requests.
53
55
  """
54
56
 
55
- def __init__(self,
56
- username: str = None,
57
- password: str = None,
58
- url: str = None,
59
- *,
60
- apikey: str = None,
61
- disable_ssl_verification: bool = False,
62
- headers: Optional[Dict[str, str]] = None,
63
- proxies: Optional[Dict[str, str]] = None,
64
- verify: Optional[str] = None) -> None:
57
+ def __init__(
58
+ self,
59
+ username: str = None,
60
+ password: str = None,
61
+ url: str = None,
62
+ *,
63
+ apikey: str = None,
64
+ disable_ssl_verification: bool = False,
65
+ headers: Optional[Dict[str, str]] = None,
66
+ proxies: Optional[Dict[str, str]] = None,
67
+ verify: Optional[str] = None,
68
+ ) -> None:
65
69
  # Check the type of `disable_ssl_verification`. Must be a bool.
66
70
  if not isinstance(disable_ssl_verification, bool):
67
71
  raise TypeError('disable_ssl_verification must be a bool')
68
72
 
69
73
  self.token_manager = CP4DTokenManager(
70
- username=username, password=password, apikey=apikey, url=url,
71
- disable_ssl_verification=disable_ssl_verification, headers=headers, proxies=proxies, verify=verify)
74
+ username=username,
75
+ password=password,
76
+ apikey=apikey,
77
+ url=url,
78
+ disable_ssl_verification=disable_ssl_verification,
79
+ headers=headers,
80
+ proxies=proxies,
81
+ verify=verify,
82
+ )
72
83
 
73
84
  self.validate()
74
85
 
@@ -88,24 +99,27 @@ class CloudPakForDataAuthenticator(Authenticator):
88
99
  if self.token_manager.username is None:
89
100
  raise ValueError('The username shouldn\'t be None.')
90
101
 
91
- if ((self.token_manager.password is None and self.token_manager.apikey is None)
92
- or (self.token_manager.password is not None and self.token_manager.apikey is not None)):
93
- raise ValueError(
94
- 'Exactly one of `apikey` or `password` must be specified.')
102
+ if (self.token_manager.password is None and self.token_manager.apikey is None) or (
103
+ self.token_manager.password is not None and self.token_manager.apikey is not None
104
+ ):
105
+ raise ValueError('Exactly one of `apikey` or `password` must be specified.')
95
106
 
96
107
  if self.token_manager.url is None:
97
108
  raise ValueError('The url shouldn\'t be None.')
98
109
 
99
- if has_bad_first_or_last_char(
100
- self.token_manager.username) or has_bad_first_or_last_char(self.token_manager.password):
110
+ if has_bad_first_or_last_char(self.token_manager.username) or has_bad_first_or_last_char(
111
+ self.token_manager.password
112
+ ):
101
113
  raise ValueError(
102
114
  'The username and password shouldn\'t start or end with curly brackets or quotes. '
103
- 'Please remove any surrounding {, }, or \" characters.')
115
+ 'Please remove any surrounding {, }, or \" characters.'
116
+ )
104
117
 
105
118
  if has_bad_first_or_last_char(self.token_manager.url):
106
119
  raise ValueError(
107
120
  'The url shouldn\'t start or end with curly brackets or quotes. '
108
- 'Please remove any surrounding {, }, or \" characters.')
121
+ 'Please remove any surrounding {, }, or \" characters.'
122
+ )
109
123
 
110
124
  def authenticate(self, req: Request) -> None:
111
125
  """Adds CP4D authentication information to the request.
@@ -115,12 +129,13 @@ class CloudPakForDataAuthenticator(Authenticator):
115
129
  Authorization: Bearer <bearer-token>
116
130
 
117
131
  Args:
118
- req: The request to add CP4D authentication information too. Must contain a key to a dictionary
132
+ req: The request to add CP4D authentication information to. Must contain a key to a dictionary
119
133
  called headers.
120
134
  """
121
135
  headers = req.get('headers')
122
136
  bearer_token = self.token_manager.get_token()
123
137
  headers['Authorization'] = 'Bearer {0}'.format(bearer_token)
138
+ logger.debug('Authenticated outbound request (type=%s)', self.authentication_type())
124
139
 
125
140
  def set_disable_ssl_verification(self, status: bool = False) -> None:
126
141
  """Set the flag that indicates whether verification of the server's SSL certificate should be
@@ -56,24 +56,32 @@ class IAMAuthenticator(IAMRequestBasedAuthenticator):
56
56
  ValueError: The apikey, client_id, and/or client_secret are not valid for IAM token requests.
57
57
  """
58
58
 
59
- def __init__(self,
60
- apikey: str,
61
- *,
62
- url: Optional[str] = None,
63
- client_id: Optional[str] = None,
64
- client_secret: Optional[str] = None,
65
- disable_ssl_verification: bool = False,
66
- headers: Optional[Dict[str, str]] = None,
67
- proxies: Optional[Dict[str, str]] = None,
68
- scope: Optional[str] = None) -> None:
59
+ def __init__(
60
+ self,
61
+ apikey: str,
62
+ *,
63
+ url: Optional[str] = None,
64
+ client_id: Optional[str] = None,
65
+ client_secret: Optional[str] = None,
66
+ disable_ssl_verification: bool = False,
67
+ headers: Optional[Dict[str, str]] = None,
68
+ proxies: Optional[Dict[str, str]] = None,
69
+ scope: Optional[str] = None,
70
+ ) -> None:
69
71
  # Check the type of `disable_ssl_verification`. Must be a bool.
70
72
  if not isinstance(disable_ssl_verification, bool):
71
73
  raise TypeError('disable_ssl_verification must be a bool')
72
74
 
73
75
  self.token_manager = IAMTokenManager(
74
- apikey, url=url, client_id=client_id, client_secret=client_secret,
76
+ apikey,
77
+ url=url,
78
+ client_id=client_id,
79
+ client_secret=client_secret,
75
80
  disable_ssl_verification=disable_ssl_verification,
76
- headers=headers, proxies=proxies, scope=scope)
81
+ headers=headers,
82
+ proxies=proxies,
83
+ scope=scope,
84
+ )
77
85
 
78
86
  self.validate()
79
87
 
@@ -98,4 +106,5 @@ class IAMAuthenticator(IAMRequestBasedAuthenticator):
98
106
  if has_bad_first_or_last_char(self.token_manager.apikey):
99
107
  raise ValueError(
100
108
  'The apikey shouldn\'t start or end with curly brackets or quotes. '
101
- 'Please remove any surrounding {, }, or \" characters.')
109
+ 'Please remove any surrounding {, }, or \" characters.'
110
+ )
@@ -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.
@@ -18,8 +18,11 @@ from typing import Dict
18
18
 
19
19
  from requests import Request
20
20
 
21
+ from ibm_cloud_sdk_core.logger import get_logger
21
22
  from .authenticator import Authenticator
22
23
 
24
+ logger = get_logger()
25
+
23
26
 
24
27
  class IAMRequestBasedAuthenticator(Authenticator):
25
28
  """The IAMRequestBasedAuthenticator class contains code that is common to all authenticators
@@ -41,12 +44,10 @@ class IAMRequestBasedAuthenticator(Authenticator):
41
44
  Raises:
42
45
  ValueError: The client_id, and/or client_secret are not valid for IAM token requests.
43
46
  """
44
- if (self.token_manager.client_id and
45
- not self.token_manager.client_secret) or (
46
- not self.token_manager.client_id and
47
- self.token_manager.client_secret):
48
- raise ValueError(
49
- 'Both client_id and client_secret should be initialized.')
47
+ if (self.token_manager.client_id and not self.token_manager.client_secret) or (
48
+ not self.token_manager.client_id and self.token_manager.client_secret
49
+ ):
50
+ raise ValueError('Both client_id and client_secret should be initialized.')
50
51
 
51
52
  def authenticate(self, req: Request) -> None:
52
53
  """Adds IAM authentication information to the request.
@@ -56,12 +57,13 @@ class IAMRequestBasedAuthenticator(Authenticator):
56
57
  Authorization: Bearer <bearer-token>
57
58
 
58
59
  Args:
59
- req: The request to add IAM authentication information too. Must contain a key to a dictionary
60
+ req: The request to add IAM authentication information to. Must contain a key to a dictionary
60
61
  called headers.
61
62
  """
62
63
  headers = req.get('headers')
63
64
  bearer_token = self.token_manager.get_token()
64
65
  headers['Authorization'] = 'Bearer {0}'.format(bearer_token)
66
+ logger.debug('Authenticated outbound request (type=%s)', self.authentication_type())
65
67
 
66
68
  def set_client_id_and_secret(self, client_id: str, client_secret: str) -> None:
67
69
  """Set the client_id and client_secret pair the token manager will use for IAM token requests.
@@ -0,0 +1,134 @@
1
+ # coding: utf-8
2
+
3
+ # Copyright 2023, 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
+
17
+ from typing import Dict, Optional
18
+
19
+ from requests import Request
20
+
21
+ from ibm_cloud_sdk_core.logger import get_logger
22
+ from .authenticator import Authenticator
23
+ from ..token_managers.mcsp_token_manager import MCSPTokenManager
24
+
25
+ logger = get_logger()
26
+
27
+
28
+ class MCSPAuthenticator(Authenticator):
29
+ """The MCSPAuthenticator uses an apikey to obtain an access token from the MCSP token server.
30
+ When the access token expires, a new access token is obtained from the token server.
31
+ The access token will be added to outbound requests via the Authorization header
32
+ of the form: "Authorization: Bearer <access-token>"
33
+
34
+ Keyword Args:
35
+ url: The base endpoint URL for the MCSP token service [required].
36
+ apikey: The API key used to obtain an access token [required].
37
+ disable_ssl_verification: A flag that indicates whether verification of the server's SSL
38
+ certificate should be disabled or not. Defaults to False.
39
+ headers: Default headers to be sent with every MCSP token request. Defaults to None.
40
+ proxies: Dictionary for mapping request protocol to proxy URL.
41
+ proxies.http (optional): The proxy endpoint to use for HTTP requests.
42
+ proxies.https (optional): The proxy endpoint to use for HTTPS requests.
43
+
44
+ Attributes:
45
+ token_manager (MCSPTokenManager): Retrieves and manages MCSP tokens from the endpoint specified by the url.
46
+
47
+ Raises:
48
+ TypeError: The `disable_ssl_verification` is not a bool.
49
+ ValueError: The apikey and/or url are not valid for MCSP token exchange requests.
50
+ """
51
+
52
+ def __init__(
53
+ self,
54
+ apikey: str,
55
+ url: str,
56
+ *,
57
+ disable_ssl_verification: bool = False,
58
+ headers: Optional[Dict[str, str]] = None,
59
+ proxies: Optional[Dict[str, str]] = None,
60
+ ) -> None:
61
+ # Check the type of `disable_ssl_verification`. Must be a bool.
62
+ if not isinstance(disable_ssl_verification, bool):
63
+ raise TypeError('disable_ssl_verification must be a bool')
64
+
65
+ self.token_manager = MCSPTokenManager(
66
+ apikey=apikey,
67
+ url=url,
68
+ disable_ssl_verification=disable_ssl_verification,
69
+ headers=headers,
70
+ proxies=proxies,
71
+ )
72
+
73
+ self.validate()
74
+
75
+ def authentication_type(self) -> str:
76
+ """Returns this authenticator's type ('mcsp')."""
77
+ return Authenticator.AUTHTYPE_MCSP
78
+
79
+ def validate(self) -> None:
80
+ """Validate apikey and url for token requests.
81
+
82
+ Raises:
83
+ ValueError: The apikey and/or url are not valid for token requests.
84
+ """
85
+ if self.token_manager.apikey is None:
86
+ raise ValueError('The apikey shouldn\'t be None.')
87
+
88
+ if self.token_manager.url is None:
89
+ raise ValueError('The url shouldn\'t be None.')
90
+
91
+ def authenticate(self, req: Request) -> None:
92
+ """Adds MCSP authentication information to the request.
93
+
94
+ The MCSP bearer token will be added to the request's headers in the form:
95
+ Authorization: Bearer <bearer-token>
96
+
97
+ Args:
98
+ req: The request to add MCSP authentication information to. Must contain a key to a dictionary
99
+ called headers.
100
+ """
101
+ headers = req.get('headers')
102
+ bearer_token = self.token_manager.get_token()
103
+ headers['Authorization'] = 'Bearer {0}'.format(bearer_token)
104
+ logger.debug('Authenticated outbound request (type=%s)', self.authentication_type())
105
+
106
+ def set_disable_ssl_verification(self, status: bool = False) -> None:
107
+ """Set the flag that indicates whether verification of the server's SSL certificate should be
108
+ disabled or not. Defaults to False.
109
+
110
+ Args:
111
+ status: Set to true in order to disable SSL certificate verification. Defaults to False.
112
+
113
+ Raises:
114
+ TypeError: The `status` is not a bool.
115
+ """
116
+ self.token_manager.set_disable_ssl_verification(status)
117
+
118
+ def set_headers(self, headers: Dict[str, str]) -> None:
119
+ """Default headers to be sent with every MCSP token request.
120
+
121
+ Args:
122
+ headers: The headers to be sent with every MCSP token request.
123
+ """
124
+ self.token_manager.set_headers(headers)
125
+
126
+ def set_proxies(self, proxies: Dict[str, str]) -> None:
127
+ """Sets the proxies the token manager will use to communicate with MCSP on behalf of the host.
128
+
129
+ Args:
130
+ proxies: Dictionary for mapping request protocol to proxy URL.
131
+ proxies.http (optional): The proxy endpoint to use for HTTP requests.
132
+ proxies.https (optional): The proxy endpoint to use for HTTPS requests.
133
+ """
134
+ self.token_manager.set_proxies(proxies)