Skip to content

Commit 7068987

Browse files
added account flag to islcli
1 parent 9655907 commit 7068987

File tree

5 files changed

+112
-15
lines changed

5 files changed

+112
-15
lines changed

SoftLayer/API.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,11 @@ class EmployeeClient(BaseClient):
646646
:param transport: An object that's callable with this signature: transport(SoftLayer.transports.Request)
647647
"""
648648

649+
def __init__(self, auth=None, transport=None, config_file=None, account_id=None):
650+
BaseClient.__init__(self, auth, transport, config_file)
651+
self.account_id = account_id
652+
653+
649654
def authenticate_with_password(self, username, password, security_token=None):
650655
"""Performs IBM IAM Username/Password Authentication
651656
@@ -687,34 +692,40 @@ def refresh_token(self, userId, auth_token):
687692
"""Refreshes the login token"""
688693

689694
self.auth = None
690-
auth_result = self.call('SoftLayer_User_Employee', 'refreshEncryptedToken', auth_token, id=userId)
695+
696+
# Go directly to base client, to avoid infite loop if the token is super expired.
697+
auth_result = BaseClient.call(self, 'SoftLayer_User_Employee', 'refreshEncryptedToken', auth_token, id=userId)
691698
if len(auth_result) > 1:
692699
for returned_data in auth_result:
693700
# Access tokens should be 188 characters, but just incase its longer or something.
694701
if len(returned_data) > 180:
695702
self.settings['softlayer']['access_token'] = returned_data
696703
else:
697-
message = "Excepted 2 properties from refreshEncryptedToken, got |{}|".format(auth_result)
704+
message = "Excepted 2 properties from refreshEncryptedToken, got {}|".format(auth_result)
698705
raise exceptions.SoftLayerAPIError(message)
699706

700707
config.write_config(self.settings, self.config_file)
701708
self.auth = slauth.EmployeeAuthentication(userId, auth_result[0])
702709
return auth_result
703710

704711
def call(self, service, method, *args, **kwargs):
705-
"""Handles refreshing IAM tokens in case of a HTTP 401 error"""
712+
"""Handles refreshing Employee tokens in case of a HTTP 401 error"""
713+
if (service == 'SoftLayer_Account' or service == 'Account') and not kwargs.get('id'):
714+
if not self.account_id:
715+
raise exceptions.SoftLayerError("SoftLayer_Account service requires an ID")
716+
kwargs['id'] = self.account_id
717+
706718
try:
707-
return super().call(service, method, *args, **kwargs)
719+
return BaseClient.call(self, service, method, *args, **kwargs)
708720
except exceptions.SoftLayerAPIError as ex:
709-
710-
if ex.faultCode == 401:
711-
LOGGER.warning("Token has expired, trying to refresh. %s", ex.faultString)
712-
return ex
713721
if ex.faultCode == "SoftLayer_Exception_EncryptedToken_Expired":
714722
userId = self.settings['softlayer'].get('userId')
715723
access_token = self.settings['softlayer'].get('access_token')
716-
LOGGER.warning("Token has expired2, trying to refresh. %s", ex.faultString)
717-
self.refresh_token()
724+
LOGGER.warning("Token has expired, trying to refresh. %s", ex.faultString)
725+
self.refresh_token(userId, access_token)
726+
# Try the Call again this time....
727+
return BaseClient.call(self, service, method, *args, **kwargs)
728+
718729
else:
719730
raise ex
720731

SoftLayer/CLI/core.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,8 @@ def get_version_message(ctx, param, value):
7070
ctx.exit()
7171

7272

