#!/usr/bin/env python
#
# AUTHCLIENT -- Client methods for the Data Lab Authentication Manager service
#
from __future__ import print_function
__authors__ = 'Mike Fitzpatrick <mike.fitzpatrick@noirlab.edu>, Data Lab <datalab@noirlab.edu>'
try:
from authmanager.__version__ import __version__
except ImportError as e:
from dl.__version__ import __version__
'''
Client methods for the Data Lab Authentication Manager Service.
Auth Manager Client Interface
-----------------------------
login (user, password=None, debug=False, verbose=False)
logout (token=None)
whoAmI ()
isAlive (svc_url=DEF_SERVICE_URL)
isValidToken (token)
isValidUser (user)
isValidPassword (user, password)
hasAccess (user, resource)
isUserLoggedIn (user)
isTokenLoggedIn (token)
passwordReset (token, username, password)
set_svc_url (svc_url)
get_svc_url ()
set_profile (profile)
get_profile ()
getClient ()
Import via
.. code-block:: python
from dl import authClient
'''
import requests
import socket
import os
import sys
from time import gmtime, strftime
try:
from urllib import urlencode # Python 2
import ConfigParser # Python 2
except ImportError:
from urllib.parse import urlencode # Python 3
import configparser as ConfigParser # Python 3
#if os.path.isfile('./Util.py'): # use local dev copy
# from Util import def_token
#else: # use distribution copy
# from dl.Util import def_token
from dl.Util import def_token, split_auth_token
is_py3 = sys.version_info.major == 3
# Pre-defined authentication tokens. These are fixed strings that provide
# limited access to Data Lab services, this access is controlled on the
# server-side so we don't need strict security here.
ANON_TOKEN = "anonymous.0.0.anon_access"
DEMO_TOKEN = "dldemo.99999.99999.demo_access"
TEST_TOKEN = "dltest.99998.99998.test_access"
# Set the default user accounts for the Authentication Manager service. We don't
# include privileged users so that account can remain secure.
DEF_USERS = {'anonymous': ANON_TOKEN,
'dldemo': DEMO_TOKEN,
'dltest': TEST_TOKEN}
# The URL of the Authentication Manager service to contact. This may be changed by
# passing a new URL into the set_svc_url() method before beginning.
DEF_SERVICE_ROOT = "https://datalab.noirlab.edu"
# Allow the service URL for dev/test systems to override the default.
THIS_HOST = socket.gethostname()
if THIS_HOST[:5] == 'dldev':
DEF_SERVICE_ROOT = "https://dldev.datalab.noirlab.edu"
elif THIS_HOST[:6] == 'dltest':
DEF_SERVICE_ROOT = "https://dltest.datalab.noirlab.edu"
DEF_SERVICE_URL = DEF_SERVICE_ROOT + "/auth"
SM_SERVICE_URL = DEF_SERVICE_ROOT + "/storage"
QM_SERVICE_URL = DEF_SERVICE_ROOT + "/query"
# Check for a file to override the default service URL.
if os.path.exists('/tmp/AM_SVC_URL'):
with open('/tmp/AM_SVC_URL') as fd:
DEF_SERVICE_URL = fd.read().strip()
if os.path.exists('/tmp/SM_SVC_URL'):
with open('/tmp/SM_SVC_URL') as fd:
SM_SERVICE_URL = fd.read().strip()
if os.path.exists('/tmp/QM_SVC_URL'):
with open('/tmp/QM_SVC_URL') as fd:
QM_SERVICE_URL = fd.read().strip()
# The requested authentication "profile". A profile refers to the specific
# machines and services used by the Authentication Manager on the server. [Note that
# profiles are not currently used.]
DEF_SERVICE_PROFILE = "default"
# Use a /tmp/AM_DEBUG file as a way to turn on debugging in the client code.
DEBUG = os.path.isfile('/tmp/AM_DEBUG')
# ######################################################################
#
# Authentication Client Interface
#
# This API provides convenience methods that allow an application to
# import the Client class without having to explicitly instantiate a
# class object. The parameter descriptions and example usage is given
# in the comments for the class methods. Module methods have their
# docstrings patched below.
#
# ######################################################################
# ###################################
# Authentication error class
# ###################################
[docs]
class dlAuthError(Exception):
'''A throwable error class.
'''
def __init__(self, message):
self.message = message
def __str__(self):
return self.message
# User methods -- All methods except login() return either a 'True' string
# or an error of the form 'ERR <message>'. On success, the login() method
# will return the user-id token.
[docs]
def login(user, password=None, debug=False, verbose=False):
response = 'OK'
if user in list(DEF_USERS.keys()):
return DEF_USERS[user]
else:
try:
response = ac_client.login(user, password, debug=debug,
verbose=verbose)
except Exception as e:
response = str(e)
return response
[docs]
def whoAmI():
user = 'anonymous'
try:
token = def_token(None)
user, uid, gid, hash = split_auth_token(token.strip())
except:
return 'anonymous'
else:
return user
[docs]
def isAlive(svc_url=DEF_SERVICE_URL):
try:
response = ac_client.isAlive(svc_url.strip('/'))
except Exception as e:
response = str(e)
return False
return response
[docs]
def isValidToken(token):
try:
user, uid, gid, hash = split_auth_token(token.strip())
except Exception:
return False
if user in list(DEF_USERS.keys()) and token in list(DEF_USERS.values()):
return True
else:
try:
response = ac_client.isValidToken(token)
except Exception:
return False
return (True if response.lower() == 'true' else False)
[docs]
def isValidUser(user):
if user in list(DEF_USERS.keys()):
return True
else:
try:
response = ac_client.isValidUser(user)
except Exception as e:
response = str(e)
return (True if response.lower() == 'true' else False)
[docs]
def isValidPassword(user, password):
if (user == password) and (user in list(DEF_USERS.keys())):
return True
else:
try:
response = ac_client.isValidPassword(user, password)
except Exception as e:
response = str(e)
return (True if response.lower() == 'true' else False)
[docs]
def hasAccess(user, resource):
try:
response = ac_client.hasAccess(user, resource)
except Exception as e:
response = str(e)
return (True if response.lower() == 'true' else False)
[docs]
def isUserLoggedIn(user):
try:
response = ac_client.isUserLoggedIn(user)
except Exception as e:
response = str(e)
return (True if response.lower() == 'true' else False)
[docs]
def isTokenLoggedIn(token):
try:
response = ac_client.isTokenLoggedIn(token)
except Exception as e:
response = str(e)
return (True if response.lower() == 'true' else False)
[docs]
def logout(token=None):
try:
response = ac_client.logout(def_token(token))
except Exception as e:
response = str(e)
return response
[docs]
def passwordReset(token, username, password):
try:
response = ac_client.passwordReset(token, username, password)
except Exception as e:
response = str(e)
return response
# Standard Service Methods
[docs]
def set_svc_url(svc_url):
if svc_url is not None and svc_url != '':
return ac_client.set_svc_url(svc_url.strip('/'))
[docs]
def get_svc_url():
return ac_client.get_svc_url()
[docs]
def set_profile(profile):
return ac_client.set_profile(profile)
[docs]
def get_profile():
return ac_client.get_profile()
[docs]
def list_profiles(token, profile=None, format='text'):
return "None"
#####################################
# Authentication client procedures
#####################################
[docs]
class authClient(object):
'''
AUTHCLIENT -- Client-side methods to access the Data Lab
Authentication Manager service.
'''
def __init__(self):
'''Initialize the authClient class. '''
self.svc_url = DEF_SERVICE_URL # service URL
self.svc_profile = DEF_SERVICE_PROFILE # service prfile
self.username = "" # default client logn user
self.auth_token = None # default client logn token
# Check the $HOME/.datalab directory for a valid token. If that dir
# doesn't already exist, create it so we can store the new token.
self.home = '%s/.datalab' % os.path.expanduser('~')
if not os.path.exists(self.home):
os.makedirs(self.home)
self.loadConfig() # load config file
self.debug = DEBUG # interface debug flag
[docs]
def loadConfig(self):
'''Read the $HOME/.datalab/dl.conf file.
'''
self.config = ConfigParser.RawConfigParser(allow_no_value=True)
if os.path.exists('%s/dl.conf' % self.home):
self.config.read('%s/dl.conf' % self.home)
else:
self.config.add_section('datalab')
self.config.set('datalab', 'created', strftime(
'%Y-%m-%d %H:%M:%S', gmtime()))
self.config.add_section('login')
self.config.set('login', 'status', 'loggedout')
self.config.set('login', 'user', '')
self.config.set('login', 'authtoken', '')
self.config.add_section('auth')
self.config.set('auth', 'profile', 'default')
self.config.set('auth', 'svc_url', DEF_SERVICE_URL)
self.config.add_section('query')
self.config.set('query', 'profile', 'default')
self.config.set('query', 'svc_url', QM_SERVICE_URL)
self.config.add_section('storage')
self.config.set('storage', 'profile', 'default')
self.config.set('storage', 'svc_url', SM_SERVICE_URL)
self.config.add_section('vospace')
self.config.set('vospace', 'mount', '')
self.writeConfig()
[docs]
def setConfig(self, section, param, value):
'''Set a value and save the configuration file.
'''
if not self.config.has_section(section):
self.config.add_section(section)
self.config.set(section, param, value)
self.writeConfig()
[docs]
def getConfig(self, section, param):
'''Get a value from the configuration file.
'''
return self.config.get(section, param)
[docs]
def writeConfig(self):
'''Write out the configuration file to disk.
'''
with open('%s/dl.conf' % self.home, 'w') as configfile:
self.config.write(configfile)
[docs]
def whoAmI(self):
'''Return the currently logged in user identity.
Parameters
----------
None
Returns
-------
name : str
Currently logged in user name, or 'anonymous' if not logged in.
Example
-------
>>> from dl import authClient
>>> name = authClient.whoAmI()
'''
user = 'anonymous'
try:
token = def_token(None)
user, uid, gid, hash = split_auth_token(token.strip())
except:
return 'anonymous'
else:
return user
# Standard Data Lab service methods.
#
[docs]
def set_svc_url(self, svc_url):
'''Set the URL of the Authentication Manager service to be used.
Parameters
----------
svc_url : str
Authentication Manager service base URL to call.
Returns
-------
Nothing
Example
-------
>>> from dl import authClient
>>> authClient.set_svc_url("http://localhost:7001/")
'''
if svc_url is not None and svc_url != '':
self.svc_url = acToString(svc_url.strip('/'))
[docs]
def get_svc_url(self):
'''Return the currently-used Authentication Manager service URL.
Parameters
----------
None
Returns
-------
service_url : str
The currently-used Authentication Manager service URL.
Example
-------
>>> from dl import authClient
>>> service_url = authClient.get_svc_url()
'''
return acToString(self.svc_url)
[docs]
def set_profile(self, profile):
'''Set the requested service profile.
Parameters
----------
profile : str
Requested service profile.
Returns
-------
Nothing
Example
-------
>>> from dl import authClient
>>> authClient.set_profile("dev")
'''
if profile is not None and profile != '':
self.svc_profile = acToString(profile)
[docs]
def get_profile(self):
'''Get the requested service profile.
Parameters
----------
None
Returns
-------
profile : str
The currently requested service profile.
Example
-------
>>> from dl import authClient
>>> profile = authClient.get_profile()
'''
return acToString(self.svc_profile)
[docs]
def list_profiles(self, token, profile=None, format='text'):
'''List the service profiles which can be accessed by the user.
Parameters
----------
token : str
Valid auth service token.
Returns
-------
profiles : JSON string
Example
-------
>>> from dl import authClient
>>> profiles = authClient.list_profiles(token, profile, format)
'''
pass
[docs]
def isAlive(self, svc_url=DEF_SERVICE_URL):
'''Check whether the Authentication Manager service at the given URL is
alive and responding. This is a simple call to the root
service URL or ``ping()`` method.
Parameters
----------
service_url : str
The Query Service URL to ping.
Returns
-------
result : bool
``True`` if service responds properly, ``False`` otherwise.
Example
-------
>>> from dl import authClient
>>> authClient.isAlive()
'''
url = svc_url
try:
r = requests.get(url, timeout=2)
resp = r.text
if r.status_code != 200:
return False
elif resp is not None and r.text.lower()[:11] != "hello world":
return False
except Exception:
return False
return True
###################################################
# SESSION MANAGEMENT
###################################################
[docs]
def login(self, username, password, debug=False, verbose=False):
'''Authenticate the user with the Authentication Manager service.
We first check for a valid login token in the user's
``$HOME/.datalab/`` directory and simply return that rather
than make a service call to get a new token. If a token
exists but is invalid, we remove it and get a new
token. In either case, we save the token for later use.
Parameters
----------
username : str
User login name.
password : str
User password. If not given, a valid ID token will be
searched for in the ``$HOME/.datalab`` directory.
debug : bool
Method debug flag.
verbose : bool
Initialize session to print verbose output messages.
Returns
-------
token : str
One-time security token for valid user (identified via
``username`` and ``password``).
Example
-------
>>> from dl import authClient
>>> token = authClient.login('dldemo', 'dldemo') # get security token
'''
# Check the $HOME/.datalab directory for a valid token. If that dir
# doesn't already exist, create it so we can store the new token.
if not os.path.exists(self.home):
os.makedirs(self.home)
# See if a datalab token file exists for the requested user.
tok_file = ('%s/id_token.%s' % (self.home, username))
if debug or self.debug:
print("top of login: tok_file = '" + tok_file + "'")
print("top of login: self.auth_token = '%s'" %
str(self.auth_token))
print("top of login: token = ")
os.system("cat " + tok_file)
if password is None:
if os.path.exists(tok_file) and os.stat(tok_file).st_size > 0:
with open(tok_file, "r") as tok_fd:
o_tok = acToString(tok_fd.read(128)) # read the old token
# Return a valid token, otherwise remove the file and obtain a
# new one.
if o_tok.startswith(username+'.') and self.isValidToken(o_tok):
self.username = username
self.auth_token = o_tok
if debug or self.debug:
print("using old token for '%s'" % username)
return acToString(o_tok)
else:
if debug or self.debug:
print("removing invalid token file '%s'" % tok_file)
os.remove(tok_file)
# Either the user is not logged in or the token is invalid, so
# make a service call to get a new token.
url = self.svc_url + "/login?"
query_args = {"username": username,
"profile": self.svc_profile,
"debug": (debug or self.debug)}
# Add the auth token and password to the reauest header.
headers = {'X-DL-Password': password}
response = 'None'
try:
r = requests.get(url, params=query_args, headers=headers)
response = acToString(r.content)
if debug or self.debug:
print("%s: resp = '%s'" % (str(r.status_code),response))
if r.status_code != 200:
raise Exception(response)
except Exception as e:
if debug or self.debug:
print("Raw exception msg = '%s'" % acToString(r.content))
if self.isAlive(self.svc_url) == False:
raise dlAuthError("AuthManager Service not responding.")
if self.isValidUser(username):
if password is None:
if not os.path.exists(tok_file):
raise dlAuthError("No password or token supplied")
else:
raise dlAuthError("No password supplied")
elif not self.isValidPassword(username, password):
raise dlAuthError("Invalid password in login()")
else:
raise dlAuthError(str(e))
else:
raise dlAuthError("Invalid username in login()")
else:
self.auth_token = response
self.username = username
# Save the token and config file.
if os.access(self.home, os.W_OK):
tok_file = '%s/id_token.%s' % (self.home, username)
with open(tok_file, 'w') as tok_fd:
if debug or self.debug:
print("login: writing new token for '%s'" % username)
print("login: self.auth_token = '%s'" %
str(self.auth_token))
print("login: token = ")
os.system('cat ' + tok_file)
tok_fd.write(self.auth_token)
self.config.set('login', 'status', 'loggedin')
self.config.set('login', 'user', username)
self.config.set('login', 'authtoken', self.auth_token)
self.writeConfig()
return acToString(self.auth_token)
[docs]
def logout(self, token=None):
'''Log the user out of Data Lab.
Parameters
----------
token : str [Optional]
User login token.
Returns
-------
'OK' string on success, or exception message.
Example
-------
>>> from dl import authClient
>>> status = authClient.logout()
'''
url = self.svc_url + "/logout?"
args = urlencode({"token": token,
"debug": self.debug})
url = url + args
if self.debug:
print("logout: token = '%s'" % token)
print("logout: auth_token = '%s'" % self.auth_token)
print("logout: url = '%s'" % url)
print("logout: logged in = " + self.isTokenLoggedIn(token))
try:
user, uid, gid, hash = split_auth_token(token.strip())
except Exception:
raise dlAuthError('Error: Invalid user token')
if not isValidToken(token):
raise dlAuthError("Error: Invalid user token")
if not isTokenLoggedIn(token):
raise dlAuthError("Error: User token is not logged in")
try:
# Add the auth token to the reauest header.
headers = {'X-DL-AuthToken': token}
r = requests.get(url, params=args, headers=headers)
response = acToString(r.content)
if r.status_code != 200:
raise Exception(response)
except Exception as e:
raise dlAuthError(str(e))
else:
self.auth_token = None
tok_file = self.home + '/id_token.' + user
if os.path.exists(tok_file):
os.remove(tok_file)
self.config.set('login', 'status', 'loggedout')
self.config.set('login', 'user', '')
self.config.set('login', 'authtoken', '')
self.writeConfig()
return response
[docs]
def passwordReset(self, token, username, password):
'''Reset a user password. We require that the user provide
either a valid 'root' token or the token for the account being
reset.
Parameters
----------
token : str
User login token.
username : str
User login name.
password : str
User password.
Returns
-------
'OK' string on success, or exception message.
Example
-------
>>> from dl import authClient
>>> authClient.passwordReset(token, 'monty', 'python')
'''
url = self.svc_url + "/passwordReset?"
args = urlencode({"token": token,
"username": username,
"debug": self.debug})
if self.debug:
print("passwdReset: token = '%s'" % token)
print("passwdReset: auth_token = '%s'" % self.auth_token)
print("passwdReset: url = '%s'" % url)
if not self.isValidToken(token):
if self.debug:
print("passwdReset: Invalid user token")
raise Exception("Error: Invalid user token")
# Reset the auth_token to the one passed in by the service call.
self.auth_token = token
user, uid, gid, hash = split_auth_token(self.auth_token.strip())
if user != 'root' and user != username:
raise Exception("Error: Invalid user or non-root token")
try:
# Add the auth token and password to the reauest header.
headers = {'X-DL-AuthToken': token,
'X-DL-Password': password}
r = requests.get(url, params=args, headers=headers)
response = acToString(r.content)
if r.status_code != 200:
raise Exception(r.content)
except Exception:
raise dlAuthError(acToString(r.content))
else:
# Update the saved user token.
if response is not None:
self.auth_token = response
tok_file = self.home + '/id_token.' + self.username
if os.path.exists(tok_file):
os.remove(tok_file)
with open(tok_file, 'wb') as tok_fd:
if self.debug:
print("pwreset: writing new token for '%s'" + username)
print("pwreset: response = '%s'" + response)
print("pwreset: token = '%s'" + self.auth_token)
tok_fd.write(acToString(self.auth_token))
tok_fd.close()
else:
print('pwReset response is None')
return response
[docs]
def hasAccess(self, token, resource):
'''See whether the given token has access to the named Resource.
Parameters
----------
token : str
User login token.
resource : str
Resource identifier to check.
Returns
-------
status : bool
``True`` if user owns or has access, ``False`` otherwise.
Example
-------
>>> from dl import authClient
>>> status = authClient.hasAccess(token, 'vos://test.dat')
'''
# Either the user is not logged in or the token is invalid, so
# make a service call to get a new token.
url = self.svc_url + "/hasAccess?"
args = urlencode({"user": token,
"resource": resource,
"profile": self.svc_profile})
url = url + args
if self.debug:
print("hasAccess: url = '%s'" % url)
return self.retBoolValue(url)
[docs]
def isValidToken(self, token):
'''See whether the current token is valid.
Parameters
----------
token : str
User login token.
Returns
-------
status : bool
``True`` if token is valid, ``False`` otherwise.
Example
-------
>>> from dl import authClient
>>> authClient.isValidToken(token)
'''
url = self.svc_url + "/isValidToken?"
args = urlencode({"token": token,
"profile": self.svc_profile})
url = url + args
if self.debug:
print("isValidToken: url = '%s'" % url)
# Save the value before returning so we can print it in debug mode.
isValid = self.retBoolValue(url)
if self.debug:
print("isValidToken: valid = " + str(isValid))
return isValid
[docs]
def isValidPassword(self, user, password):
'''See whether the password is valid for the user.
Parameters
----------
user : str
User login name.
password : str
Password for named user.
Returns
-------
status : bool
``True`` if password is valid for the user, ``False`` otherwise.
Example
-------
>>> from dl import authClient
>>> authClient.isValidPassword('monty', 'python')
'''
url = self.svc_url + "/isValidPassword?"
args = urlencode({"user": user,
"password": password,
"profile": self.svc_profile})
url = url + args
if self.debug:
print("isValidPassword: url = '%s'" % url)
try:
val = self.retBoolValue(url)
except Exception:
val = "False"
return val
[docs]
def isValidUser(self, user):
'''See whether the specified user is valid.
Parameters
----------
user : str
User login name.
Returns
-------
status : bool
``True`` if ``user`` is a valid user name, ``False`` otherwise.
Example
-------
>>> from dl import authClient
>>> authClient.isValidUser('monty')
'''
url = self.svc_url + "/isValidUser?"
args = urlencode({"user": user,
"profile": self.svc_profile})
url = url + args
if self.debug:
print("isValidUser: url = '%s'" % url)
try:
val = self.retBoolValue(url)
except Exception:
val = "False"
return val
[docs]
def isUserLoggedIn(self, user):
'''See whether the user identified by the username is currently logged in.
Parameters
----------
user : str
User login name.
Returns
-------
status : bool
``True`` if user is currently logged in, ``False`` otherwise.
Example
-------
>>> from dl import authClient
>>> authClient.isUserLoggedIn('monty')
'''
url = self.svc_url + "/isUserLoggedIn?"
args = urlencode({"user": user,
"profile": self.svc_profile})
url = url + args
if self.debug:
print("isUserLoggedIn: url = '%s'" % url)
try:
val = self.retBoolValue(url)
except Exception:
val = "False"
return val
[docs]
def isTokenLoggedIn(self, token):
'''See whether the user identified by the token is currently logged in.
Parameters
----------
token : str
User login token.
Returns
-------
status : bool
``True`` if token is marked as logged in, ``False`` otherwise.
Example
-------
>>> from dl import authClient
>>> authClient.isTokenLoggedIn(token)
'''
url = self.svc_url + "/isTokenLoggedIn?"
args = urlencode({"token": token,
"profile": self.svc_profile})
url = url + args
if self.debug:
print("isTokenLoggedIn: tok = '%s'" % token)
print("isTokenLoggedIn: url = '%s'" % url)
try:
val = self.retBoolValue(url)
except Exception:
val = "False"
return val
###################################################
# PRIVATE UTILITY METHODS
###################################################
[docs]
def debug(self, debug_val):
'''Toggle debug flag.
'''
self.debug = debug_val
[docs]
def retBoolValue(self, url):
'''Utility method to call a boolean service at the given URL.
'''
response = ""
try:
# Add the auth token to the reauest header.
if self.auth_token != None:
headers = {'X-DL-AuthToken': self.auth_token}
r = requests.get(url, headers=headers)
else:
r = requests.get(url)
response = acToString(r.content)
if r.status_code != 200:
raise Exception(r.content)
except Exception:
return acToString(r.content)
else:
return response
[docs]
def getFromURL(self, svc_url, path, token):
'''Get something from a URL. Return a 'response' object.
'''
try:
hdrs = self.getHeaders(token)
resp = requests.get("%s%s" % (svc_url, path), headers=hdrs)
except Exception as e:
raise dlAuthError(str(e))
return resp
# ###################################
# Authentication Client Handles
# ###################################
[docs]
def getClient():
'''Get a new instance of the authClient client.
Parameters
----------
None
Returns
-------
client : authClient
An authClient object.
Example
-------
>>> from dl import authClient
>>> new_client = authClient.getClient()
'''
return authClient()
ac_client = getClient()
# ##########################################
# Patch the docstrings for module functions
# ##########################################
login.__doc__ = ac_client.login.__doc__
logout.__doc__ = ac_client.logout.__doc__
passwordReset.__doc__ = ac_client.passwordReset.__doc__
whoAmI.__doc__ = ac_client.whoAmI.__doc__
isAlive.__doc__ = ac_client.isAlive.__doc__
isValidToken.__doc__ = ac_client.isValidToken.__doc__
isValidUser.__doc__ = ac_client.isValidUser.__doc__
isValidPassword.__doc__ = ac_client.isValidPassword.__doc__
hasAccess.__doc__ = ac_client.hasAccess.__doc__
isUserLoggedIn.__doc__ = ac_client.isUserLoggedIn.__doc__
isTokenLoggedIn.__doc__ = ac_client.isTokenLoggedIn.__doc__
set_svc_url.__doc__ = ac_client.set_svc_url.__doc__
get_svc_url.__doc__ = ac_client.get_svc_url.__doc__
set_profile.__doc__ = ac_client.set_profile.__doc__
get_profile.__doc__ = ac_client.get_profile.__doc__
# ####################################################################
# Py2/Py3 Compatability Utilities
# ####################################################################
[docs]
def acToString(s):
'''acToString -- Force a return value to be type 'string' for all
Python versions.
'''
if is_py3:
if isinstance(s,bytes):
strval = str(s.decode())
elif isinstance(s,str):
strval = s
else:
if isinstance(s,bytes) or isinstance(s,unicode):
strval = str(s)
else:
strval = s
return strval