Skip to content

Commit cdb2057

Browse files
committed
feat: use iam.cloud.ibm.com in IAM url; allow IAM client_id/secret to be configured
1 parent aa6a4e8 commit cdb2057

File tree

4 files changed

+183
-15
lines changed

4 files changed

+183
-15
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,6 @@ python3/
5555

5656
.sfdx/tools/apex.db
5757
.pytest_cache/
58+
/.project
59+
/.pydevproject
60+
/.settings/

ibm_cloud_sdk_core/iam_token_manager.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,18 @@
1919
from .api_exception import ApiException
2020

2121
class IAMTokenManager(object):
22-
DEFAULT_IAM_URL = 'https://iam.bluemix.net/identity/token'
22+
DEFAULT_IAM_URL = 'https://iam.cloud.ibm.com/identity/token'
2323
CONTENT_TYPE = 'application/x-www-form-urlencoded'
24-
DEFAULT_AUTHORIZATION = 'Basic Yng6Yng='
2524
REQUEST_TOKEN_GRANT_TYPE = 'urn:ibm:params:oauth:grant-type:apikey'
2625
REQUEST_TOKEN_RESPONSE_TYPE = 'cloud_iam'
2726
REFRESH_TOKEN_GRANT_TYPE = 'refresh_token'
2827

29-
def __init__(self, iam_apikey=None, iam_access_token=None, iam_url=None):
28+
def __init__(self, iam_apikey=None, iam_access_token=None, iam_url=None, iam_client_id=None, iam_secret=None):
3029
self.iam_apikey = iam_apikey
3130
self.user_access_token = iam_access_token
3231
self.iam_url = iam_url if iam_url else self.DEFAULT_IAM_URL
32+
self.iam_client_id = iam_client_id
33+
self.iam_secret = iam_secret
3334
self.token_info = {
3435
'access_token': None,
3536
'refresh_token': None,
@@ -39,9 +40,12 @@ def __init__(self, iam_apikey=None, iam_access_token=None, iam_url=None):
3940
}
4041