73-
@click.group(help="SoftLayer Command-line Client",
74-
epilog="""To use most commands your SoftLayer username and api_key need to be configured.
75-
The easiest way to do that is to use: 'slcli setup'""",
73+
@click.group(help="SoftLayer Employee Command-line Client",
74+
epilog="""Run 'islcli login' to authenticate""",
7675
cls=CommandLoader,
7776
context_settings=CONTEXT_SETTINGS)
7877
@click.option('--format',
@@ -103,6 +102,7 @@ def get_version_message(ctx, param, value):
103102
help="Use demo data instead of actually making API calls")
104103
@click.option('--version', is_flag=True, expose_value=False, is_eager=True, callback=get_version_message,
105104
help="Show version information.", allow_from_autoenv=False,)
105+
@click.option('-a', '--account', help="Account Id")
106106
@environment.pass_env
107107
def cli(env,
108108
format='table',
@@ -111,6 +111,7 @@ def cli(env,
111111
proxy=None,
112112
really=False,
113113
demo=False,
114+
account_id=None,
114115
**kwargs):
115116
"""Main click CLI entry-point."""
116117

@@ -133,6 +134,7 @@ def cli(env,
133134
env.vars['_timings'] = SoftLayer.DebugTransport(env.client.transport)
134135
env.vars['verbose'] = verbose
135136
env.client.transport = env.vars['_timings']
137+
env.client.account_id = account_id
136138

137139

138140
@cli.result_callback()
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<params>
3+
<param>
4+
<value>
5+
<struct>
6+
<member>
7+
<name>id</name>
8+
<value>
9+
<int>5555</int>
10+
</value>
11+
</member>
12+
<member>
13+
<name>username</name>
14+
<value>
15+
<string>testUser</string>
16+
</value>
17+
</member>
18+
</struct>
19+
</value>
20+
</param>
21+
</params>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="iso-8859-1"?>
2+
<methodResponse>
3+
<fault>
4+
<value>
5+
<struct>
6+
<member>
7+
<name>faultCode</name>
8+
<value>
9+
<string>SoftLayer_Exception_EncryptedToken_Expired</string>
10+
</value>
11+
</member>
12+
<member>
13+
<name>faultString</name>
14+
<value>
15+
<string>The token has expired.</string>
16+
</value>
17+
</member>
18+
</struct>
19+
</value>
20+
</fault>
21+
</methodResponse>

tests/api_tests.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from SoftLayer import testing
1515
from SoftLayer import transports
1616
from SoftLayer import exceptions
17+
from SoftLayer import auth as slauth
1718

1819

1920
class Initialization(testing.TestCase):
@@ -323,7 +324,6 @@ class EmployeeClientTests(testing.TestCase):
323324
def setup_response(filename, status_code=200, total_items=1):
324325
basepath = os.path.dirname(__file__)
325326
body = b''
326-
print(f"Base Path: {basepath}")
327327
with open(f"{basepath}/../SoftLayer/fixtures/xmlrpc/{filename}.xml", 'rb') as fixture:
328328
body = fixture.read()
329329
response = requests.Response()
@@ -366,4 +366,46 @@ def test_refresh_token(self, api_response):
366366
result = self.client.refresh_token(9999, 'qweasdzxcqweasdzxcqweasdzxc')
367367
self.assertEqual(self.client.auth.user_id, 9999)
368368
self.assertIn('REFRESHEDTOKENaaaa', self.client.auth.hash)
369-
369+
370+
@mock.patch('SoftLayer.transports.xmlrpc.requests.Session.request')
371+
def test_expired_token_is_refreshed(self, api_response):
372+
api_response.side_effect = [
373+
self.setup_response('expiredToken'),
374+
self.setup_response('refreshSuccess'),
375+
self.setup_response('Employee_getObject')
376+
]
377+
self.client.auth = slauth.EmployeeAuthentication(5555, 'aabbccee')
378+
self.client.settings['softlayer']['userid'] = '5555'
379+
result = self.client.call('SoftLayer_User_Employee', 'getObject', id=5555)
380+
self.assertIn('REFRESHEDTOKENaaaa', self.client.auth.hash)
381+
self.assertEqual('testUser', result['username'])
382+
383+
@mock.patch('SoftLayer.transports.xmlrpc.requests.Session.request')
384+
def test_expired_token_is_really_expored(self, api_response):
385+
api_response.side_effect = [
386+
self.setup_response('expiredToken'),
387+
self.setup_response('expiredToken')
388+
]
389+
self.client.auth = slauth.EmployeeAuthentication(5555, 'aabbccee')
390+
self.client.settings['softlayer']['userid'] = '5555'
391+
exception = self.assertRaises(
392+
exceptions.SoftLayerAPIError,
393+
self.client.call, 'SoftLayer_User_Employee', 'getObject', id=5555)
394+
self.assertEqual(None, self.client.auth)
395+
self.assertEqual(exception.faultCode, "SoftLayer_Exception_EncryptedToken_Expired")
396+
397+
@mock.patch('SoftLayer.API.BaseClient.call')
398+
def test_account_check(self, _call):
399+
self.client.transport = self.mocks
400+
exception = self.assertRaises(
401+
exceptions.SoftLayerError,
402+
self.client.call, "SoftLayer_Account", "getObject")
403+
self.assertEqual(str(exception), "SoftLayer_Account service requires an ID")
404+
self.client.account_id = 1234
405+
self.client.call("SoftLayer_Account", "getObject")
406+
self.client.call("SoftLayer_Account", "getObject1", id=9999)
407+
408+
_call.assert_has_calls([
409+
mock.call(self.client, 'SoftLayer_Account', 'getObject', id=1234),
410+
mock.call(self.client, 'SoftLayer_Account', 'getObject1', id=9999),
411+
])

0 commit comments

Comments
 (0)