Skip to content

Commit c45f65a

Browse files
committed
feat: add authentication_type method to authenticators
This commit adds a new method ('authentication_type()') to the Authenticator base class, and each authenticator implementation (subclasses of Authenticator). The function will return a string indicating the authenticator type ('basic', 'iam', etc.). This brings the python core in line with the Go, Java and Node cores.
1 parent 52ab020 commit c45f65a

17 files changed

+166
-60
lines changed

.secrets.baseline

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"files": "package-lock.json|^.secrets.baseline$",
44
"lines": null
55
},
6-
"generated_at": "2021-10-13T21:20:56Z",
6+
"generated_at": "2021-10-15T20:17:29Z",
77
"plugins_used": [
88
{
99
"name": "AWSKeyDetector"
@@ -91,6 +91,16 @@
9191
"verified_result": null
9292
}
9393
],
94+
"ibm_cloud_sdk_core/authenticators/authenticator.py": [
95+
{
96+
"hashed_secret": "fdee05598fdd57ff8e9ae29e92c25a04f2c52fa6",
97+
"is_secret": false,
98+
"is_verified": false,
99+
"line_number": 29,
100+
"type": "Secret Keyword",
101+
"verified_result": null
102+
}
103+
],
94104
"resources/ibm-credentials-basic.env": [
95105
{
96106
"hashed_secret": "5eb942810a75ebc850972a89285d570d484c89c4",
@@ -202,7 +212,7 @@
202212
"hashed_secret": "37e94c31b6a756ba2afd2fe9a9765172cd79ac47",
203213
"is_secret": false,
204214
"is_verified": false,
205-
"line_number": 102,
215+
"line_number": 103,
206216
"type": "Secret Keyword",
207217
"verified_result": null
208218
}
@@ -246,15 +256,15 @@
246256
"hashed_secret": "5eb942810a75ebc850972a89285d570d484c89c4",
247257
"is_secret": false,
248258
"is_verified": false,
249-
"line_number": 96,
259+
"line_number": 97,
250260
"type": "Secret Keyword",
251261
"verified_result": null
252262
},
253263
{
254264
"hashed_secret": "da2f27d2c57a0e1ed2dc3a34b4ef02faf2f7a4c2",
255265
"is_secret": false,
256266
"is_verified": false,
257-
"line_number": 135,
267+
"line_number": 136,
258268
"type": "Hex High Entropy String",
259269
"verified_result": null
260270
}
@@ -274,23 +284,23 @@
274284
"hashed_secret": "4080eeeaf54faf879b9e8d99c49a8503f7e855bb",
275285
"is_secret": false,
276286
"is_verified": false,
277-
"line_number": 70,
287+
"line_number": 73,
278288
"type": "Secret Keyword",
279289
"verified_result": null
280290
},
281291
{
282292
"hashed_secret": "37e94c31b6a756ba2afd2fe9a9765172cd79ac47",
283293
"is_secret": false,
284294
"is_verified": false,
285-
"line_number": 93,
295+
"line_number": 96,
286296
"type": "Secret Keyword",
287297
"verified_result": null
288298
},
289299
{
290300
"hashed_secret": "da2f27d2c57a0e1ed2dc3a34b4ef02faf2f7a4c2",
291301
"is_secret": false,
292302
"is_verified": false,
293-
"line_number": 118,
303+
"line_number": 121,
294304
"type": "Hex High Entropy String",
295305
"verified_result": null
296306
}
@@ -336,23 +346,23 @@
336346
"hashed_secret": "34a0a47a51d5bf739df0214450385e29ee7e9847",
337347
"is_secret": false,
338348
"is_verified": false,
339-
"line_number": 393,
349+
"line_number": 417,
340350
"type": "Secret Keyword",
341351
"verified_result": null
342352
},
343353
{
344354
"hashed_secret": "2863fa4b5510c46afc2bd2998dfbc0cf3d6df032",
345355
"is_secret": false,
346356
"is_verified": false,
347-
"line_number": 469,
357+
"line_number": 497,
348358
"type": "Secret Keyword",
349359
"verified_result": null
350360
},
351361
{
352362
"hashed_secret": "b9cad336062c0dc3bb30145b1a6697fccfe755a6",
353363
"is_secret": false,
354364
"is_verified": false,
355-
"line_number": 530,
365+
"line_number": 558,
356366
"type": "Secret Keyword",
357367
"verified_result": null
358368
}

