Skip to content

Improved employee auth to work better on dev servers #2220

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions SoftLayer/API.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
'raw_headers',
'limit',
'offset',
'verify',
'verify'
))


Expand Down Expand Up @@ -182,7 +182,7 @@ def employee_client(username=None,
verify=None,
config_file=config_file)

url = settings.get('endpoint_url')
url = settings.get('endpoint_url', '')
verify = settings.get('verify', True)

if 'internal' not in url:
Expand Down Expand Up @@ -374,7 +374,6 @@ def call(self, service, method, *args, **kwargs):
request.url = self.settings['softlayer'].get('endpoint_url')
if kwargs.get('verify') is not None:
request.verify = kwargs.get('verify')

if self.auth:
request = self.auth.get_request(request)

Expand Down Expand Up @@ -495,7 +494,7 @@ def __setAuth(self, auth=None):
"""Prepares the authentication property"""
if auth is None:
auth_cert = self.settings['softlayer'].get('auth_cert')
serv_cert = self.settings['softlayer'].get('server_cert', None)
serv_cert = self.settings['softlayer'].get('verify', True)
auth = slauth.X509Authentication(auth_cert, serv_cert)
self.auth = auth

Expand Down Expand Up @@ -712,7 +711,7 @@ def authenticate_with_internal(self, username, password, security_token=None):
if len(security_token) != 6:
raise exceptions.SoftLayerAPIError("Invalid security token: {}".format(security_token))

auth_result = self.call('SoftLayer_User_Employee', 'performExternalAuthentication',
auth_result = self.call('SoftLayer_User_Employee', 'getEncryptedSessionToken',
username, password, security_token)

self.settings['softlayer']['access_token'] = auth_result['hash']
Expand Down
17 changes: 7 additions & 10 deletions SoftLayer/CLI/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import click

from SoftLayer.API import employee_client
from SoftLayer.CLI.command import SLCommand as SLCommand
from SoftLayer.CLI import environment
from SoftLayer import config
Expand All @@ -30,16 +29,15 @@ def cli(env):
username = settings.get('username') or os.environ.get('SLCLI_USER', None)
password = os.environ.get('SLCLI_PASSWORD', '')
yubi = None
client = employee_client(config_file=env.config_file)

# Might already be logged in, try and refresh token
if settings.get('access_token') and settings.get('userid'):
client.authenticate_with_hash(settings.get('userid'), settings.get('access_token'))
env.client.authenticate_with_hash(settings.get('userid'), settings.get('access_token'))
try:
emp_id = settings.get('userid')
client.call('SoftLayer_User_Employee', 'getObject', id=emp_id, mask="mask[id,username]")
client.refresh_token(emp_id, settings.get('access_token'))
client.call('SoftLayer_User_Employee', 'refreshEncryptedToken', settings.get('access_token'), id=emp_id)
env.client.call('SoftLayer_User_Employee', 'getObject', id=emp_id, mask="mask[id,username]")
env.client.refresh_token(emp_id, settings.get('access_token'))
env.client.call('SoftLayer_User_Employee', 'refreshEncryptedToken', settings.get('access_token'), id=emp_id)

config_settings['softlayer'] = settings
config.write_config(config_settings, env.config_file)
Expand All @@ -52,13 +50,12 @@ def cli(env):
click.echo("URL: {}".format(url))
if username is None:
username = input("Username: ")
click.echo("Username: {}".format(username))
if not password:
password = env.getpass("Password: ")
click.echo("Password: {}".format(censor_password(password)))
password = env.getpass("Password: ", default="")
yubi = input("Yubi: ")

try:
result = client.authenticate_with_internal(username, password, str(yubi))
result = env.client.authenticate_with_internal(username, password, str(yubi))
print(result)
# pylint: disable=broad-exception-caught
except Exception as e:
Expand Down
8 changes: 4 additions & 4 deletions SoftLayer/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

:license: MIT, see LICENSE for more details.
"""

import os

__all__ = [
'BasicAuthentication',
Expand Down Expand Up @@ -89,7 +89,7 @@ def get_request(self, request):
return request

def __repr__(self):
return "BasicAuthentication(username=%r)" % self.username
return f"BasicAuthentication(username={self.username})"


class BasicHTTPAuthentication(AuthenticationBase):
Expand All @@ -110,7 +110,7 @@ def get_request(self, request):
return request

def __repr__(self):
return "BasicHTTPAuthentication(username=%r)" % self.username
return f"BasicHTTPAuthentication(username={self.username}"


class BearerAuthentication(AuthenticationBase):
Expand Down Expand Up @@ -149,7 +149,7 @@ class X509Authentication(AuthenticationBase):
"""

def __init__(self, cert, ca_cert):
self.cert = cert
self.cert = os.path.expanduser(cert)
self.ca_cert = ca_cert

def get_request(self, request):
Expand Down
6 changes: 5 additions & 1 deletion SoftLayer/transports/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ def __call__(self, request):

request.params = params

# This handles any edge cases on the REST api.
request.special_rest_params()

auth = None
if request.transport_user:
auth = requests.auth.HTTPBasicAuth(
Expand Down Expand Up @@ -110,7 +113,6 @@ def __call__(self, request):
# Prefer the request setting, if it's not None
if request.verify is None:
request.verify = self.verify

try:
resp = self.client.request(method, request.url,
auth=auth,
Expand Down Expand Up @@ -163,6 +165,8 @@ def print_reproduceable(request):

:param request request: Request object
"""
# This handles any edge cases on the REST api.
request.special_rest_params()
command = "curl -u $SL_USER:$SL_APIKEY -X {method} -H {headers} {data} '{uri}'"

method = REST_SPECIAL_METHODS.get(request.method)
Expand Down
19 changes: 18 additions & 1 deletion SoftLayer/transports/transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ def __init__(self):
#: API Parameters.
self.args = tuple()

#: URL Parameters, used for the REST Transport
self.params = None

#: API headers, used for authentication, masks, limits, offsets, etc.
self.headers = {}

Expand Down Expand Up @@ -103,13 +106,27 @@ def __repr__(self):
pretty_filter = self.filter
clean_args = self.args
# Passwords can show up here, so censor them before logging.
if self.method in ["performExternalAuthentication", "refreshEncryptedToken", "getPortalLoginToken"]:
if self.method in ["performExternalAuthentication", "refreshEncryptedToken",
"getPortalLoginToken", "getEncryptedSessionToken"]:
clean_args = "*************"
param_string = (f"id={self.identifier}, mask='{pretty_mask}', filter='{pretty_filter}', args={clean_args}, "
f"limit={self.limit}, offset={self.offset}")
return "{service}::{method}({params})".format(
service=self.service, method=self.method, params=param_string)

def special_rest_params(self):
"""This method is to handle the edge case of SoftLayer_User_Employee::getEncryptedSessionToken

Added this method here since it was a little easier to change the data as needed this way.
"""
if self.method == "getEncryptedSessionToken" and self.service == "SoftLayer_User_Employee":
if len(self.args) < 3:
return
self.params = {"remoteToken": self.args[2]}
self.transport_user = self.args[0]
self.transport_password = self.args[1]
self.args = []


class SoftLayerListResult(list):
"""A SoftLayer API list result."""
Expand Down
2 changes: 1 addition & 1 deletion docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
sphinx_rtd_theme==3.0.2
sphinx==8.2.1
sphinx==8.2.3
sphinx-click==6.0.0
click
prettytable
Expand Down