Skip to content

Commit 2b62252

Browse files
islcli groundwork
1 parent 016471d commit 2b62252

File tree

4 files changed

+196
-0
lines changed

4 files changed

+196
-0
lines changed

SoftLayer/API.py

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
__all__ = [
2929
'create_client_from_env',
30+
'employee_client',
3031
'Client',
3132
'BaseClient',
3233
'API_PUBLIC_ENDPOINT',
@@ -142,6 +143,88 @@ def create_client_from_env(username=None,
142143
return BaseClient(auth=auth, transport=transport, config_file=config_file)
143144

144145

146+
def employee_client(username=None,
147+
api_key=None,
148+
endpoint_url=None,
149+
timeout=None,
150+
auth=None,
151+
config_file=None,
152+
proxy=None,
153+
user_agent=None,
154+
transport=None,
155+
verify=True):
156+
"""Creates an INTERNAL SoftLayer API client using your environment.
157+
158+
Settings are loaded via keyword arguments, environemtal variables and
159+
config file.
160+
161+
:param username: your user ID
162+
:param api_key: hash from SoftLayer_User_Employee::performExternalAuthentication(username, password, 2fa_string)
163+
:param endpoint_url: the API endpoint base URL you wish to connect to.
164+
Set this to API_PRIVATE_ENDPOINT to connect via SoftLayer's private
165+
network.
166+
:param proxy: proxy to be used to make API calls
167+
:param integer timeout: timeout for API requests
168+
:param auth: an object which responds to get_headers() to be inserted into
169+
the xml-rpc headers. Example: `BasicAuthentication`
170+
:param config_file: A path to a configuration file used to load settings
171+
:param user_agent: an optional User Agent to report when making API
172+
calls if you wish to bypass the packages built in User Agent string
173+
:param transport: An object that's callable with this signature:
174+
transport(SoftLayer.transports.Request)
175+
:param bool verify: decide to verify the server's SSL/TLS cert. DO NOT SET
176+
TO FALSE WITHOUT UNDERSTANDING THE IMPLICATIONS.
177+
178+
Usage:
179+
180+
>>> import SoftLayer
181+
>>> client = SoftLayer.create_client_from_env()
182+
>>> resp = client.call('Account', 'getObject')
183+
>>> resp['companyName']
184+
'Your Company'
185+
186+
"""
187+
settings = config.get_client_settings(username=username,
188+
api_key=api_key,
189+
endpoint_url=endpoint_url,
190+
timeout=timeout,
191+
proxy=proxy,
192+
verify=verify,
193+
config_file=config_file)
194+
195+
if transport is None:
196+
url = settings.get('endpoint_url')
197+
if url is not None and '/rest' in url:
198+
# If this looks like a rest endpoint, use the rest transport
199+
transport = transports.RestTransport(
200+
endpoint_url=settings.get('endpoint_url'),
201+
proxy=settings.get('proxy'),
202+
timeout=settings.get('timeout'),
203+
user_agent=user_agent,
204+
verify=verify,
205+
)
206+
else:
207+
# Default the transport to use XMLRPC
208+
transport = transports.XmlRpcTransport(
209+
endpoint_url=settings.get('endpoint_url'),
210+
proxy=settings.get('proxy'),
211+
timeout=settings.get('timeout'),
212+
user_agent=user_agent,
213+
verify=verify,
214+
)
215+
216+
217+
if auth is None and settings.get('username') and settings.get('api_key'):
218+
real_transport = getattr(transport, 'transport', transport)
219+
if isinstance(real_transport, transports.XmlRpcTransport):
220+
auth = slauth.EmployeeAuthentication(
221+
settings.get('username'),
222+
settings.get('api_key'),
223+
)
224+
225+
return BaseClient(auth=auth, transport=transport)
226+
227+
145228
def Client(**kwargs):
146229
"""Get a SoftLayer API Client using environmental settings.
147230
@@ -545,6 +628,77 @@ def __repr__(self):
545628
return "IAMClient(transport=%r, auth=%r)" % (self.transport, self.auth)
546629

547630

631+
class EmployeeClient(BaseClient):
632+
"""Internal SoftLayer Client
633+
634+
:param auth: auth driver that looks like SoftLayer.auth.AuthenticationBase
635+
:param transport: An object that's callable with this signature: transport(SoftLayer.transports.Request)
636+
"""
637+
638+
def authenticate_with_password(self, username, password, security_token=None):
639+
"""Performs IBM IAM Username/Password Authentication
640+
641+
:param string username: your softlayer username
642+
:param string password: your softlayer password
643+
:param int security_token: your 2FA token, prompt if None
644+
"""
645+
646+
self.auth = None
647+
if security_token is None:
648+
security_token = input("Enter your 2FA Token now: ")
649+
if len(security_token) != 6:
650+
raise Exception("Invalid security token: {}".format(security_token))
651+
652+
auth_result = self.call('SoftLayer_User_Employee', 'performExternalAuthentication',
653+
username, password, security_token)
654+
655+
656+
self.settings['softlayer']['access_token'] = auth_result['hash']
657+
self.settings['softlayer']['userId'] = auth_result['userId']
658+
# self.settings['softlayer']['refresh_token'] = tokens['refresh_token']
659+
660+
config.write_config(self.settings, self.config_file)
661+
self.auth = slauth.EmployeeAuthentication(auth_result['userId'], auth_result['hash'])
662+
663+
return auth_result
664+
665+
666+
667+
def authenticate_with_hash(self, userId, access_token):
668+
"""Authenticates to the Internal SL API with an employee userid + token
669+
670+
:param string userId: Employee UserId
671+
:param string access_token: Employee Hash Token
672+
"""
673+
self.auth = slauth.EmployeeAuthentication(userId, access_token)
674+
675+
def refresh_token(self, userId, auth_token):
676+
"""Refreshes the login token"""
677+
678+
self.auth = None
679+
auth_result = self.call('SoftLayer_User_Employee', 'refreshEncryptedToken', auth_token, id=userId)
680+
print(auth_result)
681+
self.settings['softlayer']['access_token'] = auth_result[0]
682+
683+
config.write_config(self.settings, self.config_file)
684+
self.auth = slauth.EmployeeAuthentication(userId, auth_result[0])
685+
return auth_result
686+
687+
def call(self, service, method, *args, **kwargs):
688+
"""Handles refreshing IAM tokens in case of a HTTP 401 error"""
689+
try:
690+
return super().call(service, method, *args, **kwargs)
691+
except exceptions.SoftLayerAPIError as ex:
692+
693+
if ex.faultCode == 401:
694+
LOGGER.warning("Token has expired, trying to refresh. %s", ex.faultString)
695+
return ex
696+
else:
697+
raise ex
698+
699+
def __repr__(self):
700+
return "IAMClient(transport=%r, auth=%r)" % (self.transport, self.auth)
701+
548702
class Service(object):
549703
"""A SoftLayer Service.
550704

SoftLayer/CLI/login.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"""Login with your employee username, password, 2fa token"""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
6+
from SoftLayer.CLI.command import SLCommand as SLCommand
7+
from SoftLayer.CLI import config
8+
from SoftLayer.CLI import environment
9+
10+
11+
@click.command(cls=SLCommand)
12+
@environment.pass_env
13+
def cli(env):
14+
"""Logs you into the internal SoftLayer Network.
15+
16+
username and password can be set in SL_USER and SL_PASSWORD env variables. You will be prompted for them otherwise.
17+
"""
18+
19+
print("OK")

SoftLayer/CLI/routes.py

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

99
ALL_ROUTES = [
1010
('shell', 'SoftLayer.shell.core:cli'),
11+
('login', 'SoftLayer.CLI.login:cli'),
1112

1213
('call-api', 'SoftLayer.CLI.call_api:cli'),
1314

SoftLayer/auth.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
'TokenAuthentication',
1313
'BasicHTTPAuthentication',
1414
'AuthenticationBase',
15+
'EmployeeAuthentication'
1516
]
1617

1718

@@ -137,3 +138,24 @@ def get_request(self, request):
137138

138139
def __repr__(self):
139140
return "BearerAuthentication(username={}, token={})".format(self.username, self.api_key)
141+
142+
class EmployeeAuthentication(AuthenticationBase):
143+
"""Token-based authentication class.
144+
145+
:param username str: a user's username
146+
:param api_key str: a user's API key
147+
"""
148+
def __init__(self, user_id, user_hash):
149+
self.user_id = user_id
150+
self.hash = user_hash
151+
152+
def get_request(self, request):
153+
"""Sets token-based auth headers."""
154+
request.headers['employeesession'] = {
155+
'userId': self.user_id,
156+
'authToken': self.hash,
157+
}
158+
return request
159+
160+
def __repr__(self):
161+
return "EmployeeAuthentication(userId=%r,hash=%s)" % (self.user_id, self.hash)

0 commit comments

Comments
 (0)