ibm_cloud_sdk_core/authenticators/authenticator.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@
1919

2020
class Authenticator(ABC):
2121
"""This interface defines the common methods and constants associated with an Authenticator implementation."""
22+
23+
# Constants representing the various authenticator types.
24+
AUTHTYPE_BASIC = 'basic'
25+
AUTHTYPE_BEARERTOKEN = 'bearerToken'
26+
AUTHTYPE_IAM = 'iam'
27+
AUTHTYPE_CONTAINER = 'container'
28+
AUTHTYPE_CP4D = 'cp4d'
29+
AUTHTYPE_NOAUTH = 'noAuth'
30+
AUTHTYPE_UNKNOWN = 'unknown'
31+
2232
@abstractmethod
2333
def authenticate(self, req: dict) -> None:
2434
"""Perform the necessary authentication steps for the specified request.
@@ -40,3 +50,8 @@ def validate(self) -> None:
4050
To be implemented by subclasses.
4151
"""
4252
pass
53+
54+
# pylint: disable=R0201
55+
def authentication_type(self) -> str:
56+
"""Returns the authenticator's type. This method should be overridden by each authenticator implementation."""
57+
return Authenticator.AUTHTYPE_UNKNOWN

ibm_cloud_sdk_core/authenticators/basic_authenticator.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,16 @@ class BasicAuthenticator(Authenticator):
3535
Raises:
3636
ValueError: The username or password is not specified or contains invalid characters.
3737
"""
38-
authentication_type = 'basic'
3938

4039
def __init__(self, username: str, password: str) -> None:
4140
self.username = username
4241
self.password = password
4342
self.validate()
4443
self.authorization_header = self.__construct_basic_auth_header()
4544

45+
def authentication_type(self) -> str:
46+
"""Returns this authenticator's type ('basic')."""
47+
return Authenticator.AUTHTYPE_BASIC
4648

4749
def validate(self) -> None:
4850
"""Validate username and password.
@@ -61,13 +63,12 @@ def validate(self) -> None:
6163
'The username and password shouldn\'t start or end with curly brackets or quotes. '
6264
'Please remove any surrounding {, }, or \" characters.')
6365

64-
6566
def __construct_basic_auth_header(self) -> str:
6667
authstring = "{0}:{1}".format(self.username, self.password)
67-
base64_authorization = base64.b64encode(authstring.encode('utf-8')).decode('utf-8')
68+
base64_authorization = base64.b64encode(
69+
authstring.encode('utf-8')).decode('utf-8')
6870
return 'Basic {0}'.format(base64_authorization)
6971

70-
7172
def authenticate(self, req: Request) -> None:
7273
"""Add basic authentication information to a request.
7374

ibm_cloud_sdk_core/authenticators/bearer_token_authenticator.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,15 @@ class BearerTokenAuthenticator(Authenticator):
3333
Raises:
3434
ValueError: Bearer token is none.
3535
"""
36-
authentication_type = 'bearerToken'
3736

3837
def __init__(self, bearer_token: str) -> None:
3938
self.bearer_token = bearer_token
4039
self.validate()
4140

41+
def authentication_type(self) -> str:
42+
"""Returns this authenticator's type ('bearertoken')."""
43+
return Authenticator.AUTHTYPE_BEARERTOKEN
44+
4245
def validate(self) -> None:
4346
"""Validate the bearer token.
4447

ibm_cloud_sdk_core/authenticators/container_authenticator.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from .iam_request_based_authenticator import IAMRequestBasedAuthenticator
2020
from ..token_managers.container_token_manager import ContainerTokenManager
21+
from .authenticator import Authenticator
2122

2223

2324
class ContainerAuthenticator(IAMRequestBasedAuthenticator):
@@ -62,7 +63,6 @@ class ContainerAuthenticator(IAMRequestBasedAuthenticator):
6263
ValueError: Neither of iam_profile_name or iam_profile_idk are set,
6364
or client_id, and/or client_secret are not valid for IAM token requests.
6465
"""
65-
authentication_type = 'container'
6666

