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.
- 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 +12 -7
- ibm_cloud_sdk_core/authenticators/bearer_token_authenticator.py +7 -2
- ibm_cloud_sdk_core/authenticators/container_authenticator.py +25 -16
- ibm_cloud_sdk_core/authenticators/cp4d_authenticator.py +38 -23
- ibm_cloud_sdk_core/authenticators/iam_authenticator.py +22 -13
- ibm_cloud_sdk_core/authenticators/iam_request_based_authenticator.py +10 -8
- ibm_cloud_sdk_core/authenticators/mcsp_authenticator.py +134 -0
- ibm_cloud_sdk_core/authenticators/vpc_instance_authenticator.py +12 -11
- ibm_cloud_sdk_core/base_service.py +137 -103
- ibm_cloud_sdk_core/detailed_response.py +21 -15
- ibm_cloud_sdk_core/get_authenticator.py +35 -17
- ibm_cloud_sdk_core/http_adapter.py +28 -0
- ibm_cloud_sdk_core/logger.py +85 -0
- ibm_cloud_sdk_core/private_helpers.py +34 -0
- ibm_cloud_sdk_core/token_managers/container_token_manager.py +63 -33
- ibm_cloud_sdk_core/token_managers/cp4d_token_manager.py +35 -22
- ibm_cloud_sdk_core/token_managers/iam_request_based_token_manager.py +50 -21
- 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 +108 -0
- ibm_cloud_sdk_core/token_managers/token_manager.py +20 -23
- ibm_cloud_sdk_core/token_managers/vpc_instance_token_manager.py +37 -18
- ibm_cloud_sdk_core/utils.py +127 -48
- ibm_cloud_sdk_core/version.py +1 -1
- ibm_cloud_sdk_core-3.21.0.dist-info/METADATA +159 -0
- ibm_cloud_sdk_core-3.21.0.dist-info/RECORD +35 -0
- {ibm_cloud_sdk_core-3.16.0.dist-info → ibm_cloud_sdk_core-3.21.0.dist-info}/WHEEL +1 -1
- ibm_cloud_sdk_core-3.21.0.dist-info/top_level.txt +1 -0
- ibm_cloud_sdk_core-3.16.0.dist-info/METADATA +0 -112
- 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.21.0.dist-info}/LICENSE +0 -0
test/test_detailed_response.py
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# coding=utf-8
|
|
2
|
-
# pylint: disable=missing-docstring
|
|
3
|
-
import json
|
|
4
|
-
|
|
5
|
-
import responses
|
|
6
|
-
import requests
|
|
7
|
-
|
|
8
|
-
from ibm_cloud_sdk_core import DetailedResponse
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def clean(val):
|
|
12
|
-
"""Eliminate all whitespace and convert single to double quotes"""
|
|
13
|
-
return val.translate(str.maketrans('', '', ' \n\t\r')).replace("'", "\"")
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
@responses.activate
|
|
17
|
-
def test_detailed_response_dict():
|
|
18
|
-
responses.add(responses.GET,
|
|
19
|
-
'https://test.com',
|
|
20
|
-
status=200,
|
|
21
|
-
body=json.dumps({'foobar': 'baz'}),
|
|
22
|
-
content_type='application/json')
|
|
23
|
-
|
|
24
|
-
mock_response = requests.get('https://test.com')
|
|
25
|
-
detailed_response = DetailedResponse(response=mock_response.json(), headers=mock_response.headers,
|
|
26
|
-
status_code=mock_response.status_code)
|
|
27
|
-
assert detailed_response is not None
|
|
28
|
-
assert detailed_response.get_result() == {'foobar': 'baz'}
|
|
29
|
-
assert detailed_response.get_headers() == {'Content-Type': 'application/json'}
|
|
30
|
-
assert detailed_response.get_status_code() == 200
|
|
31
|
-
|
|
32
|
-
response_str = clean(str(detailed_response))
|
|
33
|
-
assert clean(str(detailed_response.get_result())) in response_str
|
|
34
|
-
#assert clean(str(detailed_response.get_headers())) in response_str
|
|
35
|
-
assert clean(str(detailed_response.get_status_code())) in response_str
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@responses.activate
|
|
39
|
-
def test_detailed_response_list():
|
|
40
|
-
responses.add(responses.GET,
|
|
41
|
-
'https://test.com',
|
|
42
|
-
status=200,
|
|
43
|
-
body=json.dumps(['foobar', 'baz']),
|
|
44
|
-
content_type='application/json')
|
|
45
|
-
|
|
46
|
-
mock_response = requests.get('https://test.com')
|
|
47
|
-
detailed_response = DetailedResponse(response=mock_response.json(), headers=mock_response.headers,
|
|
48
|
-
status_code=mock_response.status_code)
|
|
49
|
-
assert detailed_response is not None
|
|
50
|
-
assert detailed_response.get_result() == ['foobar', 'baz']
|
|
51
|
-
assert detailed_response.get_headers() == {'Content-Type': 'application/json'}
|
|
52
|
-
assert detailed_response.get_status_code() == 200
|
|
53
|
-
|
|
54
|
-
response_str = clean(str(detailed_response))
|
|
55
|
-
assert clean(str(detailed_response.get_result())) in response_str
|
|
56
|
-
#assert clean(str(detailed_response.get_headers())) in response_str
|
|
57
|
-
assert clean(str(detailed_response.get_status_code())) in response_str
|
test/test_iam_authenticator.py
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
# pylint: disable=missing-docstring
|
|
2
|
-
import json
|
|
3
|
-
|
|
4
|
-
import jwt
|
|
5
|
-
import pytest
|
|
6
|
-
import responses
|
|
7
|
-
|
|
8
|
-
from ibm_cloud_sdk_core.authenticators import IAMAuthenticator, Authenticator
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def test_iam_authenticator():
|
|
12
|
-
authenticator = IAMAuthenticator(apikey='my_apikey')
|
|
13
|
-
assert authenticator is not None
|
|
14
|
-
assert authenticator.authentication_type() == Authenticator.AUTHTYPE_IAM
|
|
15
|
-
assert authenticator.token_manager.url == 'https://iam.cloud.ibm.com'
|
|
16
|
-
assert authenticator.token_manager.client_id is None
|
|
17
|
-
assert authenticator.token_manager.client_secret is None
|
|
18
|
-
assert authenticator.token_manager.disable_ssl_verification is False
|
|
19
|
-
assert authenticator.token_manager.headers is None
|
|
20
|
-
assert authenticator.token_manager.proxies is None
|
|
21
|
-
assert authenticator.token_manager.apikey == 'my_apikey'
|
|
22
|
-
assert authenticator.token_manager.scope is None
|
|
23
|
-
|
|
24
|
-
authenticator.set_client_id_and_secret('tom', 'jerry')
|
|
25
|
-
assert authenticator.token_manager.client_id == 'tom'
|
|
26
|
-
assert authenticator.token_manager.client_secret == 'jerry'
|
|
27
|
-
|
|
28
|
-
authenticator.set_scope('scope1 scope2 scope3')
|
|
29
|
-
assert authenticator.token_manager.scope == 'scope1 scope2 scope3'
|
|
30
|
-
|
|
31
|
-
with pytest.raises(TypeError) as err:
|
|
32
|
-
authenticator.set_headers('dummy')
|
|
33
|
-
assert str(err.value) == 'headers must be a dictionary'
|
|
34
|
-
|
|
35
|
-
authenticator.set_headers({'dummy': 'headers'})
|
|
36
|
-
assert authenticator.token_manager.headers == {'dummy': 'headers'}
|
|
37
|
-
|
|
38
|
-
with pytest.raises(TypeError) as err:
|
|
39
|
-
authenticator.set_proxies('dummy')
|
|
40
|
-
assert str(err.value) == 'proxies must be a dictionary'
|
|
41
|
-
|
|
42
|
-
authenticator.set_proxies({'dummy': 'proxies'})
|
|
43
|
-
assert authenticator.token_manager.proxies == {'dummy': 'proxies'}
|
|
44
|
-
|
|
45
|
-
authenticator.set_disable_ssl_verification(True)
|
|
46
|
-
assert authenticator.token_manager.disable_ssl_verification
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def test_disable_ssl_verification():
|
|
50
|
-
authenticator = IAMAuthenticator(
|
|
51
|
-
apikey='my_apikey', disable_ssl_verification=True)
|
|
52
|
-
assert authenticator.token_manager.disable_ssl_verification is True
|
|
53
|
-
|
|
54
|
-
authenticator.set_disable_ssl_verification(False)
|
|
55
|
-
assert authenticator.token_manager.disable_ssl_verification is False
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def test_invalid_disable_ssl_verification_type():
|
|
59
|
-
with pytest.raises(TypeError) as err:
|
|
60
|
-
authenticator = IAMAuthenticator(
|
|
61
|
-
apikey='my_apikey', disable_ssl_verification='True')
|
|
62
|
-
assert str(err.value) == 'disable_ssl_verification must be a bool'
|
|
63
|
-
|
|
64
|
-
authenticator = IAMAuthenticator(apikey='my_apikey')
|
|
65
|
-
assert authenticator.token_manager.disable_ssl_verification is False
|
|
66
|
-
|
|
67
|
-
with pytest.raises(TypeError) as err:
|
|
68
|
-
authenticator.set_disable_ssl_verification('True')
|
|
69
|
-
assert str(err.value) == 'status must be a bool'
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def test_iam_authenticator_with_scope():
|
|
73
|
-
authenticator = IAMAuthenticator(apikey='my_apikey', scope='scope1 scope2')
|
|
74
|
-
assert authenticator is not None
|
|
75
|
-
assert authenticator.token_manager.scope == 'scope1 scope2'
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def test_iam_authenticator_validate_failed():
|
|
79
|
-
with pytest.raises(ValueError) as err:
|
|
80
|
-
IAMAuthenticator(None)
|
|
81
|
-
assert str(err.value) == 'The apikey shouldn\'t be None.'
|
|
82
|
-
|
|
83
|
-
with pytest.raises(ValueError) as err:
|
|
84
|
-
IAMAuthenticator('{apikey}')
|
|
85
|
-
assert str(
|
|
86
|
-
err.value
|
|
87
|
-
) == 'The apikey shouldn\'t start or end with curly brackets or quotes. '\
|
|
88
|
-
'Please remove any surrounding {, }, or \" characters.'
|
|
89
|
-
|
|
90
|
-
with pytest.raises(ValueError) as err:
|
|
91
|
-
IAMAuthenticator('my_apikey', client_id='my_client_id')
|
|
92
|
-
assert str(
|
|
93
|
-
err.value) == 'Both client_id and client_secret should be initialized.'
|
|
94
|
-
|
|
95
|
-
with pytest.raises(ValueError) as err:
|
|
96
|
-
IAMAuthenticator('my_apikey', client_secret='my_client_secret')
|
|
97
|
-
assert str(
|
|
98
|
-
err.value) == 'Both client_id and client_secret should be initialized.'
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
@responses.activate
|
|
102
|
-
def test_get_token():
|
|
103
|
-
url = "https://iam.cloud.ibm.com/identity/token"
|
|
104
|
-
access_token_layout = {
|
|
105
|
-
"username": "dummy",
|
|
106
|
-
"role": "Admin",
|
|
107
|
-
"permissions": ["administrator", "manage_catalog"],
|
|
108
|
-
"sub": "admin",
|
|
109
|
-
"iss": "sss",
|
|
110
|
-
"aud": "sss",
|
|
111
|
-
"uid": "sss",
|
|
112
|
-
"iat": 1559324664,
|
|
113
|
-
"exp": 1559324664
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
access_token = jwt.encode(
|
|
117
|
-
access_token_layout,
|
|
118
|
-
'secret',
|
|
119
|
-
algorithm='HS256',
|
|
120
|
-
headers={
|
|
121
|
-
'kid': '230498151c214b788dd97f22b85410a5'
|
|
122
|
-
})
|
|
123
|
-
response = {
|
|
124
|
-
"access_token": access_token,
|
|
125
|
-
"token_type": "Bearer",
|
|
126
|
-
"expires_in": 3600,
|
|
127
|
-
"expiration": 1524167011,
|
|
128
|
-
"refresh_token": "jy4gl91BQ"
|
|
129
|
-
}
|
|
130
|
-
responses.add(
|
|
131
|
-
responses.POST, url=url, body=json.dumps(response), status=200)
|
|
132
|
-
|
|
133
|
-
auth_headers = {'Host': 'iam.cloud.ibm.com:443'}
|
|
134
|
-
authenticator = IAMAuthenticator('my_apikey', headers=auth_headers)
|
|
135
|
-
|
|
136
|
-
# Simulate an SDK API request that needs to be authenticated.
|
|
137
|
-
request = {'headers': {}}
|
|
138
|
-
|
|
139
|
-
# Trigger the "get token" processing to obtain the access token and add to the "SDK request".
|
|
140
|
-
authenticator.authenticate(request)
|
|
141
|
-
|
|
142
|
-
# Verify that the "authenticate()" method added the Authorization header
|
|
143
|
-
assert request['headers']['Authorization'] is not None
|
|
144
|
-
|
|
145
|
-
# Verify that the "get token" call contained the Host header.
|
|
146
|
-
assert responses.calls[0].request.headers.get(
|
|
147
|
-
'Host') == 'iam.cloud.ibm.com:443'
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
def test_multiple_iam_authenticators():
|
|
151
|
-
authenticator_1 = IAMAuthenticator('my_apikey')
|
|
152
|
-
assert authenticator_1.token_manager.request_payload['apikey'] == 'my_apikey'
|
|
153
|
-
|
|
154
|
-
authenticator_2 = IAMAuthenticator('my_other_apikey')
|
|
155
|
-
assert authenticator_2.token_manager.request_payload['apikey'] == 'my_other_apikey'
|
|
156
|
-
|
|
157
|
-
assert authenticator_1.token_manager.request_payload['apikey'] == 'my_apikey'
|
test/test_iam_token_manager.py
DELETED
|
@@ -1,362 +0,0 @@
|
|
|
1
|
-
# pylint: disable=missing-docstring
|
|
2
|
-
import os
|
|
3
|
-
import time
|
|
4
|
-
|
|
5
|
-
import jwt
|
|
6
|
-
import pytest
|
|
7
|
-
import responses
|
|
8
|
-
|
|
9
|
-
from ibm_cloud_sdk_core import IAMTokenManager, ApiException, get_authenticator_from_environment
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def get_access_token() -> str:
|
|
13
|
-
access_token_layout = {
|
|
14
|
-
"username": "dummy",
|
|
15
|
-
"role": "Admin",
|
|
16
|
-
"permissions": [
|
|
17
|
-
"administrator",
|
|
18
|
-
"manage_catalog"
|
|
19
|
-
],
|
|
20
|
-
"sub": "admin",
|
|
21
|
-
"iss": "sss",
|
|
22
|
-
"aud": "sss",
|
|
23
|
-
"uid": "sss",
|
|
24
|
-
"iat": 3600,
|
|
25
|
-
"exp": int(time.time())
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
access_token = jwt.encode(access_token_layout, 'secret', algorithm='HS256',
|
|
29
|
-
headers={'kid': '230498151c214b788dd97f22b85410a5'})
|
|
30
|
-
return access_token
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@responses.activate
|
|
34
|
-
def test_request_token_auth_default():
|
|
35
|
-
iam_url = "https://iam.cloud.ibm.com/identity/token"
|
|
36
|
-
response = """{
|
|
37
|
-
"access_token": "oAeisG8yqPY7sFR_x66Z15",
|
|
38
|
-
"token_type": "Bearer",
|
|
39
|
-
"expires_in": 3600,
|
|
40
|
-
"expiration": 1524167011,
|
|
41
|
-
"refresh_token": "jy4gl91BQ"
|
|
42
|
-
}"""
|
|
43
|
-
responses.add(responses.POST, url=iam_url, body=response, status=200)
|
|
44
|
-
|
|
45
|
-
token_manager = IAMTokenManager("apikey")
|
|
46
|
-
token_manager.request_token()
|
|
47
|
-
|
|
48
|
-
assert len(responses.calls) == 1
|
|
49
|
-
assert responses.calls[0].request.url == iam_url
|
|
50
|
-
assert responses.calls[0].request.headers.get('Authorization') is None
|
|
51
|
-
assert responses.calls[0].response.text == response
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
@responses.activate
|
|
55
|
-
def test_request_token_auth_in_ctor():
|
|
56
|
-
iam_url = "https://iam.cloud.ibm.com/identity/token"
|
|
57
|
-
response = """{
|
|
58
|
-
"access_token": "oAeisG8yqPY7sFR_x66Z15",
|
|
59
|
-
"token_type": "Bearer",
|
|
60
|
-
"expires_in": 3600,
|
|
61
|
-
"expiration": 1524167011,
|
|
62
|
-
"refresh_token": "jy4gl91BQ"
|
|
63
|
-
}"""
|
|
64
|
-
default_auth_header = 'Basic Yng6Yng='
|
|
65
|
-
responses.add(responses.POST, url=iam_url, body=response, status=200)
|
|
66
|
-
|
|
67
|
-
token_manager = IAMTokenManager(
|
|
68
|
-
"apikey", url=iam_url, client_id='foo', client_secret='bar')
|
|
69
|
-
token_manager.request_token()
|
|
70
|
-
|
|
71
|
-
assert len(responses.calls) == 1
|
|
72
|
-
assert responses.calls[0].request.url == iam_url
|
|
73
|
-
assert responses.calls[0].request.headers['Authorization'] != default_auth_header
|
|
74
|
-
assert responses.calls[0].response.text == response
|
|
75
|
-
assert 'scope' not in responses.calls[0].response.request.body
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
@responses.activate
|
|
79
|
-
def test_request_token_auth_in_ctor_with_scope():
|
|
80
|
-
iam_url = "https://iam.cloud.ibm.com/identity/token"
|
|
81
|
-
response = """{
|
|
82
|
-
"access_token": "oAeisG8yqPY7sFR_x66Z15",
|
|
83
|
-
"token_type": "Bearer",
|
|
84
|
-
"expires_in": 3600,
|
|
85
|
-
"expiration": 1524167011,
|
|
86
|
-
"refresh_token": "jy4gl91BQ"
|
|
87
|
-
}"""
|
|
88
|
-
default_auth_header = 'Basic Yng6Yng='
|
|
89
|
-
responses.add(responses.POST, url=iam_url, body=response, status=200)
|
|
90
|
-
|
|
91
|
-
token_manager = IAMTokenManager(
|
|
92
|
-
"apikey", url=iam_url, client_id='foo', client_secret='bar', scope='john snow')
|
|
93
|
-
token_manager.request_token()
|
|
94
|
-
|
|
95
|
-
assert len(responses.calls) == 1
|
|
96
|
-
assert responses.calls[0].request.url == iam_url
|
|
97
|
-
assert responses.calls[0].request.headers['Authorization'] != default_auth_header
|
|
98
|
-
assert responses.calls[0].response.text == response
|
|
99
|
-
assert 'scope=john+snow' in responses.calls[0].response.request.body
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
@responses.activate
|
|
103
|
-
def test_request_token_unsuccessful():
|
|
104
|
-
iam_url = "https://iam.cloud.ibm.com/identity/token"
|
|
105
|
-
response = """{
|
|
106
|
-
"context": {
|
|
107
|
-
"requestId": "38a0e9c226d94764820d92aa623eb0f6",
|
|
108
|
-
"requestType": "incoming.Identity_Token",
|
|
109
|
-
"userAgent": "ibm-python-sdk-core-1.0.0",
|
|
110
|
-
"url": "https://iam.cloud.ibm.com",
|
|
111
|
-
"instanceId": "iamid-4.5-6788-90b137c-75f48695b5-kl4wx",
|
|
112
|
-
"threadId": "169de5",
|
|
113
|
-
"host": "iamid-4.5-6788-90b137c-75f48695b5-kl4wx",
|
|
114
|
-
"startTime": "29.10.2019 12:31:00:300 GMT",
|
|
115
|
-
"endTime": "29.10.2019 12:31:00:381 GMT",
|
|
116
|
-
"elapsedTime": "81",
|
|
117
|
-
"locale": "en_US",
|
|
118
|
-
"clusterName": "iam-id-prdal12-8brn"
|
|
119
|
-
},
|
|
120
|
-
"errorCode": "BXNIM0415E",
|
|
121
|
-
"errorMessage": "Provided API key could not be found"
|
|
122
|
-
}
|
|
123
|
-
"""
|
|
124
|
-
responses.add(responses.POST, url=iam_url, body=response, status=400)
|
|
125
|
-
|
|
126
|
-
token_manager = IAMTokenManager("apikey")
|
|
127
|
-
with pytest.raises(ApiException):
|
|
128
|
-
token_manager.request_token()
|
|
129
|
-
|
|
130
|
-
assert len(responses.calls) == 1
|
|
131
|
-
assert responses.calls[0].request.url == iam_url
|
|
132
|
-
assert responses.calls[0].response.text == response
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
@responses.activate
|
|
136
|
-
def test_request_token_auth_in_ctor_client_id_only():
|
|
137
|
-
iam_url = "https://iam.cloud.ibm.com/identity/token"
|
|
138
|
-
response = """{
|
|
139
|
-
"access_token": "oAeisG8yqPY7sFR_x66Z15",
|
|
140
|
-
"token_type": "Bearer",
|
|
141
|
-
"expires_in": 3600,
|
|
142
|
-
"expiration": 1524167011,
|
|
143
|
-
"refresh_token": "jy4gl91BQ"
|
|
144
|
-
}"""
|
|
145
|
-
responses.add(responses.POST, url=iam_url, body=response, status=200)
|
|
146
|
-
|
|
147
|
-
token_manager = IAMTokenManager("iam_apikey", url=iam_url, client_id='foo')
|
|
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.get('Authorization') is None
|
|
153
|
-
assert responses.calls[0].response.text == response
|
|
154
|
-
assert 'scope' not in responses.calls[0].response.request.body
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
@responses.activate
|
|
158
|
-
def test_request_token_auth_in_ctor_secret_only():
|
|
159
|
-
iam_url = "https://iam.cloud.ibm.com/identity/token"
|
|
160
|
-
response = """{
|
|
161
|
-
"access_token": "oAeisG8yqPY7sFR_x66Z15",
|
|
162
|
-
"token_type": "Bearer",
|
|
163
|
-
"expires_in": 3600,
|
|
164
|
-
"expiration": 1524167011,
|
|
165
|
-
"refresh_token": "jy4gl91BQ"
|
|
166
|
-
}"""
|
|
167
|
-
responses.add(responses.POST, url=iam_url, body=response, status=200)
|
|
168
|
-
|
|
169
|
-
token_manager = IAMTokenManager(
|
|
170
|
-
"iam_apikey", url=iam_url, client_id=None, client_secret='bar')
|
|
171
|
-
token_manager.request_token()
|
|
172
|
-
|
|
173
|
-
assert len(responses.calls) == 1
|
|
174
|
-
assert responses.calls[0].request.url == iam_url
|
|
175
|
-
assert responses.calls[0].request.headers.get('Authorization') is None
|
|
176
|
-
assert responses.calls[0].response.text == response
|
|
177
|
-
assert 'scope' not in responses.calls[0].response.request.body
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
@responses.activate
|
|
181
|
-
def test_request_token_auth_in_setter():
|
|
182
|
-
iam_url = "https://iam.cloud.ibm.com/identity/token"
|
|
183
|
-
response = """{
|
|
184
|
-
"access_token": "oAeisG8yqPY7sFR_x66Z15",
|
|
185
|
-
"token_type": "Bearer",
|
|
186
|
-
"expires_in": 3600,
|
|
187
|
-
"expiration": 1524167011,
|
|
188
|
-
"refresh_token": "jy4gl91BQ"
|
|
189
|
-
}"""
|
|
190
|
-
default_auth_header = 'Basic Yng6Yng='
|
|
191
|
-
responses.add(responses.POST, url=iam_url, body=response, status=200)
|
|
192
|
-
|
|
193
|
-
token_manager = IAMTokenManager("iam_apikey")
|
|
194
|
-
token_manager.set_client_id_and_secret('foo', 'bar')
|
|
195
|
-
token_manager.request_token()
|
|
196
|
-
|
|
197
|
-
assert len(responses.calls) == 1
|
|
198
|
-
assert responses.calls[0].request.url == iam_url
|
|
199
|
-
assert responses.calls[0].request.headers['Authorization'] != default_auth_header
|
|
200
|
-
assert responses.calls[0].response.text == response
|
|
201
|
-
assert 'scope' not in responses.calls[0].response.request.body
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
@responses.activate
|
|
205
|
-
def test_request_token_auth_in_setter_client_id_only():
|
|
206
|
-
iam_url = "https://iam.cloud.ibm.com/identity/token"
|
|
207
|
-
response = """{
|
|
208
|
-
"access_token": "oAeisG8yqPY7sFR_x66Z15",
|
|
209
|
-
"token_type": "Bearer",
|
|
210
|
-
"expires_in": 3600,
|
|
211
|
-
"expiration": 1524167011,
|
|
212
|
-
"refresh_token": "jy4gl91BQ"
|
|
213
|
-
}"""
|
|
214
|
-
responses.add(responses.POST, url=iam_url, body=response, status=200)
|
|
215
|
-
|
|
216
|
-
token_manager = IAMTokenManager("iam_apikey")
|
|
217
|
-
token_manager.set_client_id_and_secret('foo', None)
|
|
218
|
-
token_manager.request_token()
|
|
219
|
-
|
|
220
|
-
assert len(responses.calls) == 1
|
|
221
|
-
assert responses.calls[0].request.url == iam_url
|
|
222
|
-
assert responses.calls[0].request.headers.get('Authorization') is None
|
|
223
|
-
assert responses.calls[0].response.text == response
|
|
224
|
-
assert 'scope' not in responses.calls[0].response.request.body
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
@responses.activate
|
|
228
|
-
def test_request_token_auth_in_setter_secret_only():
|
|
229
|
-
iam_url = "https://iam.cloud.ibm.com/identity/token"
|
|
230
|
-
response = """{
|
|
231
|
-
"access_token": "oAeisG8yqPY7sFR_x66Z15",
|
|
232
|
-
"token_type": "Bearer",
|
|
233
|
-
"expires_in": 3600,
|
|
234
|
-
"expiration": 1524167011,
|
|
235
|
-
"refresh_token": "jy4gl91BQ"
|
|
236
|
-
}"""
|
|
237
|
-
responses.add(responses.POST, url=iam_url, body=response, status=200)
|
|
238
|
-
|
|
239
|
-
token_manager = IAMTokenManager("iam_apikey")
|
|
240
|
-
token_manager.set_client_id_and_secret(None, 'bar')
|
|
241
|
-
token_manager.set_headers({'user': 'header'})
|
|
242
|
-
token_manager.request_token()
|
|
243
|
-
|
|
244
|
-
assert len(responses.calls) == 1
|
|
245
|
-
assert responses.calls[0].request.url == iam_url
|
|
246
|
-
assert responses.calls[0].request.headers.get('Authorization') is None
|
|
247
|
-
assert responses.calls[0].response.text == response
|
|
248
|
-
assert 'scope' not in responses.calls[0].response.request.body
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
@responses.activate
|
|
252
|
-
def test_request_token_auth_in_setter_scope():
|
|
253
|
-
iam_url = "https://iam.cloud.ibm.com/identity/token"
|
|
254
|
-
response = """{
|
|
255
|
-
"access_token": "oAeisG8yqPY7sFR_x66Z15",
|
|
256
|
-
"token_type": "Bearer",
|
|
257
|
-
"expires_in": 3600,
|
|
258
|
-
"expiration": 1524167011,
|
|
259
|
-
"refresh_token": "jy4gl91BQ"
|
|
260
|
-
}"""
|
|
261
|
-
responses.add(responses.POST, url=iam_url, body=response, status=200)
|
|
262
|
-
|
|
263
|
-
token_manager = IAMTokenManager("iam_apikey")
|
|
264
|
-
token_manager.set_client_id_and_secret(None, 'bar')
|
|
265
|
-
token_manager.set_headers({'user': 'header'})
|
|
266
|
-
token_manager.set_scope('john snow')
|
|
267
|
-
token_manager.request_token()
|
|
268
|
-
|
|
269
|
-
assert len(responses.calls) == 1
|
|
270
|
-
assert responses.calls[0].request.url == iam_url
|
|
271
|
-
assert responses.calls[0].request.headers.get('Authorization') is None
|
|
272
|
-
assert responses.calls[0].response.text == response
|
|
273
|
-
assert 'scope=john+snow' in responses.calls[0].response.request.body
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
@responses.activate
|
|
277
|
-
def test_get_refresh_token():
|
|
278
|
-
iam_url = "https://iam.cloud.ibm.com/identity/token"
|
|
279
|
-
access_token_str = get_access_token()
|
|
280
|
-
response = """{
|
|
281
|
-
"access_token": "%s",
|
|
282
|
-
"token_type": "Bearer",
|
|
283
|
-
"expires_in": 3600,
|
|
284
|
-
"expiration": 1524167011,
|
|
285
|
-
"refresh_token": "jy4gl91BQ"
|
|
286
|
-
}""" % (access_token_str)
|
|
287
|
-
responses.add(responses.POST, url=iam_url, body=response, status=200)
|
|
288
|
-
|
|
289
|
-
token_manager = IAMTokenManager("iam_apikey")
|
|
290
|
-
token_manager.get_token()
|
|
291
|
-
|
|
292
|
-
assert len(responses.calls) == 2
|
|
293
|
-
assert token_manager.refresh_token == "jy4gl91BQ"
|
|
294
|
-
|
|
295
|
-
#
|
|
296
|
-
# In order to run the following integration test with a live IAM server:
|
|
297
|
-
#
|
|
298
|
-
# 1. Create file "iamtest.env" in the project root.
|
|
299
|
-
# It should look like this:
|
|
300
|
-
# IAMTEST1_AUTH_URL=<url> e.g. https://iam.cloud.ibm.com
|
|
301
|
-
# IAMTEST1_AUTH_TYPE=iam
|
|
302
|
-
# IAMTEST1_APIKEY=<apikey>
|
|
303
|
-
# IAMTEST2_AUTH_URL=<url> e.g. https://iam.test.cloud.ibm.com
|
|
304
|
-
# IAMTEST2_AUTH_TYPE=iam
|
|
305
|
-
# IAMTEST2_APIKEY=<apikey>
|
|
306
|
-
# IAMTEST2_CLIENT_ID=<client id>
|
|
307
|
-
# IAMTEST2_CLIENT_SECRET=<client secret>
|
|
308
|
-
#
|
|
309
|
-
# 2. Comment out the "@pytest.mark.skip" decorator below.
|
|
310
|
-
#
|
|
311
|
-
# 3. Run this command:
|
|
312
|
-
# python3 -m pytest -s test -k "test_iam_live_token_server"
|
|
313
|
-
# (or just run tests like normal and this test function will be invoked)
|
|
314
|
-
#
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
@pytest.mark.skip(reason="avoid integration test in automated builds")
|
|
318
|
-
def test_iam_live_token_server():
|
|
319
|
-
# Get two iam authenticators from the environment.
|
|
320
|
-
# "iamtest1" uses the production IAM token server
|
|
321
|
-
# "iamtest2" uses the staging IAM token server
|
|
322
|
-
os.environ['IBM_CREDENTIALS_FILE'] = "iamtest.env"
|
|
323
|
-
|
|
324
|
-
# Test "iamtest1" service
|
|
325
|
-
auth1 = get_authenticator_from_environment("iamtest1")
|
|
326
|
-
assert auth1 is not None
|
|
327
|
-
assert auth1.token_manager is not None
|
|
328
|
-
assert auth1.token_manager.url is not None
|
|
329
|
-
|
|
330
|
-
request = {'method': "GET"}
|
|
331
|
-
request["url"] = ""
|
|
332
|
-
request["headers"] = {}
|
|
333
|
-
|
|
334
|
-
assert auth1.token_manager.refresh_token is None
|
|
335
|
-
|
|
336
|
-
auth1.authenticate(request)
|
|
337
|
-
|
|
338
|
-
assert request.get("headers") is not None
|
|
339
|
-
assert request["headers"].get("Authorization") is not None
|
|
340
|
-
assert "Bearer " in request["headers"].get("Authorization")
|
|
341
|
-
|
|
342
|
-
# Test "iamtest2" service
|
|
343
|
-
auth2 = get_authenticator_from_environment("iamtest2")
|
|
344
|
-
assert auth2 is not None
|
|
345
|
-
assert auth2.token_manager is not None
|
|
346
|
-
assert auth2.token_manager.url is not None
|
|
347
|
-
|
|
348
|
-
request = {'method': "GET"}
|
|
349
|
-
request["url"] = ""
|
|
350
|
-
request["headers"] = {}
|
|
351
|
-
|
|
352
|
-
assert auth2.token_manager.refresh_token is None
|
|
353
|
-
|
|
354
|
-
auth2.authenticate(request)
|
|
355
|
-
|
|
356
|
-
assert auth2.token_manager.refresh_token is not None
|
|
357
|
-
|
|
358
|
-
assert request.get("headers") is not None
|
|
359
|
-
assert request["headers"].get("Authorization") is not None
|
|
360
|
-
assert "Bearer " in request["headers"].get("Authorization")
|
|
361
|
-
|
|
362
|
-
# print('Refresh token: ', auth2.token_manager.refresh_token)
|
test/test_jwt_token_manager.py
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
# pylint: disable=missing-docstring,protected-access,abstract-class-instantiated
|
|
2
|
-
import time
|
|
3
|
-
import threading
|
|
4
|
-
from typing import Optional
|
|
5
|
-
|
|
6
|
-
import jwt
|
|
7
|
-
import pytest
|
|
8
|
-
|
|
9
|
-
from ibm_cloud_sdk_core import JWTTokenManager, DetailedResponse
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class JWTTokenManagerMockImpl(JWTTokenManager):
|
|
13
|
-
def __init__(self, url: Optional[str] = None, access_token: Optional[str] = None) -> None:
|
|
14
|
-
self.url = url
|
|
15
|
-
self.access_token = access_token
|
|
16
|
-
self.request_count = 0 # just for tests to see how many times request was called
|
|
17
|
-
super().__init__(url, disable_ssl_verification=access_token,
|
|
18
|
-
token_name='access_token')
|
|
19
|
-
|
|
20
|
-
def request_token(self) -> DetailedResponse:
|
|
21
|
-
self.request_count += 1
|
|
22
|
-
current_time = int(time.time())
|
|
23
|
-
token_layout = {
|
|
24
|
-
"username": "dummy",
|
|
25
|
-
"role": "Admin",
|
|
26
|
-
"permissions": [
|
|
27
|
-
"administrator",
|
|
28
|
-
"manage_catalog"
|
|
29
|
-
],
|
|
30
|
-
"sub": "admin",
|
|
31
|
-
"iss": "sss",
|
|
32
|
-
"aud": "sss",
|
|
33
|
-
"uid": "sss",
|
|
34
|
-
"iat": current_time,
|
|
35
|
-
"exp": current_time + 3600
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
access_token = jwt.encode(token_layout, 'secret', algorithm='HS256',
|
|
39
|
-
headers={'kid': '230498151c214b788dd97f22b85410a5'})
|
|
40
|
-
response = {"access_token": access_token,
|
|
41
|
-
"token_type": "Bearer",
|
|
42
|
-
"expires_in": 3600,
|
|
43
|
-
"expiration": current_time + 3600,
|
|
44
|
-
"refresh_token": "jy4gl91BQ",
|
|
45
|
-
"from_token_manager": True
|
|
46
|
-
}
|
|
47
|
-
time.sleep(0.5)
|
|
48
|
-
return response
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def _get_current_time() -> int:
|
|
52
|
-
return int(time.time())
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def test_get_token():
|
|
56
|
-
url = "https://iam.cloud.ibm.com/identity/token"
|
|
57
|
-
token_manager = JWTTokenManagerMockImpl(url)
|
|
58
|
-
old_token = token_manager.get_token()
|
|
59
|
-
assert token_manager.token_info.get('expires_in') == 3600
|
|
60
|
-
assert token_manager._is_token_expired() is False
|
|
61
|
-
|
|
62
|
-
token_manager.token_info = {"access_token": "old_dummy",
|
|
63
|
-
"token_type": "Bearer",
|
|
64
|
-
"expires_in": 3600,
|
|
65
|
-
"expiration": time.time(),
|
|
66
|
-
"refresh_token": "jy4gl91BQ"
|
|
67
|
-
}
|
|
68
|
-
token = token_manager.get_token()
|
|
69
|
-
assert token == old_token
|
|
70
|
-
|
|
71
|
-
# expired token:
|
|
72
|
-
token_manager.expire_time = _get_current_time() - 300
|
|
73
|
-
token = token_manager.get_token()
|
|
74
|
-
assert token != "old_dummy"
|
|
75
|
-
assert token_manager.request_count == 2
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def test_paced_get_token():
|
|
79
|
-
url = "https://iam.cloud.ibm.com/identity/token"
|
|
80
|
-
token_manager = JWTTokenManagerMockImpl(url)
|
|
81
|
-
threads = []
|
|
82
|
-
for _ in range(10):
|
|
83
|
-
thread = threading.Thread(target=token_manager.get_token)
|
|
84
|
-
thread.start()
|
|
85
|
-
threads.append(thread)
|
|
86
|
-
for thread in threads:
|
|
87
|
-
thread.join()
|
|
88
|
-
assert token_manager.request_count == 1
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
def test_is_token_expired():
|
|
92
|
-
token_manager = JWTTokenManagerMockImpl(None, access_token=None)
|
|
93
|
-
assert token_manager._is_token_expired() is True
|
|
94
|
-
token_manager.expire_time = _get_current_time() + 3600
|
|
95
|
-
assert token_manager._is_token_expired() is False
|
|
96
|
-
token_manager.expire_time = _get_current_time() - 3600
|
|
97
|
-
assert token_manager._is_token_expired()
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def test_abstract_class_instantiation():
|
|
101
|
-
with pytest.raises(TypeError) as err:
|
|
102
|
-
JWTTokenManager(None)
|
|
103
|
-
assert str(err.value).startswith("Can't instantiate abstract class JWTTokenManager with abstract")
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def test_disable_ssl_verification():
|
|
107
|
-
token_manager = JWTTokenManagerMockImpl('https://iam.cloud.ibm.com/identity/token')
|
|
108
|
-
token_manager.set_disable_ssl_verification(True)
|
|
109
|
-
assert token_manager.disable_ssl_verification is True
|