4142
def request(self, method, url, headers=None, params=None, data=None, **kwargs):
43+
auth_tuple = ('bx', 'bx')
44+
if self.iam_client_id and self.iam_secret:
45+
auth_tuple = (self.iam_client_id, self.iam_secret)
4246
response = requests.request(method=method, url=url,
4347
headers=headers, params=params,
44-
data=data, **kwargs)
48+
data=data, auth=auth_tuple, **kwargs)
4549
if 200 <= response.status_code <= 299:
4650
return response.json()
4751
else:
@@ -77,8 +81,7 @@ def _request_token(self):
7781
"""
7882
headers = {
7983
'Content-type': self.CONTENT_TYPE,
80-
'Authorization': self.DEFAULT_AUTHORIZATION,
81-
'accept': 'application/json'
84+
'Accept': 'application/json'
8285
}
8386
data = {
8487
'grant_type': self.REQUEST_TOKEN_GRANT_TYPE,
@@ -98,8 +101,7 @@ def _refresh_token(self):
98101
"""
99102
headers = {
100103
'Content-type': self.CONTENT_TYPE,
101-
'Authorization': self.DEFAULT_AUTHORIZATION,
102-
'accept': 'application/json'
104+
'Accept': 'application/json'
103105
}
104106
data = {
105107
'grant_type': self.REFRESH_TOKEN_GRANT_TYPE,
@@ -131,6 +133,18 @@ def set_iam_url(self, iam_url):
131133
"""
132134
self.iam_url = iam_url
133135

136+
def set_iam_authorization_info(self, iam_client_id, iam_secret):
137+
"""
138+
Set the IAM authorization information.
139+
This consists of the client_id and secret.
140+
These values are used to form the basic authorization header that
141+
is used when interacting with the IAM token server.
142+
If these values are not supplied, then a default Authorization header
143+
is used.
144+
"""
145+
self.iam_client_id = iam_client_id
146+
self.iam_secret = iam_secret
147+
134148
def _is_token_expired(self):
135149
"""
136150
Check if currently stored token is expired.
@@ -166,3 +180,4 @@ def _save_token_info(self, token_info):
166180
Save the response from the IAM service request to the object's state.
167181
"""
168182
self.token_info = token_info
183+

test/test_base_service.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,14 @@ def test_fail_http_config():
101101

102102
@responses.activate
103103
def test_iam():
104-
iam_url = "https://iam.bluemix.net/identity/token"
104+
iam_url = "https://iam.cloud.ibm.com/identity/token"
105105
service = AnyServiceV1('2017-07-07', iam_apikey="iam_apikey")
106106
assert service.token_manager is not None
107107

108-
service.set_iam_url('https://iam-test.bluemix.net/identity/token')
109-
assert service.token_manager.iam_url == 'https://iam-test.bluemix.net/identity/token'
108+
service.set_iam_url('https://iam-test.cloud.ibm.com/identity/token')
109+
assert service.token_manager.iam_url == 'https://iam-test.cloud.ibm.com/identity/token'
110110

111-
iam_url = "https://iam.bluemix.net/identity/token"
111+
iam_url = "https://iam.cloud.ibm.com/identity/token"
112112
service = AnyServiceV1('2017-07-07', username='xxx', password='yyy')
113113
assert service.token_manager is None
114114
service.set_iam_apikey('yyy')
@@ -149,14 +149,14 @@ def test_when_apikey_is_username():
149149
assert service1.iam_apikey == 'xxxxx'
150150
assert service1.username is None
151151
assert service1.password is None
152-
assert service1.token_manager.iam_url == 'https://iam.bluemix.net/identity/token'
152+
assert service1.token_manager.iam_url == 'https://iam.cloud.ibm.com/identity/token'
153153

154-
service2 = AnyServiceV1('2017-07-07', username='apikey', password='xxxxx', iam_url='https://iam.stage1.bluemix.net/identity/token')
154+
service2 = AnyServiceV1('2017-07-07', username='apikey', password='xxxxx', iam_url='https://iam.stage1.cloud.ibm.com/identity/token')
155155
assert service2.token_manager is not None
156156
assert service2.iam_apikey == 'xxxxx'
157157
assert service2.username is None
158158
assert service2.password is None
159-
assert service2.token_manager.iam_url == 'https://iam.stage1.bluemix.net/identity/token'
159+
assert service2.token_manager.iam_url == 'https://iam.stage1.cloud.ibm.com/identity/token'
160160

161161
def test_for_icp():
162162
service1 = AnyServiceV1('2017-07-07', username='apikey', password='icp-xxxx', url='service_url')

test/test_iam_token_manager.py

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,153 @@ def test_get_token():
109109
}
110110
token = token_manager.get_token()
111111
assert token == 'dummy'
112+
113+
@responses.activate
114+
def test_request_token_auth_default():
115+
iam_url = "https://iam.cloud.ibm.com/identity/token"
116+
response = """{
117+
"access_token": "oAeisG8yqPY7sFR_x66Z15",
118+
"token_type": "Bearer",
119+
"expires_in": 3600,
120+
"expiration": 1524167011,
121+
"refresh_token": "jy4gl91BQ"
122+
}"""
123+
default_auth_header = 'Basic Yng6Yng='
124+
responses.add(responses.POST, url=iam_url, body=response, status=200)
125+
126+
token_manager = IAMTokenManager("iam_apikey", "iam_access_token")
127+
token_manager._request_token()
128+
129+
assert len(responses.calls) == 1
130+
assert responses.calls[0].request.url == iam_url
131+
assert responses.calls[0].request.headers['Authorization'] == default_auth_header
132+
assert responses.calls[0].response.text == response
133+
134+
@responses.activate
135+
def test_request_token_auth_in_ctor():
136+
iam_url = "https://iam.cloud.ibm.com/identity/token"
137+
response = """{
138+
"access_token": "oAeisG8yqPY7sFR_x66Z15",
139+
"token_type": "Bearer",
140+
"expires_in": 3600,
141+
"expiration": 1524167011,
142+
"refresh_token": "jy4gl91BQ"
143+
}"""
144+
default_auth_header = 'Basic Yng6Yng='
145+
responses.add(responses.POST, url=iam_url, body=response, status=200)
146+
147+
token_manager = IAMTokenManager("iam_apikey", "iam_access_token", iam_url, 'foo', 'bar')
148+
token_manager._request_token()
149+
150+
assert len(responses.calls) == 1
151+
assert responses.calls[0].request.url == iam_url
152+
assert responses.calls[0].request.headers['Authorization'] != default_auth_header
153+
assert responses.calls[0].response.text == response
154+
155+
@responses.activate
156+
def test_request_token_auth_in_ctor_client_id_only():
157+
iam_url = "https://iam.bluemix.net/identity/token"
158+
response = """{
159+
"access_token": "oAeisG8yqPY7sFR_x66Z15",
160+
"token_type": "Bearer",
161+
"expires_in": 3600,
162+
"expiration": 1524167011,
163+
"refresh_token": "jy4gl91BQ"
164+
}"""
165+
default_auth_header = 'Basic Yng6Yng='
166+
responses.add(responses.POST, url=iam_url, body=response, status=200)
167+
168+
token_manager = IAMTokenManager("iam_apikey", "iam_access_token", iam_url, 'foo')
169+
token_manager._request_token()
170+
171+
assert len(responses.calls) == 1
172+
assert responses.calls[0].request.url == iam_url
173+
assert responses.calls[0].request.headers['Authorization'] == default_auth_header
174+
assert responses.calls[0].response.text == response
175+
176+
@responses.activate
177+
def test_request_token_auth_in_ctor_secret_only():
178+
iam_url = "https://iam.bluemix.net/identity/token"
179+
response = """{
180+
"access_token": "oAeisG8yqPY7sFR_x66Z15",
181+
"token_type": "Bearer",
182+
"expires_in": 3600,
183+
"expiration": 1524167011,
184+
"refresh_token": "jy4gl91BQ"
185+
}"""
186+
default_auth_header = 'Basic Yng6Yng='
187+
responses.add(responses.POST, url=iam_url, body=response, status=200)
188+
189+
token_manager = IAMTokenManager("iam_apikey", "iam_access_token", iam_url, None, 'bar')
190+
token_manager._request_token()
191+
192+
assert len(responses.calls) == 1
193+
assert responses.calls[0].request.url == iam_url
194+
assert responses.calls[0].request.headers['Authorization'] == default_auth_header
195+
assert responses.calls[0].response.text == response
196+
197+
@responses.activate
198+
def test_request_token_auth_in_setter():
199+
iam_url = "https://iam.cloud.ibm.com/identity/token"
200+
response = """{
201+
"access_token": "oAeisG8yqPY7sFR_x66Z15",
202+
"token_type": "Bearer",
203+
"expires_in": 3600,
204+
"expiration": 1524167011,
205+
"refresh_token": "jy4gl91BQ"
206+
}"""
207+
default_auth_header = 'Basic Yng6Yng='
208+
responses.add(responses.POST, url=iam_url, body=response, status=200)
209+
210+
token_manager = IAMTokenManager("iam_apikey")
211+
token_manager.set_iam_authorization_info('foo', 'bar')
212+
token_manager._request_token()
213+
214+
assert len(responses.calls) == 1
215+
assert responses.calls[0].request.url == iam_url
216+
assert responses.calls[0].request.headers['Authorization'] != default_auth_header
217+
assert responses.calls[0].response.text == response
218+
219+
@responses.activate
220+
def test_request_token_auth_in_setter_client_id_only():
221+
iam_url = "https://iam.cloud.ibm.com/identity/token"
222+
response = """{
223+
"access_token": "oAeisG8yqPY7sFR_x66Z15",
224+
"token_type": "Bearer",
225+
"expires_in": 3600,
226+
"expiration": 1524167011,
227+
"refresh_token": "jy4gl91BQ"
228+
}"""
229+
default_auth_header = 'Basic Yng6Yng='
230+
responses.add(responses.POST, url=iam_url, body=response, status=200)
231+
232+
token_manager = IAMTokenManager("iam_apikey")
233+
token_manager.set_iam_authorization_info('foo', None)
234+
token_manager._request_token()
235+
236+
assert len(responses.calls) == 1
237+
assert responses.calls[0].request.url == iam_url
238+
assert responses.calls[0].request.headers['Authorization'] == default_auth_header
239+
assert responses.calls[0].response.text == response
240+
241+
@responses.activate
242+
def test_request_token_auth_in_setter_secret_only():
243+
iam_url = "https://iam.cloud.ibm.com/identity/token"
244+
response = """{
245+
"access_token": "oAeisG8yqPY7sFR_x66Z15",
246+
"token_type": "Bearer",
247+
"expires_in": 3600,
248+
"expiration": 1524167011,
249+
"refresh_token": "jy4gl91BQ"
250+
}"""
251+
default_auth_header = 'Basic Yng6Yng='
252+
responses.add(responses.POST, url=iam_url, body=response, status=200)
253+
254+
token_manager = IAMTokenManager("iam_apikey")
255+
token_manager.set_iam_authorization_info(None, 'bar')
256+
token_manager._request_token()
257+
258+
assert len(responses.calls) == 1
259+
assert responses.calls[0].request.url == iam_url
260+
assert responses.calls[0].request.headers['Authorization'] == default_auth_header
261+
assert responses.calls[0].response.text == response

0 commit comments

Comments
 (0)