6767
def __init__(self,
6868
cr_token_filename: Optional[str] = None,
@@ -86,6 +86,10 @@ def __init__(self,
8686

8787
self.validate()
8888

89+
def authentication_type(self) -> str:
90+
"""Returns this authenticator's type ('container')."""
91+
return Authenticator.AUTHTYPE_CONTAINER
92+
8993
def validate(self) -> None:
9094
"""Validates the iam_profile_name, iam_profile_id, client_id, and client_secret for IAM token requests.
9195

ibm_cloud_sdk_core/authenticators/cp4d_authenticator.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ class CloudPakForDataAuthenticator(Authenticator):
5050
TypeError: The `disable_ssl_verification` is not a bool.
5151
ValueError: The username, password/apikey, and/or url are not valid for CP4D token requests.
5252
"""
53-
authenticationdict = 'cp4d'
5453

5554
def __init__(self,
5655
username: str = None,
@@ -71,6 +70,10 @@ def __init__(self,
7170

7271
self.validate()
7372

73+
def authentication_type(self) -> str:
74+
"""Returns this authenticator's type ('cp4d')."""
75+
return Authenticator.AUTHTYPE_CP4D
76+
7477
def validate(self) -> None:
7578
"""Validate username, password, and url for token requests.
7679
@@ -85,7 +88,8 @@ def validate(self) -> None:
8588

8689
if ((self.token_manager.password is None and self.token_manager.apikey is None)
8790
or (self.token_manager.password is not None and self.token_manager.apikey is not None)):
88-
raise ValueError('Exactly one of `apikey` or `password` must be specified.')
91+
raise ValueError(
92+
'Exactly one of `apikey` or `password` must be specified.')
8993

9094
if self.token_manager.url is None:
9195
raise ValueError('The url shouldn\'t be None.')

ibm_cloud_sdk_core/authenticators/iam_authenticator.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from typing import Dict, Optional
1818

19+
from .authenticator import Authenticator
1920
from .iam_request_based_authenticator import IAMRequestBasedAuthenticator
2021
from ..token_managers.iam_token_manager import IAMTokenManager
2122
from ..utils import has_bad_first_or_last_char
@@ -54,7 +55,6 @@ class IAMAuthenticator(IAMRequestBasedAuthenticator):
5455
TypeError: The `disable_ssl_verification` is not a bool.
5556
ValueError: The apikey, client_id, and/or client_secret are not valid for IAM token requests.
5657
"""
57-
authentication_type = 'iam'
5858

5959
def __init__(self,
6060
apikey: str,
@@ -77,6 +77,10 @@ def __init__(self,
7777

7878
self.validate()
7979

80+
def authentication_type(self) -> str:
81+
"""Returns this authenticator's type ('iam')."""
82+
return Authenticator.AUTHTYPE_IAM
83+
8084
def validate(self) -> None:
8185
"""Validates the apikey, client_id, and client_secret for IAM token requests.
8286

ibm_cloud_sdk_core/authenticators/no_auth_authenticator.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919

2020
class NoAuthAuthenticator(Authenticator):
2121
"""Performs no authentication."""
22-
authentication_type = 'noAuth'
22+
23+
def authentication_type(self) -> str:
24+
"""Returns this authenticator's type ('noauth')."""
25+
return Authenticator.AUTHTYPE_NOAUTH
2326

2427
def validate(self) -> None:
2528
pass

ibm_cloud_sdk_core/get_authenticator.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,23 +43,28 @@ def get_authenticator_from_environment(service_name: str) -> Authenticator:
4343
def __construct_authenticator(config: dict) -> Authenticator:
4444
# Determine the authentication type if not specified explicitly.
4545
if config.get('AUTH_TYPE'):
46-
auth_type = config.get('AUTH_TYPE').lower()
46+
auth_type = config.get('AUTH_TYPE')
4747
elif config.get('AUTHTYPE'):
48-
auth_type = config.get('AUTHTYPE').lower()
48+
auth_type = config.get('AUTHTYPE')
4949
else:
50+
# If authtype wasn't specified explicitly, then determine the default.
5051
# If the APIKEY property is specified, then it should be IAM, otherwise Container Auth.
51-
auth_type = 'iam' if config.get('APIKEY') else 'container'
52+
if config.get('APIKEY'):
53+
auth_type = Authenticator.AUTHTYPE_IAM
54+
else:
55+
auth_type = Authenticator.AUTHTYPE_CONTAINER
5256

57+
auth_type = auth_type.lower()
5358
authenticator = None
5459

55-
if auth_type == 'basic':
60+
if auth_type == Authenticator.AUTHTYPE_BASIC.lower():
5661
authenticator = BasicAuthenticator(
5762
username=config.get('USERNAME'),
5863
password=config.get('PASSWORD'))
59-
elif auth_type == 'bearertoken':
64+
elif auth_type == Authenticator.AUTHTYPE_BEARERTOKEN.lower():
6065
authenticator = BearerTokenAuthenticator(
6166
bearer_token=config.get('BEARER_TOKEN'))
62-
elif auth_type == 'container':
67+
elif auth_type == Authenticator.AUTHTYPE_CONTAINER.lower():
6368
authenticator = ContainerAuthenticator(
6469
cr_token_filename=config.get('CR_TOKEN_FILENAME'),
6570
iam_profile_name=config.get('IAM_PROFILE_NAME'),
@@ -70,14 +75,14 @@ def __construct_authenticator(config: dict) -> Authenticator:
7075
disable_ssl_verification=config.get(
7176
'AUTH_DISABLE_SSL', 'false').lower() == 'true',
7277
scope=config.get('SCOPE'))
73-
elif auth_type == 'cp4d':
78+
elif auth_type == Authenticator.AUTHTYPE_CP4D.lower():
7479
authenticator = CloudPakForDataAuthenticator(
7580
username=config.get('USERNAME'),
7681
password=config.get('PASSWORD'),
7782
url=config.get('AUTH_URL'),
7883
apikey=config.get('APIKEY'),
7984
disable_ssl_verification=config.get('AUTH_DISABLE_SSL', 'false').lower() == 'true')
80-
elif auth_type == 'iam' and config.get('APIKEY'):
85+
elif auth_type == Authenticator.AUTHTYPE_IAM.lower() and config.get('APIKEY'):
8186
authenticator = IAMAuthenticator(
8287
apikey=config.get('APIKEY'),
8388
url=config.get('AUTH_URL'),
@@ -86,7 +91,7 @@ def __construct_authenticator(config: dict) -> Authenticator:
8691
disable_ssl_verification=config.get(
8792
'AUTH_DISABLE_SSL', 'false').lower() == 'true',
8893
scope=config.get('SCOPE'))
89-
elif auth_type == 'noauth':
94+
elif auth_type == Authenticator.AUTHTYPE_NOAUTH.lower():
9095
authenticator = NoAuthAuthenticator()
9196

9297
return authenticator

test/test_authenticator.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# coding=utf-8
2+
# pylint: disable=missing-docstring
3+
4+
from requests import Request
5+
from ibm_cloud_sdk_core.authenticators import Authenticator
6+
7+
8+
class TestAuthenticator(Authenticator):
9+
"""A test of the Authenticator base class"""
10+
11+
def validate(self) -> None:
12+
"""Simulated validate() method."""
13+
14+
def authenticate(self, req: Request) -> None:
15+
"""Simulated authenticate() method."""
16+
17+
18+
def test_authenticator():
19+
authenticator = TestAuthenticator()
20+
assert authenticator is not None
21+
assert authenticator.authentication_type() == Authenticator.AUTHTYPE_UNKNOWN

test/test_basic_authenticator.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
# pylint: disable=missing-docstring
22
import pytest
33

4-
from ibm_cloud_sdk_core.authenticators import BasicAuthenticator
4+
from ibm_cloud_sdk_core.authenticators import BasicAuthenticator, Authenticator
55

66

77
def test_basic_authenticator():
88
authenticator = BasicAuthenticator('my_username', 'my_password')
99
assert authenticator is not None
10+
assert authenticator.authentication_type() == Authenticator.AUTHTYPE_BASIC
1011
assert authenticator.username == 'my_username'
1112
assert authenticator.password == 'my_password'
1213

test/test_bearer_authenticator.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
# pylint: disable=missing-docstring
22
import pytest
33

4-
from ibm_cloud_sdk_core.authenticators import BearerTokenAuthenticator
4+
from ibm_cloud_sdk_core.authenticators import BearerTokenAuthenticator, Authenticator
55

66

77
def test_bearer_authenticator():
88
authenticator = BearerTokenAuthenticator('my_bearer_token')
99
assert authenticator is not None
10+
assert authenticator.authentication_type() == Authenticator.AUTHTYPE_BEARERTOKEN
1011
assert authenticator.bearer_token == 'my_bearer_token'
1112

1213
authenticator.set_bearer_token('james bond')

0 commit comments

Comments
 (0)