import webob
from heat import utils
from heat.common import context
+from heat.api.v1 import exception
logger = logging.getLogger(__name__)
@webob.dec.wsgify(RequestClass=wsgi.Request)
def __call__(self, req):
# Read request signature and access id.
+ # If we find KeyStoneCreds in the params we ignore a key error
+ # here so that we can use both authentication methods.
+ # Returning here just means the user didn't supply AWS
+ # authentication and we'll let the app try native keystone next.
logger.info("Checking AWS credentials..")
try:
signature = req.params['Signature']
+ except KeyError:
+ logger.info("No AWS Signature found.")
+ if 'KeyStoneCreds' in req.params:
+ return self.application
+ else:
+ raise exception.HeatIncompleteSignatureError()
+
+ try:
access = req.params['AWSAccessKeyId']
except KeyError:
- # We ignore a key error here so that we can use both
- # authentication methods. Returning here just means
- # the user didn't supply AWS authentication and we'll let
- # the app try native keystone next.
- logger.info("No AWS credentials found.")
- return self.application
+ logger.info("No AWSAccessKeyId found.")
+ if 'KeyStoneCreds' in req.params:
+ return self.application
+ else:
+ raise exception.HeatMissingAuthenticationTokenError()
logger.info("AWS credentials found, checking against keystone.")
# Make a copy of args for authentication and signature verification.
token_id = result['access']['token']['id']
logger.info("AWS authentication successful.")
except (AttributeError, KeyError):
- # FIXME: Should be 404 I think.
logger.info("AWS authentication failure.")
- raise webob.exc.HTTPBadRequest()
+ # Try to extract the reason for failure so we can return the
+ # appropriate AWS error via raising an exception
+ try:
+ reason = result['error']['message']
+ except KeyError:
+ reason = None
+
+ if reason == "EC2 access key not found.":
+ raise exception.HeatInvalidClientTokenIdError()
+ elif reason == "EC2 signature not supplied.":
+ raise exception.HeatSignatureError()
+ else:
+ raise exception.HeatAccessDeniedError()
# Authenticated!
req.headers['X-Auth-EC2-Creds'] = creds_json
"""Heat API exception subclasses - maps API response errors to AWS Errors"""
import webob.exc
+from heat.common import wsgi
class HeatAPIException(webob.exc.HTTPError):
explanation = "Generic HeatAPIException, please use specific subclasses!"
err_type = "Sender"
+ def __init__(self, detail=None):
+ '''
+ Overload HTTPError constructor, so we can create a default serialized
+ body. This is required because not all error responses are processed
+ by the wsgi controller (ie auth errors, which are further up the
+ paste pipeline. We serialize in XML by default (as AWS does)
+ '''
+ webob.exc.HTTPError.__init__(self, detail=detail)
+ serializer = wsgi.XMLResponseSerializer()
+ serializer.default(self, self.get_unserialized_body())
+
def get_unserialized_body(self):
'''
Return a dict suitable for serialization in the wsgi controller
code = 400
title = "Throttling"
explanation = "Request was denied due to request throttling"
+
+
+# Not documented in the AWS docs, authentication failure errors
+class HeatAccessDeniedError(HeatAPIException):
+ '''
+ This is the response given when authentication fails due to user
+ IAM group memberships meaning we deny access
+ '''
+ code = 403
+ title = "AccessDenied"
+ explanation = "User is not authorized to perform action"
+
+
+class HeatSignatureError(HeatAPIException):
+ '''
+ This is the response given when authentication fails due to
+ a bad signature
+ '''
+ code = 403
+ title = "SignatureDoesNotMatch"
+ explanation = ("The request signature we calculated does not match the " +
+ "signature you provided")
# Here we should get API exceptions derived from HeatAPIException
# these implement get_unserialized_body(), which allow us to get
# a dict containing the unserialized error response.
+ # We only need to serialize for JSON content_type, as the
+ # exception body is pre-serialized to the default XML in the
+ # HeatAPIException constructor
# If we get something else here (e.g a webob.exc exception),
# this will fail, and we just return it without serializing,
# which will not conform to the expected AWS error response format
- try:
- err_body = action_result.get_unserialized_body()
- serializer.default(action_result, err_body)
- except:
- logging.warning("Unable to serialize exception response")
+ if content_type == "JSON":
+ try:
+ err_body = action_result.get_unserialized_body()
+ serializer.default(action_result, err_body)
+ except:
+ logging.warning("Unable to serialize exception response")
return action_result