ibm-cloud-sdk-core 3.16.0__py3-none-any.whl → 3.20.6__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.
- ibm_cloud_sdk_core/__init__.py +1 -0
- ibm_cloud_sdk_core/api_exception.py +18 -4
- ibm_cloud_sdk_core/authenticators/__init__.py +1 -0
- ibm_cloud_sdk_core/authenticators/authenticator.py +2 -1
- ibm_cloud_sdk_core/authenticators/basic_authenticator.py +5 -6
- ibm_cloud_sdk_core/authenticators/bearer_token_authenticator.py +1 -1
- ibm_cloud_sdk_core/authenticators/container_authenticator.py +25 -16
- ibm_cloud_sdk_core/authenticators/cp4d_authenticator.py +33 -21
- ibm_cloud_sdk_core/authenticators/iam_authenticator.py +22 -13
- ibm_cloud_sdk_core/authenticators/iam_request_based_authenticator.py +5 -7
- ibm_cloud_sdk_core/authenticators/mcsp_authenticator.py +130 -0
- ibm_cloud_sdk_core/authenticators/vpc_instance_authenticator.py +7 -10
- ibm_cloud_sdk_core/base_service.py +107 -91
- ibm_cloud_sdk_core/detailed_response.py +21 -15
- ibm_cloud_sdk_core/get_authenticator.py +28 -16
- ibm_cloud_sdk_core/http_adapter.py +28 -0
- ibm_cloud_sdk_core/private_helpers.py +34 -0
- ibm_cloud_sdk_core/token_managers/container_token_manager.py +61 -30
- ibm_cloud_sdk_core/token_managers/cp4d_token_manager.py +30 -22
- ibm_cloud_sdk_core/token_managers/iam_request_based_token_manager.py +43 -20
- ibm_cloud_sdk_core/token_managers/iam_token_manager.py +24 -13
- ibm_cloud_sdk_core/token_managers/jwt_token_manager.py +3 -16
- ibm_cloud_sdk_core/token_managers/mcsp_token_manager.py +102 -0
- ibm_cloud_sdk_core/token_managers/token_manager.py +13 -23
- ibm_cloud_sdk_core/token_managers/vpc_instance_token_manager.py +33 -13
- ibm_cloud_sdk_core/utils.py +121 -46
- ibm_cloud_sdk_core/version.py +1 -1
- {ibm_cloud_sdk_core-3.16.0.dist-info → ibm_cloud_sdk_core-3.20.6.dist-info}/METADATA +40 -28
- ibm_cloud_sdk_core-3.20.6.dist-info/RECORD +34 -0
- {ibm_cloud_sdk_core-3.16.0.dist-info → ibm_cloud_sdk_core-3.20.6.dist-info}/WHEEL +1 -1
- ibm_cloud_sdk_core-3.20.6.dist-info/top_level.txt +1 -0
- ibm_cloud_sdk_core-3.16.0.dist-info/RECORD +0 -52
- ibm_cloud_sdk_core-3.16.0.dist-info/top_level.txt +0 -3
- ibm_cloud_sdk_core-3.16.0.dist-info/zip-safe +0 -1
- test/__init__.py +0 -0
- test/test_api_exception.py +0 -73
- test/test_authenticator.py +0 -21
- test/test_base_service.py +0 -933
- test/test_basic_authenticator.py +0 -36
- test/test_bearer_authenticator.py +0 -28
- test/test_container_authenticator.py +0 -105
- test/test_container_token_manager.py +0 -283
- test/test_cp4d_authenticator.py +0 -171
- test/test_cp4d_token_manager.py +0 -56
- test/test_detailed_response.py +0 -57
- test/test_iam_authenticator.py +0 -157
- test/test_iam_token_manager.py +0 -362
- test/test_jwt_token_manager.py +0 -109
- test/test_no_auth_authenticator.py +0 -15
- test/test_token_manager.py +0 -84
- test/test_utils.py +0 -634
- test/test_vpc_instance_authenticator.py +0 -66
- test/test_vpc_instance_token_manager.py +0 -266
- test_integration/__init__.py +0 -0
- test_integration/test_cp4d_authenticator_integration.py +0 -45
- {ibm_cloud_sdk_core-3.16.0.dist-info → ibm_cloud_sdk_core-3.20.6.dist-info}/LICENSE +0 -0
|
@@ -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.
|
|
@@ -18,6 +18,7 @@ import logging
|
|
|
18
18
|
from typing import Dict, Optional
|
|
19
19
|
|
|
20
20
|
from .iam_request_based_token_manager import IAMRequestBasedTokenManager
|
|
21
|
+
from ..private_helpers import _build_user_agent
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
logger = logging.getLogger(__name__)
|
|
@@ -79,51 +80,64 @@ class ContainerTokenManager(IAMRequestBasedTokenManager):
|
|
|
79
80
|
scope: The "scope" to use when fetching the bearer token from the IAM token server.
|
|
80
81
|
This can be used to obtain an access token with a specific scope.
|
|
81
82
|
"""
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
83
|
+
|
|
84
|
+
DEFAULT_CR_TOKEN_FILENAME1 = '/var/run/secrets/tokens/vault-token'
|
|
85
|
+
DEFAULT_CR_TOKEN_FILENAME2 = '/var/run/secrets/tokens/sa-token'
|
|
86
|
+
|
|
87
|
+
def __init__(
|
|
88
|
+
self,
|
|
89
|
+
cr_token_filename: Optional[str] = None,
|
|
90
|
+
iam_profile_name: Optional[str] = None,
|
|
91
|
+
iam_profile_id: Optional[str] = None,
|
|
92
|
+
url: Optional[str] = None,
|
|
93
|
+
client_id: Optional[str] = None,
|
|
94
|
+
client_secret: Optional[str] = None,
|
|
95
|
+
disable_ssl_verification: bool = False,
|
|
96
|
+
scope: Optional[str] = None,
|
|
97
|
+
proxies: Optional[Dict[str, str]] = None,
|
|
98
|
+
headers: Optional[Dict[str, str]] = None,
|
|
99
|
+
) -> None:
|
|
95
100
|
super().__init__(
|
|
96
|
-
url=url,
|
|
97
|
-
|
|
101
|
+
url=url,
|
|
102
|
+
client_id=client_id,
|
|
103
|
+
client_secret=client_secret,
|
|
104
|
+
disable_ssl_verification=disable_ssl_verification,
|
|
105
|
+
headers=headers,
|
|
106
|
+
proxies=proxies,
|
|
107
|
+
scope=scope,
|
|
108
|
+
)
|
|
98
109
|
|
|
99
110
|
self.cr_token_filename = cr_token_filename
|
|
100
111
|
self.iam_profile_name = iam_profile_name
|
|
101
112
|
self.iam_profile_id = iam_profile_id
|
|
102
113
|
|
|
103
114
|
self.request_payload['grant_type'] = 'urn:ibm:params:oauth:grant-type:cr-token'
|
|
115
|
+
self._set_user_agent(_build_user_agent('container-authenticator'))
|
|
104
116
|
|
|
105
117
|
def retrieve_cr_token(self) -> str:
|
|
106
118
|
"""Retrieves the CR token for the current compute resource by reading it from the local file system.
|
|
107
119
|
|
|
108
120
|
Raises:
|
|
109
|
-
Exception:
|
|
121
|
+
Exception: Error retrieving the compute resource token.
|
|
110
122
|
|
|
111
123
|
Returns:
|
|
112
124
|
A string which contains the compute resource token.
|
|
113
125
|
"""
|
|
114
|
-
cr_token_filename = self.cr_token_filename if self.cr_token_filename else self.DEFAULT_CR_TOKEN_FILENAME
|
|
115
|
-
|
|
116
|
-
logger.debug('Attempting to read CR token from file: %s',
|
|
117
|
-
cr_token_filename)
|
|
118
|
-
|
|
119
126
|
try:
|
|
120
|
-
|
|
121
|
-
|
|
127
|
+
cr_token = None
|
|
128
|
+
if self.cr_token_filename:
|
|
129
|
+
# If the user specified a filename, then use that.
|
|
130
|
+
cr_token = self.read_file(self.cr_token_filename)
|
|
131
|
+
else:
|
|
132
|
+
# If the user didn't specify a filename, then try our two defaults.
|
|
133
|
+
try:
|
|
134
|
+
cr_token = self.read_file(self.DEFAULT_CR_TOKEN_FILENAME1)
|
|
135
|
+
except:
|
|
136
|
+
cr_token = self.read_file(self.DEFAULT_CR_TOKEN_FILENAME2)
|
|
122
137
|
return cr_token
|
|
123
|
-
# pylint: disable=broad-except
|
|
124
138
|
except Exception as ex:
|
|
125
|
-
|
|
126
|
-
|
|
139
|
+
# pylint: disable=broad-exception-raised
|
|
140
|
+
raise Exception('Unable to retrieve the CR token: {}'.format(ex)) from None
|
|
127
141
|
|
|
128
142
|
def request_token(self) -> dict:
|
|
129
143
|
"""Retrieves a CR token value from the current compute resource,
|
|
@@ -132,11 +146,9 @@ class ContainerTokenManager(IAMRequestBasedTokenManager):
|
|
|
132
146
|
Returns:
|
|
133
147
|
A dictionary containing the bearer token to be subsequently used service requests.
|
|
134
148
|
"""
|
|
135
|
-
# Retrieve the CR token for this compute resource.
|
|
136
|
-
cr_token = self.retrieve_cr_token()
|
|
137
149
|
|
|
138
150
|
# Set the request payload.
|
|
139
|
-
self.request_payload['cr_token'] =
|
|
151
|
+
self.request_payload['cr_token'] = self.retrieve_cr_token()
|
|
140
152
|
|
|
141
153
|
if self.iam_profile_id:
|
|
142
154
|
self.request_payload['profile_id'] = self.iam_profile_id
|
|
@@ -168,3 +180,22 @@ class ContainerTokenManager(IAMRequestBasedTokenManager):
|
|
|
168
180
|
iam_profile_id: id of the linked trusted IAM profile to be used when obtaining the IAM access token
|
|
169
181
|
"""
|
|
170
182
|
self.iam_profile_id = iam_profile_id
|
|
183
|
+
|
|
184
|
+
def read_file(self, filename: str) -> str:
|
|
185
|
+
"""Read in the specified file and return the contents as a string.
|
|
186
|
+
Args:
|
|
187
|
+
filename: the name of the file to read
|
|
188
|
+
Returns:
|
|
189
|
+
The contents of the file as a string.
|
|
190
|
+
Raises:
|
|
191
|
+
Exception: An error occured reading the file.
|
|
192
|
+
"""
|
|
193
|
+
try:
|
|
194
|
+
logger.debug('Attempting to read CR token from file: %s', filename)
|
|
195
|
+
with open(filename, 'r', encoding='utf-8') as file:
|
|
196
|
+
cr_token = file.read()
|
|
197
|
+
logger.debug('Successfully read CR token from file: %s', filename)
|
|
198
|
+
return cr_token
|
|
199
|
+
except Exception as ex:
|
|
200
|
+
# pylint: disable=broad-exception-raised
|
|
201
|
+
raise Exception('Error reading CR token from file {}: {}'.format(filename, ex)) from None
|
|
@@ -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.
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
import json
|
|
18
18
|
from typing import Dict, Optional
|
|
19
19
|
|
|
20
|
+
from ..private_helpers import _build_user_agent
|
|
20
21
|
from .jwt_token_manager import JWTTokenManager
|
|
21
22
|
|
|
22
23
|
|
|
@@ -48,19 +49,22 @@ class CP4DTokenManager(JWTTokenManager):
|
|
|
48
49
|
proxies.https (str): The proxy endpoint to use for HTTPS requests.
|
|
49
50
|
verify (str): The path to the certificate to use for HTTPS requests.
|
|
50
51
|
"""
|
|
52
|
+
|
|
51
53
|
TOKEN_NAME = 'token'
|
|
52
54
|
VALIDATE_AUTH_PATH = '/v1/authorize'
|
|
53
55
|
|
|
54
|
-
def __init__(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
username: str = None,
|
|
59
|
+
password: str = None,
|
|
60
|
+
url: str = None,
|
|
61
|
+
*,
|
|
62
|
+
apikey: str = None,
|
|
63
|
+
disable_ssl_verification: bool = False,
|
|
64
|
+
headers: Optional[Dict[str, str]] = None,
|
|
65
|
+
proxies: Optional[Dict[str, str]] = None,
|
|
66
|
+
verify: Optional[str] = None,
|
|
67
|
+
) -> None:
|
|
64
68
|
self.username = username
|
|
65
69
|
self.password = password
|
|
66
70
|
self.verify = verify
|
|
@@ -72,23 +76,27 @@ class CP4DTokenManager(JWTTokenManager):
|
|
|
72
76
|
self.headers = {}
|
|
73
77
|
self.headers['Content-Type'] = 'application/json'
|
|
74
78
|
self.proxies = proxies
|
|
75
|
-
super().__init__(url, disable_ssl_verification=disable_ssl_verification,
|
|
76
|
-
|
|
79
|
+
super().__init__(url, disable_ssl_verification=disable_ssl_verification, token_name=self.TOKEN_NAME)
|
|
80
|
+
self._set_user_agent(_build_user_agent('cp4d-authenticator'))
|
|
77
81
|
|
|
78
82
|
def request_token(self) -> dict:
|
|
79
|
-
"""Makes a request for a token.
|
|
80
|
-
|
|
83
|
+
"""Makes a request for a token."""
|
|
84
|
+
required_headers = {
|
|
85
|
+
'User-Agent': self.user_agent,
|
|
86
|
+
}
|
|
87
|
+
request_headers = {}
|
|
88
|
+
if self.headers is not None and isinstance(self.headers, dict):
|
|
89
|
+
request_headers.update(self.headers)
|
|
90
|
+
request_headers.update(required_headers)
|
|
91
|
+
|
|
81
92
|
response = self._request(
|
|
82
93
|
method='POST',
|
|
83
|
-
headers=
|
|
94
|
+
headers=request_headers,
|
|
84
95
|
url=self.url,
|
|
85
|
-
data=json.dumps({
|
|
86
|
-
"username": self.username,
|
|
87
|
-
"password": self.password,
|
|
88
|
-
"api_key": self.apikey
|
|
89
|
-
}),
|
|
96
|
+
data=json.dumps({"username": self.username, "password": self.password, "api_key": self.apikey}),
|
|
90
97
|
proxies=self.proxies,
|
|
91
|
-
verify=self.verify
|
|
98
|
+
verify=self.verify,
|
|
99
|
+
)
|
|
92
100
|
return response
|
|
93
101
|
|
|
94
102
|
def set_headers(self, headers: Dict[str, str]) -> None:
|
|
@@ -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.
|
|
@@ -19,7 +19,7 @@ from typing import Dict, Optional
|
|
|
19
19
|
from .jwt_token_manager import JWTTokenManager
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
#pylint: disable=too-many-instance-attributes
|
|
22
|
+
# pylint: disable=too-many-instance-attributes
|
|
23
23
|
class IAMRequestBasedTokenManager(JWTTokenManager):
|
|
24
24
|
"""The IamRequestBasedTokenManager class contains code relevant to any token manager that
|
|
25
25
|
interacts with the IAM service to manage a token. It stores information relevant to all
|
|
@@ -60,21 +60,25 @@ class IAMRequestBasedTokenManager(JWTTokenManager):
|
|
|
60
60
|
scope: The "scope" to use when fetching the bearer token from the IAM token server.
|
|
61
61
|
This can be used to obtain an access token with a specific scope.
|
|
62
62
|
"""
|
|
63
|
+
|
|
63
64
|
DEFAULT_IAM_URL = 'https://iam.cloud.ibm.com'
|
|
64
65
|
OPERATION_PATH = "/identity/token"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
66
|
+
IAM_EXPIRATION_WINDOW = 10
|
|
67
|
+
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
url: Optional[str] = None,
|
|
71
|
+
client_id: Optional[str] = None,
|
|
72
|
+
client_secret: Optional[str] = None,
|
|
73
|
+
disable_ssl_verification: bool = False,
|
|
74
|
+
headers: Optional[Dict[str, str]] = None,
|
|
75
|
+
proxies: Optional[Dict[str, str]] = None,
|
|
76
|
+
scope: Optional[str] = None,
|
|
77
|
+
) -> None:
|
|
74
78
|
if not url:
|
|
75
79
|
url = self.DEFAULT_IAM_URL
|
|
76
80
|
if url.endswith(self.OPERATION_PATH):
|
|
77
|
-
url = url[
|
|
81
|
+
url = url[: -len(self.OPERATION_PATH)]
|
|
78
82
|
self.url = url
|
|
79
83
|
self.client_id = client_id
|
|
80
84
|
self.client_secret = client_secret
|
|
@@ -83,8 +87,7 @@ class IAMRequestBasedTokenManager(JWTTokenManager):
|
|
|
83
87
|
self.proxies = proxies
|
|
84
88
|
self.scope = scope
|
|
85
89
|
self.request_payload = {}
|
|
86
|
-
super().__init__(
|
|
87
|
-
self.url, disable_ssl_verification=disable_ssl_verification, token_name='access_token')
|
|
90
|
+
super().__init__(self.url, disable_ssl_verification=disable_ssl_verification, token_name='access_token')
|
|
88
91
|
|
|
89
92
|
def request_token(self) -> dict:
|
|
90
93
|
"""Request an IAM OAuth token given an API Key.
|
|
@@ -95,12 +98,15 @@ class IAMRequestBasedTokenManager(JWTTokenManager):
|
|
|
95
98
|
Returns:
|
|
96
99
|
A dictionary containing the bearer token to be subsequently used service requests.
|
|
97
100
|
"""
|
|
98
|
-
|
|
99
|
-
'Content-
|
|
100
|
-
'Accept': 'application/json'
|
|
101
|
+
required_headers = {
|
|
102
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
103
|
+
'Accept': 'application/json',
|
|
104
|
+
'User-Agent': self._get_user_agent(),
|
|
101
105
|
}
|
|
106
|
+
request_headers = {}
|
|
102
107
|
if self.headers is not None and isinstance(self.headers, dict):
|
|
103
|
-
|
|
108
|
+
request_headers.update(self.headers)
|
|
109
|
+
request_headers.update(required_headers)
|
|
104
110
|
|
|
105
111
|
data = dict(self.request_payload)
|
|
106
112
|
|
|
@@ -115,10 +121,11 @@ class IAMRequestBasedTokenManager(JWTTokenManager):
|
|
|
115
121
|
response = self._request(
|
|
116
122
|
method='POST',
|
|
117
123
|
url=(self.url + self.OPERATION_PATH) if self.url else self.url,
|
|
118
|
-
headers=
|
|
124
|
+
headers=request_headers,
|
|
119
125
|
data=data,
|
|
120
126
|
auth_tuple=auth_tuple,
|
|
121
|
-
proxies=self.proxies
|
|
127
|
+
proxies=self.proxies,
|
|
128
|
+
)
|
|
122
129
|
return response
|
|
123
130
|
|
|
124
131
|
def set_client_id_and_secret(self, client_id: str, client_secret: str) -> None:
|
|
@@ -167,3 +174,19 @@ class IAMRequestBasedTokenManager(JWTTokenManager):
|
|
|
167
174
|
value: A space seperated string that makes up the scope parameter.
|
|
168
175
|
"""
|
|
169
176
|
self.scope = value
|
|
177
|
+
|
|
178
|
+
def _is_token_expired(self) -> bool:
|
|
179
|
+
"""
|
|
180
|
+
Returns true iff the current cached token is expired.
|
|
181
|
+
We'll consider an access token as expired when we reach its IAM server-reported expiration time
|
|
182
|
+
minus our expiration window (10 secs).
|
|
183
|
+
We do this to avoid using an access token that might expire in the middle of a long-running transaction
|
|
184
|
+
within an IBM Cloud service.
|
|
185
|
+
|
|
186
|
+
Returns
|
|
187
|
+
-------
|
|
188
|
+
bool
|
|
189
|
+
True if token is expired; False otherwise
|
|
190
|
+
"""
|
|
191
|
+
current_time = self._get_current_time()
|
|
192
|
+
return current_time >= (self.expire_time - self.IAM_EXPIRATION_WINDOW)
|
|
@@ -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.
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
from typing import Dict, Optional
|
|
18
18
|
|
|
19
19
|
from .iam_request_based_token_manager import IAMRequestBasedTokenManager
|
|
20
|
+
from ..private_helpers import _build_user_agent
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class IAMTokenManager(IAMRequestBasedTokenManager):
|
|
@@ -60,19 +61,27 @@ class IAMTokenManager(IAMRequestBasedTokenManager):
|
|
|
60
61
|
This can be used to obtain an access token with a specific scope.
|
|
61
62
|
"""
|
|
62
63
|
|
|
63
|
-
def __init__(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
apikey: str,
|
|
67
|
+
*,
|
|
68
|
+
url: Optional[str] = None,
|
|
69
|
+
client_id: Optional[str] = None,
|
|
70
|
+
client_secret: Optional[str] = None,
|
|
71
|
+
disable_ssl_verification: bool = False,
|
|
72
|
+
headers: Optional[Dict[str, str]] = None,
|
|
73
|
+
proxies: Optional[Dict[str, str]] = None,
|
|
74
|
+
scope: Optional[str] = None,
|
|
75
|
+
) -> None:
|
|
73
76
|
super().__init__(
|
|
74
|
-
url=url,
|
|
75
|
-
|
|
77
|
+
url=url,
|
|
78
|
+
client_id=client_id,
|
|
79
|
+
client_secret=client_secret,
|
|
80
|
+
disable_ssl_verification=disable_ssl_verification,
|
|
81
|
+
headers=headers,
|
|
82
|
+
proxies=proxies,
|
|
83
|
+
scope=scope,
|
|
84
|
+
)
|
|
76
85
|
|
|
77
86
|
self.apikey = apikey
|
|
78
87
|
|
|
@@ -80,3 +89,5 @@ class IAMTokenManager(IAMRequestBasedTokenManager):
|
|
|
80
89
|
self.request_payload['grant_type'] = 'urn:ibm:params:oauth:grant-type:apikey'
|
|
81
90
|
self.request_payload['apikey'] = self.apikey
|
|
82
91
|
self.request_payload['response_type'] = 'cloud_iam'
|
|
92
|
+
|
|
93
|
+
self._set_user_agent(_build_user_agent('iam-authenticator'))
|
|
@@ -75,15 +75,7 @@ class JWTTokenManager(TokenManager, ABC):
|
|
|
75
75
|
buffer = (exp - iat) * 0.2
|
|
76
76
|
self.refresh_time = self.expire_time - buffer
|
|
77
77
|
|
|
78
|
-
def _request(self,
|
|
79
|
-
method,
|
|
80
|
-
url,
|
|
81
|
-
*,
|
|
82
|
-
headers=None,
|
|
83
|
-
params=None,
|
|
84
|
-
data=None,
|
|
85
|
-
auth_tuple=None,
|
|
86
|
-
**kwargs) -> dict:
|
|
78
|
+
def _request(self, method, url, *, headers=None, params=None, data=None, auth_tuple=None, **kwargs) -> dict:
|
|
87
79
|
kwargs = dict({"timeout": 60}, **kwargs)
|
|
88
80
|
kwargs = dict(kwargs, **self.http_config)
|
|
89
81
|
|
|
@@ -91,13 +83,8 @@ class JWTTokenManager(TokenManager, ABC):
|
|
|
91
83
|
kwargs['verify'] = False
|
|
92
84
|
|
|
93
85
|
response = requests.request(
|
|
94
|
-
method=method,
|
|
95
|
-
|
|
96
|
-
headers=headers,
|
|
97
|
-
params=params,
|
|
98
|
-
data=data,
|
|
99
|
-
auth=auth_tuple,
|
|
100
|
-
**kwargs)
|
|
86
|
+
method=method, url=url, headers=headers, params=params, data=data, auth=auth_tuple, **kwargs
|
|
87
|
+
)
|
|
101
88
|
if 200 <= response.status_code <= 299:
|
|
102
89
|
return response.json()
|
|
103
90
|
|
|
@@ -0,0 +1,102 @@
|
|
|
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
|
+
import json
|
|
18
|
+
from typing import Dict, Optional
|
|
19
|
+
|
|
20
|
+
from ..private_helpers import _build_user_agent
|
|
21
|
+
from .jwt_token_manager import JWTTokenManager
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class MCSPTokenManager(JWTTokenManager):
|
|
25
|
+
"""The MCSPTokenManager accepts a user-supplied apikey and performs the necessary interactions with
|
|
26
|
+
the Multi-Cloud Saas Platform (MCSP) token service to obtain an MCSP access token (a bearer token).
|
|
27
|
+
When the access token expires, a new access token is obtained from the token server.
|
|
28
|
+
|
|
29
|
+
Keyword Arguments:
|
|
30
|
+
apikey: The apikey for authentication [required].
|
|
31
|
+
url: The endpoint for JWT token requests [required].
|
|
32
|
+
disable_ssl_verification: Disable ssl verification. Defaults to False.
|
|
33
|
+
headers: Headers to be sent with every service token request. Defaults to None.
|
|
34
|
+
proxies: Proxies to use for making request. Defaults to None.
|
|
35
|
+
proxies.http (optional): The proxy endpoint to use for HTTP requests.
|
|
36
|
+
proxies.https (optional): The proxy endpoint to use for HTTPS requests.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
TOKEN_NAME = 'token'
|
|
40
|
+
OPERATION_PATH = '/siusermgr/api/1.0/apikeys/token'
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
apikey: str,
|
|
45
|
+
url: str,
|
|
46
|
+
*,
|
|
47
|
+
disable_ssl_verification: bool = False,
|
|
48
|
+
headers: Optional[Dict[str, str]] = None,
|
|
49
|
+
proxies: Optional[Dict[str, str]] = None,
|
|
50
|
+
) -> None:
|
|
51
|
+
self.apikey = apikey
|
|
52
|
+
self.headers = headers
|
|
53
|
+
if self.headers is None:
|
|
54
|
+
self.headers = {}
|
|
55
|
+
self.headers['Content-Type'] = 'application/json'
|
|
56
|
+
self.headers['Accept'] = 'application/json'
|
|
57
|
+
self.proxies = proxies
|
|
58
|
+
super().__init__(url, disable_ssl_verification=disable_ssl_verification, token_name=self.TOKEN_NAME)
|
|
59
|
+
self._set_user_agent(_build_user_agent('mcsp-authenticator'))
|
|
60
|
+
|
|
61
|
+
def request_token(self) -> dict:
|
|
62
|
+
"""Makes a request for a token."""
|
|
63
|
+
required_headers = {
|
|
64
|
+
'User-Agent': self.user_agent,
|
|
65
|
+
}
|
|
66
|
+
request_headers = {}
|
|
67
|
+
if self.headers is not None and isinstance(self.headers, dict):
|
|
68
|
+
request_headers.update(self.headers)
|
|
69
|
+
request_headers.update(required_headers)
|
|
70
|
+
|
|
71
|
+
response = self._request(
|
|
72
|
+
method='POST',
|
|
73
|
+
headers=request_headers,
|
|
74
|
+
url=self.url + self.OPERATION_PATH,
|
|
75
|
+
data=json.dumps({"apikey": self.apikey}),
|
|
76
|
+
proxies=self.proxies,
|
|
77
|
+
)
|
|
78
|
+
return response
|
|
79
|
+
|
|
80
|
+
def set_headers(self, headers: Dict[str, str]) -> None:
|
|
81
|
+
"""Headers to be sent with every MCSP token request.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
headers: The headers to be sent with every MCSP token request.
|
|
85
|
+
"""
|
|
86
|
+
if isinstance(headers, dict):
|
|
87
|
+
self.headers = headers
|
|
88
|
+
else:
|
|
89
|
+
raise TypeError('headers must be a dictionary')
|
|
90
|
+
|
|
91
|
+
def set_proxies(self, proxies: Dict[str, str]) -> None:
|
|
92
|
+
"""Sets the proxies the token manager will use to communicate with MCSP on behalf of the host.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
proxies: Proxies to use for making request. Defaults to None.
|
|
96
|
+
proxies.http (optional): The proxy endpoint to use for HTTP requests.
|
|
97
|
+
proxies.https (optional): The proxy endpoint to use for HTTPS requests.
|
|
98
|
+
"""
|
|
99
|
+
if isinstance(proxies, dict):
|
|
100
|
+
self.proxies = proxies
|
|
101
|
+
else:
|
|
102
|
+
raise TypeError('proxies must be a dictionary')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
2
|
|
|
3
|
-
# Copyright 2020 IBM All Rights Reserved.
|
|
3
|
+
# Copyright 2020, 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.
|
|
@@ -49,14 +49,10 @@ class TokenManager(ABC):
|
|
|
49
49
|
lock (Lock): Lock variable to serialize access to refresh/request times
|
|
50
50
|
http_config (dict): A dictionary containing values that control the timeout, proxies, and etc of HTTP requests.
|
|
51
51
|
access_token (str): The latest stored access token
|
|
52
|
+
user_agent (str): The User-Agent header value to be included in each outbound token request
|
|
52
53
|
"""
|
|
53
54
|
|
|
54
|
-
def __init__(
|
|
55
|
-
self,
|
|
56
|
-
url: str,
|
|
57
|
-
*,
|
|
58
|
-
disable_ssl_verification: bool = False
|
|
59
|
-
):
|
|
55
|
+
def __init__(self, url: str, *, disable_ssl_verification: bool = False):
|
|
60
56
|
self.url = url
|
|
61
57
|
self.disable_ssl_verification = disable_ssl_verification
|
|
62
58
|
self.expire_time = 0
|
|
@@ -65,6 +61,7 @@ class TokenManager(ABC):
|
|
|
65
61
|
self.lock = Lock()
|
|
66
62
|
self.http_config = {}
|
|
67
63
|
self.access_token = None
|
|
64
|
+
self.user_agent = None
|
|
68
65
|
|
|
69
66
|
def get_token(self) -> str:
|
|
70
67
|
"""Get a token to be used for authentication.
|
|
@@ -100,6 +97,12 @@ class TokenManager(ABC):
|
|
|
100
97
|
else:
|
|
101
98
|
raise TypeError('status must be a bool')
|
|
102
99
|
|
|
100
|
+
def _set_user_agent(self, user_agent: str = None) -> None:
|
|
101
|
+
self.user_agent = user_agent
|
|
102
|
+
|
|
103
|
+
def _get_user_agent(self) -> str:
|
|
104
|
+
return self.user_agent
|
|
105
|
+
|
|
103
106
|
def paced_request_token(self) -> None:
|
|
104
107
|
"""
|
|
105
108
|
Paces requests to request_token.
|
|
@@ -190,15 +193,7 @@ class TokenManager(ABC):
|
|
|
190
193
|
"""
|
|
191
194
|
pass
|
|
192
195
|
|
|
193
|
-
def _request(self,
|
|
194
|
-
method,
|
|
195
|
-
url,
|
|
196
|
-
*,
|
|
197
|
-
headers=None,
|
|
198
|
-
params=None,
|
|
199
|
-
data=None,
|
|
200
|
-
auth_tuple=None,
|
|
201
|
-
**kwargs):
|
|
196
|
+
def _request(self, method, url, *, headers=None, params=None, data=None, auth_tuple=None, **kwargs):
|
|
202
197
|
kwargs = dict({"timeout": 60}, **kwargs)
|
|
203
198
|
kwargs = dict(kwargs, **self.http_config)
|
|
204
199
|
|
|
@@ -206,13 +201,8 @@ class TokenManager(ABC):
|
|
|
206
201
|
kwargs['verify'] = False
|
|
207
202
|
|
|
208
203
|
response = requests.request(
|
|
209
|
-
method=method,
|
|
210
|
-
|
|
211
|
-
headers=headers,
|
|
212
|
-
params=params,
|
|
213
|
-
data=data,
|
|
214
|
-
auth=auth_tuple,
|
|
215
|
-
**kwargs)
|
|
204
|
+
method=method, url=url, headers=headers, params=params, data=data, auth=auth_tuple, **kwargs
|
|
205
|
+
)
|
|
216
206
|
if 200 <= response.status_code <= 299:
|
|
217
207
|
return response
|
|
218
208
|
|