Source code for Auth1
#!/usr/bin/python
# Copyright 2012 Rackspace Hosting {{{
#
# Contact - Nate House nathan.house@rackspace.com
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# }}}
import requests
import yaml
import simplejson as json
import os
import sys
import re
from datetime import datetime
import dateutil.parser
from pytz import utc
import keyring
import keyring.backend
from bin._Globals import *
import logging
#Setup logger. Root logger config in _Globals.py
log = logging.getLogger(__name__)
#log.setLevel(logging.DEBUG)
"""
A module to interface into Rackspace Cloud's Auth 1.1 api.
Exported Classes:
Auth1 -- A class that handles the auth and token caching for Rackspace Cloud accounts via Auth 1.1 api.
"""
[docs]class Auth1(object):
"""A class that handles the auth and token caching for Rackspace Cloud accounts.
It only uses api key for auth. You will be prompted for creds if a .cloudAccount.yml
file doesn't exist. Create the object to initially validate. Any time an api
request is needed, call validate() to verify the token.
Variables:
cloudAccount -- List containing the following creds
username -- Cloud username(string)
key -- Cloud api key(string)
token -- Cloud cached token(string)
expires -- Cloud token expiration time(string)
tenant -- Cloud tenant id
cloudAccountUK -- List containing the following UK creds
username -- Cloud UK username(string)
key -- Cloud UK api key(string)
token -- Cloud UK token(string)
expires -- Cloud UK token expiration time(string)
tenant -- Cloud tenant id
endpoints -- Dictionary containing US and UK Auth endpoints
pd -- Current working directory
Public Functions:
__init__ -- Object initialization
validate -- Validates current token stored in cloudAccount.
Private Functions:
_auth -- Authenticate using cloudAccount credentials and endpoints
_updateResults -- Dumps account and endpoints to file
_logResults --
_logError --
"""
cloudAccount = ""
env = ""
region = None
headers = { }
endpoints = dict(
US="https://identity.api.rackspacecloud.com/v1.1/auth",
UK="https://lon.identity.api.rackspacecloud.com/v1.1/auth"
)
pd = os.path.dirname(os.path.realpath(__file__))
def __init__(self, env):
"""Initializes Auth1 object and assigns cloudAccount dict based on env and keyring creds. Calls validate().
Arguments:
env -- The cloud environment(us,uk)(string).
"""
if re.search("us", env, re.IGNORECASE) or re.search("uk", env, re.IGNORECASE):
self.env = env.upper()
self._getKeyring()
self.validate()
[docs] def validate(self, force=False):
"""Checks expires time to current time and re-auths if needed.
Arguments:
force -- Force token re-auth if needed(bool).
Returns:
True or False indicating an existing valid cached token or re-auth success.
"""
if not force:
if dateutil.parser.parse(self.cloudAccount['expires']) > datetime.now(utc):
self._setHeaders()
return True
else:
return self._auth()
else:
return self._auth()
def _auth(self):
"""Re-Authenticates to Rackspace Cloud endpoints using data in cloudAccount lists.
Returns:
True of False indicating success of failure.
"""
credentials = { "credentials":{ "username":self.cloudAccount['username'], "key":self.cloudAccount['key'] } }
headers = {'content-type': 'application/json'}
try:
r = requests.post(self.endpoints[self.env], data=json.dumps(credentials), headers=headers)
if r.status_code == requests.codes.ok:
self._logResults("Success-", r.text)
return self._updateResults(r)
else:
print r.text
self._logResults("Error-", r.text)
return False
except requests.exceptions.RequestException as error:
print "Error when processing request"
#log somewhere
return False
def _updateResults(self, r):
"""Updates cloudAccount.yaml and endpoints.yaml files based on successful auth.
Arguments:
r -- Requests object containing our data
Returns:
True or False depending on successful yaml updating
"""
#should be in a try block
data = json.loads(r.text)
cloudServices = json.loads(json.dumps(data['auth']['serviceCatalog']))
auth = json.loads(json.dumps(data['auth']['token']))
self.cloudAccount['token'] = auth['id']
self.cloudAccount['expires'] = auth['expires']
self._setKeyring() #store the new token and expiration
self._setHeaders() #stores the new token headers
f = file(os.path.join(self.pd,".cloudServices%s.yml" % self.env), 'w')
yaml.dump(cloudServices,f, default_flow_style=False)
f.close()
return True
def _getKeyring(self):
"""
This method checks for keyring entries for Cloud credentials and prompts for them if not present.
Should be called with validate() and initialization.
"""
username = keyring.get_password('cloud_auth%s' % self.env, 'username')
key = keyring.get_password('cloud_auth%s' % self.env, 'key')
#password = keyring.get_password('cloud_auth%s' % self.env, 'password')
token = keyring.get_password('cloud_auth%s' % self.env, 'token')
expires = keyring.get_password('cloud_auth%s' % self.env, 'expires')
if username is None: #prompt for username and api key
username = raw_input("%s Username: " % self.env)
key = raw_input("%s Api key: " % self.env)
token = "invalid"
expires = "2012-09-27T12:17:48.000+01:00"
if key is None and isinstance(self.cloudAccount, str):
key = raw_input("%s Api key: " % self.env)
self.cloudAccount = { "username":username,"key":key,"token":token,"expires":expires }
def _setKeyring(self):
"""
This method sets the keyring with the cloudAccount dict.
"""
try:
keyring.set_password('cloud_auth%s' % self.env, 'username', self.cloudAccount['username'])
keyring.set_password('cloud_auth%s' % self.env, 'key', self.cloudAccount['key'])
keyring.set_password('cloud_auth%s' % self.env, 'token', self.cloudAccount['token'])
keyring.set_password('cloud_auth%s' % self.env, 'expires', self.cloudAccount['expires'])
except keyring.backend.PasswordError:
print "Error storing credentials"
#log somewhere
def _setHeaders(self, noType=False):
"""
Updates json headers with latest token.
"""
if noType:
self.headers = { 'X-Auth-Token': '%s' % self.cloudAccount['token']}
else:
self.headers = { 'X-Auth-Token': '%s' % self.cloudAccount['token'],'content-type': 'application/json'}
def _logResults(self, status, results):
"""
Uses logger to log to file
"""
pass