def test_fault_call(self): # Ensure proper EC2 response on faults. message = "test message" ex = webob.exc.HTTPNotFound(explanation=message) fault = faults.Fault(ex) req = wsgi.Request.blank("/test") req.GET["AWSAccessKeyId"] = "test_user_id:test_project_id" self.mox.StubOutWithMock(faults, "ec2_error_response") faults.ec2_error_response(mox.IgnoreArg(), "HTTPNotFound", message=message, status=ex.status_int) self.mox.ReplayAll() fault(req)
def test_fault_call(self): # Ensure proper EC2 response on faults. message = 'test message' ex = webob.exc.HTTPNotFound(explanation=message) fault = faults.Fault(ex) req = Request.blank('/test') req.GET['AWSAccessKeyId'] = "test_user_id:test_project_id" self.mox.StubOutWithMock(faults, 'ec2_error_response') faults.ec2_error_response(mox.IgnoreArg(), 'HTTPNotFound', message=message, status=ex.status_int) self.mox.ReplayAll() fault(req)
def ec2_error_ex(ex, req, code=None, message=None, unexpected=False): """ Return an EC2 error response based on passed exception and log the exception on an appropriate log level: * DEBUG: expected errors * ERROR: unexpected errors All expected errors are treated as client errors and 4xx HTTP status codes are always returned for them. Unexpected 5xx errors may contain sensitive information, suppress their messages for security. """ if not code: code = exception_to_ec2code(ex) status = getattr(ex, 'code', None) if not status: status = 500 if unexpected: log_fun = LOG.error if ex.args and status < 500: log_msg = _("Unexpected %(ex_name)s raised: %(ex_str)s") else: log_msg = _("Unexpected %(ex_name)s raised") else: log_fun = LOG.debug if ex.args: log_msg = _("%(ex_name)s raised: %(ex_str)s") else: log_msg = _("%(ex_name)s raised") # NOTE(jruzicka): For compatibility with EC2 API, treat expected # exceptions as client (4xx) errors. The exception error code is 500 # by default and most exceptions inherit this from NovaException even # though they are actually client errors in most cases. if status >= 500: status = 400 context = req.environ['nova.context'] request_id = context.request_id log_msg_args = { 'ex_name': type(ex).__name__, 'ex_str': unicode(ex) } log_fun(log_msg % log_msg_args, context=context) if ex.args and not message and (not unexpected or status < 500): message = unicode(ex.args[0]) if unexpected: # Log filtered environment for unexpected errors. env = req.environ.copy() for k in env.keys(): if not isinstance(env[k], six.string_types): env.pop(k) log_fun(_('Environment: %s') % jsonutils.dumps(env)) if not message: message = _('Unknown error occurred.') return faults.ec2_error_response(request_id, code, message, status=status)
def ec2_error_ex(ex, req, code=None, message=None, unexpected=False): """ Return an EC2 error response based on passed exception and log the exception on an appropriate log level: * DEBUG: expected errors * ERROR: unexpected client (4xx) errors * CRITICAL: unexpected server (5xx) errors Unexpected 5xx errors may contain sensitive information, supress their messages for security. """ if not code: code = exception_to_ec2code(ex) status = getattr(ex, 'code', None) if not status: status = 500 if unexpected: log_msg = _("Unexpected %(ex_name)s raised") if status >= 500: log_fun = LOG.critical else: log_fun = LOG.error if ex.args: log_msg = _("Unexpected %(ex_name)s raised: %(ex_str)s") else: log_fun = LOG.debug if ex.args: log_msg = _("%(ex_name)s raised: %(ex_str)s") else: log_msg = _("%(ex_name)s raised") context = req.environ['nova.context'] request_id = context.request_id log_msg_args = { 'ex_name': type(ex).__name__, 'ex_str': unicode(ex) } log_fun(log_msg % log_msg_args, context=context) if ex.args and not message and (not unexpected or status < 500): message = unicode(ex.args[0]) if unexpected: # Log filtered environment for unexpected errors. env = req.environ.copy() for k in env.keys(): if not isinstance(env[k], basestring): env.pop(k) log_fun(_('Environment: %s') % jsonutils.dumps(env)) if not message: message = _('Unknown error occured.') # note(jruzicka): To preserve old behavior, all exceptions are returned # with 400 status until EC2 errors are properly fixed. return faults.ec2_error_response(request_id, code, message, status=400)
def ec2_error_ex(ex, req, code=None, message=None, unexpected=False): """ Return an EC2 error response based on passed exception and log the exception on an appropriate log level: * DEBUG: expected errors * ERROR: unexpected client (4xx) errors * CRITICAL: unexpected server (5xx) errors Unexpected 5xx errors may contain sensitive information, supress their messages for security. """ if not code: code = exception_to_ec2code(ex) status = getattr(ex, 'code', None) if not status: status = 500 if unexpected: log_msg = _("Unexpected %(ex_name)s raised") if status >= 500: log_fun = LOG.critical else: log_fun = LOG.error if ex.args: log_msg = _("Unexpected %(ex_name)s raised: %(ex_str)s") else: log_fun = LOG.debug if ex.args: log_msg = _("%(ex_name)s raised: %(ex_str)s") else: log_msg = _("%(ex_name)s raised") context = req.environ['nova.context'] request_id = context.request_id log_msg_args = {'ex_name': type(ex).__name__, 'ex_str': unicode(ex)} log_fun(log_msg % log_msg_args, context=context) if ex.args and not message and (not unexpected or status < 500): message = unicode(ex.args[0]) if unexpected: # Log filtered environment for unexpected errors. env = req.environ.copy() for k in env.keys(): if not isinstance(env[k], basestring): env.pop(k) log_fun(_('Environment: %s') % jsonutils.dumps(env)) if not message: message = _('Unknown error occured.') # note(jruzicka): To preserve old behavior, all exceptions are returned # with 400 status until EC2 errors are properly fixed. return faults.ec2_error_response(request_id, code, message, status=400)
def __call__(self, req): request_id = common_context.generate_request_id() signature = req.params.get('Signature') if not signature: msg = _("Signature not provided") return faults.ec2_error_response(request_id, "AuthFailure", msg, status=400) access = req.params.get('AWSAccessKeyId') if not access: msg = _("Access key not provided") return faults.ec2_error_response(request_id, "AuthFailure", msg, status=400) # Make a copy of args for authentication and signature verification. auth_params = dict(req.params) # Not part of authentication args auth_params.pop('Signature') cred_dict = { 'access': access, 'signature': signature, 'host': req.host, 'verb': req.method, 'path': req.path, 'params': auth_params, } if "ec2" in CONF.keystone_ec2_url: creds = {'ec2Credentials': cred_dict} else: creds = {'auth': {'OS-KSEC2:ec2Credentials': cred_dict}} creds_json = jsonutils.dumps(creds) headers = {'Content-Type': 'application/json'} o = urlparse.urlparse(CONF.keystone_ec2_url) if o.scheme == "http": conn = httplib.HTTPConnection(o.netloc) else: conn = httplib.HTTPSConnection(o.netloc) conn.request('POST', o.path, body=creds_json, headers=headers) response = conn.getresponse() data = response.read() if response.status != 200: if response.status == 401: msg = response.reason else: msg = _("Failure communicating with keystone") return faults.ec2_error_response(request_id, "AuthFailure", msg, status=response.status) result = jsonutils.loads(data) conn.close() try: token_id = result['access']['token']['id'] user_id = result['access']['user']['id'] project_id = result['access']['token']['tenant']['id'] user_name = result['access']['user'].get('name') project_name = result['access']['token']['tenant'].get('name') roles = [role['name'] for role in result['access']['user']['roles']] except (AttributeError, KeyError) as e: LOG.error(_LE("Keystone failure: %s"), e) msg = _("Failure communicating with keystone") return faults.ec2_error_response(request_id, "AuthFailure", msg, status=400) remote_address = req.remote_addr if CONF.use_forwarded_for: remote_address = req.headers.get('X-Forwarded-For', remote_address) catalog = result['access']['serviceCatalog'] ctxt = context.RequestContext(user_id, project_id, user_name=user_name, project_name=project_name, roles=roles, auth_token=token_id, remote_address=remote_address, service_catalog=catalog) req.environ['nova.context'] = ctxt return self.application
def __call__(self, req): # NOTE(alevine) We need to calculate the hash here because # subsequent access to request modifies the req.body so the hash # calculation will yield invalid results. body_hash = hashlib.sha256(req.body).hexdigest() request_id = common_context.generate_request_id() signature = self._get_signature(req) if not signature: msg = _("Signature not provided") return faults.ec2_error_response(request_id, "AuthFailure", msg, status=400) access = self._get_access(req) if not access: msg = _("Access key not provided") return faults.ec2_error_response(request_id, "AuthFailure", msg, status=400) if "X-Amz-Signature" in req.params or "Authorization" in req.headers: auth_params = {} else: # Make a copy of args for authentication and signature verification auth_params = dict(req.params) # Not part of authentication args auth_params.pop("Signature", None) cred_dict = { "access": access, "signature": signature, "host": req.host, "verb": req.method, "path": req.path, "params": auth_params, "headers": req.headers, "body_hash": body_hash, } if "ec2" in CONF.keystone_ec2_url: creds = {"ec2Credentials": cred_dict} else: creds = {"auth": {"OS-KSEC2:ec2Credentials": cred_dict}} creds_json = jsonutils.dumps(creds) headers = {"Content-Type": "application/json"} verify = not CONF.keystone_ec2_insecure if verify and CONF.ssl.ca_file: verify = CONF.ssl.ca_file cert = None if CONF.ssl.cert_file and CONF.ssl.key_file: cert = (CONF.ssl.cert_file, CONF.ssl.key_file) elif CONF.ssl.cert_file: cert = CONF.ssl.cert_file response = requests.request( "POST", CONF.keystone_ec2_url, data=creds_json, headers=headers, verify=verify, cert=cert ) status_code = response.status_code if status_code != 200: msg = response.reason return faults.ec2_error_response(request_id, "AuthFailure", msg, status=status_code) result = response.json() try: token_id = result["access"]["token"]["id"] user_id = result["access"]["user"]["id"] project_id = result["access"]["token"]["tenant"]["id"] user_name = result["access"]["user"].get("name") project_name = result["access"]["token"]["tenant"].get("name") roles = [role["name"] for role in result["access"]["user"]["roles"]] except (AttributeError, KeyError) as e: LOG.error(_LE("Keystone failure: %s"), e) msg = _("Failure parsing response from keystone: %s") % e return faults.ec2_error_response(request_id, "AuthFailure", msg, status=400) remote_address = req.remote_addr if CONF.use_forwarded_for: remote_address = req.headers.get("X-Forwarded-For", remote_address) catalog = result["access"]["serviceCatalog"] ctxt = context.RequestContext( user_id, project_id, user_name=user_name, project_name=project_name, roles=roles, auth_token=token_id, remote_address=remote_address, service_catalog=catalog, ) req.environ["nova.context"] = ctxt return self.application
def __call__(self, req): # NOTE(alevine) We need to calculate the hash here because # subsequent access to request modifies the req.body so the hash # calculation will yield invalid results. body_hash = hashlib.sha256(req.body).hexdigest() request_id = common_context.generate_request_id() signature = self._get_signature(req) if not signature: msg = _("Signature not provided") return faults.ec2_error_response(request_id, "AuthFailure", msg, status=400) access = self._get_access(req) if not access: msg = _("Access key not provided") return faults.ec2_error_response(request_id, "AuthFailure", msg, status=400) if 'X-Amz-Signature' in req.params or 'Authorization' in req.headers: auth_params = {} else: # Make a copy of args for authentication and signature verification auth_params = dict(req.params) # Not part of authentication args auth_params.pop('Signature', None) cred_dict = { 'access': access, 'signature': signature, 'host': req.host, 'verb': req.method, 'path': req.path, 'params': auth_params, 'headers': req.headers, 'body_hash': body_hash } if "ec2" in CONF.keystone_ec2_url: creds = {'ec2Credentials': cred_dict} else: creds = {'auth': {'OS-KSEC2:ec2Credentials': cred_dict}} creds_json = jsonutils.dumps(creds) headers = {'Content-Type': 'application/json'} verify = not CONF.keystone_ec2_insecure if verify and CONF.ssl.ca_file: verify = CONF.ssl.ca_file cert = None if CONF.ssl.cert_file and CONF.ssl.key_file: cert = (CONF.ssl.cert_file, CONF.ssl.key_file) elif CONF.ssl.cert_file: cert = CONF.ssl.cert_file response = requests.request('POST', CONF.keystone_ec2_url, data=creds_json, headers=headers, verify=verify, cert=cert) status_code = response.status_code if status_code != 200: msg = response.reason return faults.ec2_error_response(request_id, "AuthFailure", msg, status=status_code) result = response.json() try: token_id = result['access']['token']['id'] user_id = result['access']['user']['id'] project_id = result['access']['token']['tenant']['id'] user_name = result['access']['user'].get('name') project_name = result['access']['token']['tenant'].get('name') roles = [role['name'] for role in result['access']['user']['roles']] except (AttributeError, KeyError) as e: LOG.error(_LE("Keystone failure: %s"), e) msg = _("Failure parsing response from keystone: %s") % e return faults.ec2_error_response(request_id, "AuthFailure", msg, status=400) remote_address = req.remote_addr if CONF.use_forwarded_for: remote_address = req.headers.get('X-Forwarded-For', remote_address) catalog = result['access']['serviceCatalog'] ctxt = context.RequestContext(user_id, project_id, user_name=user_name, project_name=project_name, roles=roles, auth_token=token_id, remote_address=remote_address, service_catalog=catalog) req.environ['nova.context'] = ctxt return self.application
def __call__(self, req): request_id = context.generate_request_id() signature = req.params.get("Signature") if not signature: msg = _("Signature not provided") return faults.ec2_error_response(request_id, "AuthFailure", msg, status=400) access = req.params.get("AWSAccessKeyId") if not access: msg = _("Access key not provided") return faults.ec2_error_response(request_id, "AuthFailure", msg, status=400) # Make a copy of args for authentication and signature verification. auth_params = dict(req.params) # Not part of authentication args auth_params.pop("Signature") cred_dict = { "access": access, "signature": signature, "host": req.host, "verb": req.method, "path": req.path, "params": auth_params, } if "ec2" in CONF.keystone_ec2_url: creds = {"ec2Credentials": cred_dict} else: creds = {"auth": {"OS-KSEC2:ec2Credentials": cred_dict}} creds_json = jsonutils.dumps(creds) headers = {"Content-Type": "application/json"} o = urlparse.urlparse(CONF.keystone_ec2_url) if o.scheme == "http": conn = httplib.HTTPConnection(o.netloc) else: conn = httplib.HTTPSConnection(o.netloc) conn.request("POST", o.path, body=creds_json, headers=headers) response = conn.getresponse() data = response.read() if response.status != 200: if response.status == 401: msg = response.reason else: msg = _("Failure communicating with keystone") return faults.ec2_error_response(request_id, "AuthFailure", msg, status=response.status) result = jsonutils.loads(data) conn.close() try: token_id = result["access"]["token"]["id"] user_id = result["access"]["user"]["id"] project_id = result["access"]["token"]["tenant"]["id"] user_name = result["access"]["user"].get("name") project_name = result["access"]["token"]["tenant"].get("name") roles = [role["name"] for role in result["access"]["user"]["roles"]] except (AttributeError, KeyError) as e: LOG.error(_LE("Keystone failure: %s"), e) msg = _("Failure communicating with keystone") return faults.ec2_error_response(request_id, "AuthFailure", msg, status=400) remote_address = req.remote_addr if CONF.use_forwarded_for: remote_address = req.headers.get("X-Forwarded-For", remote_address) catalog = result["access"]["serviceCatalog"] ctxt = context.RequestContext( user_id, project_id, user_name=user_name, project_name=project_name, roles=roles, auth_token=token_id, remote_address=remote_address, service_catalog=catalog, ) req.environ["nova.context"] = ctxt return self.application
def __call__(self, req): request_id = common_context.generate_request_id() signature = req.params.get('Signature') if not signature: msg = _("Signature not provided") return faults.ec2_error_response(request_id, "AuthFailure", msg, status=400) access = req.params.get('AWSAccessKeyId') if not access: msg = _("Access key not provided") return faults.ec2_error_response(request_id, "AuthFailure", msg, status=400) # Make a copy of args for authentication and signature verification. auth_params = dict(req.params) # Not part of authentication args auth_params.pop('Signature') cred_dict = { 'access': access, 'signature': signature, 'host': req.host, 'verb': req.method, 'path': req.path, 'params': auth_params, } if "ec2" in CONF.keystone_ec2_url: creds = {'ec2Credentials': cred_dict} else: creds = {'auth': {'OS-KSEC2:ec2Credentials': cred_dict}} creds_json = jsonutils.dumps(creds) headers = {'Content-Type': 'application/json'} verify = not CONF.keystone_ec2_insecure if verify and CONF.ssl.ca_file: verify = CONF.ssl.ca_file cert = None if CONF.ssl.cert_file and CONF.ssl.key_file: cert = (CONF.ssl.cert_file, CONF.ssl.key_file) elif CONF.ssl.cert_file: cert = CONF.ssl.cert_file response = requests.request('POST', CONF.keystone_ec2_url, data=creds_json, headers=headers, verify=verify, cert=cert) status_code = response.status_code if status_code != 200: if status_code == 401: msg = response.reason else: msg = _("Failure communicating with keystone") return faults.ec2_error_response(request_id, "AuthFailure", msg, status=status_code) result = response.json() try: token_id = result['access']['token']['id'] user_id = result['access']['user']['id'] project_id = result['access']['token']['tenant']['id'] user_name = result['access']['user'].get('name') project_name = result['access']['token']['tenant'].get('name') roles = [role['name'] for role in result['access']['user']['roles']] except (AttributeError, KeyError) as e: LOG.error(_LE("Keystone failure: %s"), e) msg = _("Failure communicating with keystone") return faults.ec2_error_response(request_id, "AuthFailure", msg, status=400) remote_address = req.remote_addr if CONF.use_forwarded_for: remote_address = req.headers.get('X-Forwarded-For', remote_address) catalog = result['access']['serviceCatalog'] ctxt = context.RequestContext(user_id, project_id, user_name=user_name, project_name=project_name, roles=roles, auth_token=token_id, remote_address=remote_address, service_catalog=catalog) req.environ['nova.context'] = ctxt return self.application