Skip to content

Commit e0aeed7

Browse files
authored
refactor: use shared code for IAM based authenticators (#118)
This commit makes 3 main things: - Refactors the common IAM related functionalities into a shared class to make the implementation of the upcoming IAM based authenticators easier - Restructures the token managers into a single directory to make the project structure a bit cleaner. - Improves the code style, by reordering/grouping the imports and adding missing blank lines (PEP8).
1 parent 814a596 commit e0aeed7

32 files changed

+312
-137
lines changed

ibm_cloud_sdk_core/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@
3838

3939
from .base_service import BaseService
4040
from .detailed_response import DetailedResponse
41-
from .iam_token_manager import IAMTokenManager
42-
from .jwt_token_manager import JWTTokenManager
43-
from .cp4d_token_manager import CP4DTokenManager
41+
from .token_managers.iam_token_manager import IAMTokenManager
42+
from .token_managers.jwt_token_manager import JWTTokenManager
43+
from .token_managers.cp4d_token_manager import CP4DTokenManager
4444
from .api_exception import ApiException
4545
from .utils import datetime_to_string, string_to_datetime, read_external_sources
4646
from .utils import datetime_to_string_list, string_to_datetime_list

ibm_cloud_sdk_core/api_exception.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
16-
from typing import Optional
16+
1717
from http import HTTPStatus
18+
from typing import Optional
19+
1820
from requests import Response
1921

2022

ibm_cloud_sdk_core/authenticators/authenticator.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from abc import ABC, abstractmethod
1818

19+
1920
class Authenticator(ABC):
2021
"""This interface defines the common methods and constants associated with an Authenticator implementation."""
2122
@abstractmethod

ibm_cloud_sdk_core/authenticators/bearer_token_authenticator.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from .authenticator import Authenticator
2020

21+
2122
class BearerTokenAuthenticator(Authenticator):
2223
"""The BearerTokenAuthenticator will add a user-supplied bearer token
2324
to requests.

ibm_cloud_sdk_core/authenticators/cp4d_authenticator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from requests import Request
2020

2121
from .authenticator import Authenticator
22-
from ..cp4d_token_manager import CP4DTokenManager
22+
from ..token_managers.cp4d_token_manager import CP4DTokenManager
2323
from ..utils import has_bad_first_or_last_char
2424

2525

ibm_cloud_sdk_core/authenticators/iam_authenticator.py

Lines changed: 6 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@
1616

1717
from typing import Dict, Optional
1818

19-
from requests import Request
20-
21-
from .authenticator import Authenticator
22-
from ..iam_token_manager import IAMTokenManager
19+
from .iam_request_based_authenticator import IAMRequestBasedAuthenticator
20+
from ..token_managers.iam_token_manager import IAMTokenManager
2321
from ..utils import has_bad_first_or_last_char
2422

25-
class IAMAuthenticator(Authenticator):
23+
24+
class IAMAuthenticator(IAMRequestBasedAuthenticator):
2625
"""The IAMAuthenticator utilizes an apikey, or client_id and client_secret pair to
2726
obtain a suitable bearer token, and adds it to requests.
2827
@@ -81,81 +80,12 @@ def validate(self) -> None:
8180
Raises:
8281
ValueError: The apikey, client_id, and/or client_secret are not valid for IAM token requests.
8382
"""
83+
super().validate()
84+
8485
if self.token_manager.apikey is None:
8586
raise ValueError('The apikey shouldn\'t be None.')
8687

8788
if has_bad_first_or_last_char(self.token_manager.apikey):
8889
raise ValueError(
8990
'The apikey shouldn\'t start or end with curly brackets or quotes. '
9091
'Please remove any surrounding {, }, or \" characters.')
91-
92-
if (self.token_manager.client_id and
93-
not self.token_manager.client_secret) or (
94-
not self.token_manager.client_id and
95-
self.token_manager.client_secret):
96-
raise ValueError(
97-
'Both client_id and client_secret should be initialized.')
98-
99-
def authenticate(self, req: Request) -> None:
100-
"""Adds IAM authentication information to the request.
101-
102-
The IAM bearer token will be added to the request's headers in the form:
103-
104-
Authorization: Bearer <bearer-token>
105-
106-
Args:
107-
req: The request to add IAM authentication information too. Must contain a key to a dictionary
108-
called headers.
109-
"""
110-
headers = req.get('headers')
111-
bearer_token = self.token_manager.get_token()
112-
headers['Authorization'] = 'Bearer {0}'.format(bearer_token)
113-
114-
def set_client_id_and_secret(self, client_id: str, client_secret: str) -> None:
115-
"""Set the client_id and client_secret pair the token manager will use for IAM token requests.
116-
117-
Args:
118-
client_id: The client id to be used in basic auth.
119-
client_secret: The client secret to be used in basic auth.
120-
121-
Raises:
122-
ValueError: The apikey, client_id, and/or client_secret are not valid for IAM token requests.
123-
"""
124-
self.token_manager.set_client_id_and_secret(client_id, client_secret)
125-
self.validate()
126-
127-
def set_disable_ssl_verification(self, status: bool = False) -> None:
128-
"""Set the flag that indicates whether verification of the server's SSL certificate should be
129-
disabled or not. Defaults to False.
130-
131-
Keyword Arguments:
132-
status: Headers to be sent with every IAM token request. Defaults to None.
133-
"""
134-
self.token_manager.set_disable_ssl_verification(status)
135-
136-
def set_headers(self, headers: Dict[str, str]) -> None:
137-
"""Headers to be sent with every IAM token request.
138-
139-
Args:
140-
headers: Headers to be sent with every IAM token request.
141-
"""
142-
self.token_manager.set_headers(headers)
143-
144-
def set_proxies(self, proxies: Dict[str, str]) -> None:
145-
"""Sets the proxies the token manager will use to communicate with IAM on behalf of the host.
146-
147-
Args:
148-
proxies: Dictionary for mapping request protocol to proxy URL.
149-
proxies.http (optional): The proxy endpoint to use for HTTP requests.
150-
proxies.https (optional): The proxy endpoint to use for HTTPS requests.
151-
"""
152-
self.token_manager.set_proxies(proxies)
153-
154-
def set_scope(self, value: str) -> None:
155-
"""Sets the "scope" parameter to use when fetching the bearer token from the IAM token server.
156-
This can be used to obtain an access token with a specific scope.
157-
158-
Args:
159-
value: A space seperated string that makes up the scope parameter.
160-
"""
161-
self.token_manager.set_scope(value)
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# coding: utf-8
2+
3+
# Copyright 2019 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
18+
19+
from requests import Request
20+
21+
from .authenticator import Authenticator
22+
23+
24+
class IAMRequestBasedAuthenticator(Authenticator):
25+
"""The IAMRequestBasedAuthenticator class contains code that is common to all authenticators
26+
that need to interact with the IAM tokens service to obtain an access token.
27+
28+
The bearer token will be sent as an Authorization header in the form:
29+
30+
Authorization: Bearer <bearer-token>
31+
32+
Attributes:
33+
token_manager (TokenManager): Retrives and manages IAM tokens from the endpoint specified by the url.
34+
"""
35+
36+
def validate(self) -> None:
37+
"""Validates the client_id, and client_secret for IAM token requests.
38+
39+
Ensure both the client_id and client_secret are set if either of them are defined.
40+
41+
Raises:
42+
ValueError: The client_id, and/or client_secret are not valid for IAM token requests.
43+
"""
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.')
50+
51+
def authenticate(self, req: Request) -> None:
52+
"""Adds IAM authentication information to the request.
53+
54+
The IAM bearer token will be added to the request's headers in the form:
55+
56+
Authorization: Bearer <bearer-token>
57+
58+
Args:
59+
req: The request to add IAM authentication information too. Must contain a key to a dictionary
60+
called headers.
61+
"""
62+
headers = req.get('headers')
63+
bearer_token = self.token_manager.get_token()
64+
headers['Authorization'] = 'Bearer {0}'.format(bearer_token)
65+
66+
def set_client_id_and_secret(self, client_id: str, client_secret: str) -> None:
67+
"""Set the client_id and client_secret pair the token manager will use for IAM token requests.
68+
69+
Args:
70+
client_id: The client id to be used in basic auth.
71+
client_secret: The client secret to be used in basic auth.
72+
73+
Raises:
74+
ValueError: The apikey, client_id, and/or client_secret are not valid for IAM token requests.
75+
"""
76+
self.token_manager.set_client_id_and_secret(client_id, client_secret)
77+
self.validate()
78+
79+
def set_disable_ssl_verification(self, status: bool = False) -> None:
80+
"""Set the flag that indicates whether verification of the server's SSL certificate should be
81+
disabled or not. Defaults to False.
82+
83+
Keyword Arguments:
84+
status: Headers to be sent with every IAM token request. Defaults to None.
85+
"""
86+
self.token_manager.set_disable_ssl_verification(status)
87+
88+
def set_headers(self, headers: Dict[str, str]) -> None:
89+
"""Headers to be sent with every IAM token request.
90+
91+
Args:
92+
headers: Headers to be sent with every IAM token request.
93+
"""
94+
self.token_manager.set_headers(headers)
95+
96+
def set_proxies(self, proxies: Dict[str, str]) -> None:
97+
"""Sets the proxies the token manager will use to communicate with IAM on behalf of the host.
98+
99+
Args:
100+
proxies: Dictionary for mapping request protocol to proxy URL.
101+
proxies.http (optional): The proxy endpoint to use for HTTP requests.
102+
proxies.https (optional): The proxy endpoint to use for HTTPS requests.
103+
"""
104+
self.token_manager.set_proxies(proxies)
105+
106+
def set_scope(self, value: str) -> None:
107+
"""Sets the "scope" parameter to use when fetching the bearer token from the IAM token server.
108+
This can be used to obtain an access token with a specific scope.
109+
110+
Args:
111+
value: A space seperated string that makes up the scope parameter.
112+
"""
113+
self.token_manager.set_scope(value)

ibm_cloud_sdk_core/authenticators/no_auth_authenticator.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from .authenticator import Authenticator
1818

19+
1920
class NoAuthAuthenticator(Authenticator):
2021
"""Performs no authentication."""
2122
authentication_type = 'noAuth'

ibm_cloud_sdk_core/base_service.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,27 @@
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
1616

17-
import logging
18-
from http.cookiejar import CookieJar
17+
import gzip
1918
import json as json_import
20-
from os.path import basename
19+
import logging
2120
import platform
2221
import sys
23-
import gzip
22+
from http.cookiejar import CookieJar
23+
from os.path import basename
2424
from typing import Dict, List, Optional, Tuple, Union
25+
from urllib3.util.retry import Retry
2526

2627
import requests
2728
from requests.adapters import HTTPAdapter
2829
from requests.structures import CaseInsensitiveDict
29-
from urllib3.util.retry import Retry
30+
3031
from ibm_cloud_sdk_core.authenticators import Authenticator
31-
from .version import __version__
32+
from .api_exception import ApiException
33+
from .detailed_response import DetailedResponse
34+
from .token_managers.token_manager import TokenManager
3235
from .utils import (has_bad_first_or_last_char, remove_null_values,
3336
cleanup_values, read_external_sources, strip_extra_slashes)
34-
from .detailed_response import DetailedResponse
35-
from .api_exception import ApiException
36-
from .token_manager import TokenManager
37+
from .version import __version__
3738

3839
# Uncomment this to enable http debugging
3940
# import http.client as http_client

ibm_cloud_sdk_core/detailed_response.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
1616

17+
import json
1718
from typing import Dict, Optional
1819

19-
import json
2020
import requests
2121

22+
2223
class DetailedResponse:
2324
"""Custom class for detailed response returned from APIs.
2425

ibm_cloud_sdk_core/get_authenticator.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
CloudPakForDataAuthenticator, IAMAuthenticator, NoAuthAuthenticator)
1919
from .utils import read_external_sources
2020

21+
2122
def get_authenticator_from_environment(service_name: str) -> Authenticator:
2223
"""Look for external configuration of authenticator.
2324
@@ -38,6 +39,7 @@ def get_authenticator_from_environment(service_name: str) -> Authenticator:
3839
authenticator = __construct_authenticator(config)
3940
return authenticator
4041

42+
4143
def __construct_authenticator(config: dict) -> Authenticator:
4244
auth_type = config.get('AUTH_TYPE').lower() if config.get('AUTH_TYPE') else 'iam'
4345
authenticator = None
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# coding: utf-8
2+
3+
# Copyright 2021 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.

ibm_cloud_sdk_core/cp4d_token_manager.py renamed to ibm_cloud_sdk_core/token_managers/cp4d_token_manager.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import json
1818
from typing import Dict, Optional
19+
1920
from .jwt_token_manager import JWTTokenManager
2021

2122

0 commit comments

Comments
 (0)