def update(self, req, id, body): """ Updates the server name or password """ if len(req.body) == 0: raise exc.HTTPUnprocessableEntity() if not body: return wsgi.Fault(exc.HTTPUnprocessableEntity()) ctxt = req.environ['nova.context'] update_dict = {} if 'name' in body['server']: name = body['server']['name'] self._validate_server_name(name) update_dict['display_name'] = name.strip() if 'description' in body['server']: description = body['server']['description'] update_dict['display_description'] = description.strip() self._parse_update(ctxt, id, body, update_dict) try: self.compute_api.update(ctxt, id, **update_dict) except exception.NotFound: return wsgi.Fault(exc.HTTPNotFound()) return exc.HTTPNoContent()
def __call__(self, req): if not self.has_authentication(req): return self.authenticate(req) user_id = self.get_user_by_authentication(req) if not user_id: token = req.headers["X-Auth-Token"] msg = _("%(user_id)s could not be found with token '%(token)s'") LOG.warn(msg % locals()) return wsgi.Fault(webob.exc.HTTPUnauthorized()) # Get all valid projects for the user projects = self.auth.get_projects(user_id) if not projects: return wsgi.Fault(webob.exc.HTTPUnauthorized()) project_id = "" path_parts = req.path.split('/') # TODO(wwolf): this v1.1 check will be temporary as # keystone should be taking this over at some point if len(path_parts) > 1 and path_parts[1] in ('v1.1', 'v2'): project_id = path_parts[2] # Check that the project for project_id exists, and that user # is authorized to use it try: self.auth.get_project(project_id) except exception.ProjectNotFound: return wsgi.Fault(webob.exc.HTTPUnauthorized()) if project_id not in [p.id for p in projects]: return wsgi.Fault(webob.exc.HTTPUnauthorized()) else: # As a fallback, set project_id from the headers, which is the v1.0 # behavior. As a last resort, be forgiving to the user and set # project_id based on a valid project of theirs. try: project_id = req.headers["X-Auth-Project-Id"] except KeyError: project_id = projects[0].id is_admin = self.auth.is_admin(user_id) remote_address = getattr(req, 'remote_address', '127.0.0.1') if FLAGS.use_forwarded_for: remote_address = req.headers.get('X-Forwarded-For', remote_address) ctx = context.RequestContext(user_id, project_id, is_admin=is_admin, remote_address=remote_address) req.environ['nova.context'] = ctx if not is_admin and not self.auth.is_project_member( user_id, project_id): msg = _("%(user_id)s must be an admin or a " "member of %(project_id)s") LOG.warn(msg % locals()) return wsgi.Fault(webob.exc.HTTPUnauthorized()) return self.application
def authenticate(self, req): # Unless the request is explicitly made against /<version>/ don't # honor it path_info = req.path_info if len(path_info) > 1: msg = _("Authentication requests must be made against a version " "root (e.g. /v2).") LOG.warn(msg) return wsgi.Fault(webob.exc.HTTPUnauthorized(explanation=msg)) def _get_auth_header(key): """Ensures that the KeyError returned is meaningful.""" try: return req.headers[key] except KeyError as ex: raise KeyError(key) try: username = _get_auth_header('X-Auth-User') key = _get_auth_header('X-Auth-Key') except KeyError as ex: msg = _("Could not find %s in request.") % ex LOG.warn(msg) return wsgi.Fault(webob.exc.HTTPUnauthorized(explanation=msg)) token, user = self._authorize_user(username, key, req) if user and token: res = webob.Response() res.headers['X-Auth-Token'] = token['token_hash'] _x_server_url = 'X-Server-Management-Url' _server_url = 'server_management_url' res.headers[_x_server_url] = token[_server_url] if token['storage_url']: _x_storage_url = 'X-Storage-Url' _storage_url = 'storage_url' res.headers[_x_storage_url] = token[_storage_url] if token['cdn_management_url']: _x_cdn_url = 'X-CDN-Management-Url' _cdn_url = 'cdn_management_url' res.headers[_x_cdn_url] = token[_cdn_url] res.content_type = 'text/plain' res.status = '204' LOG.debug(_("Successfully authenticated '%s'") % username) return res else: return wsgi.Fault(webob.exc.HTTPUnauthorized())
def _error(self, inner, req): LOG.exception(_LE("Caught error: %s"), unicode(inner)) safe = getattr(inner, 'safe', False) headers = getattr(inner, 'headers', None) status = getattr(inner, 'code', 500) if status is None: status = 500 msg_dict = dict(url=req.url, status=status) LOG.info(_LI("%(url)s returned with HTTP %(status)d"), msg_dict) outer = self.status_to_type(status) if headers: outer.headers = headers # NOTE(johannes): We leave the explanation empty here on # purpose. It could possibly have sensitive information # that should not be returned back to the user. See # bugs 868360 and 874472 # NOTE(eglynn): However, it would be over-conservative and # inconsistent with the EC2 API to hide every exception, # including those that are safe to expose, see bug 1021373 if safe: user_locale = req.best_match_language() inner_msg = translate(inner.message, user_locale) outer.explanation = '%s: %s' % (inner.__class__.__name__, inner_msg) notifications.send_api_fault(req.url, status, inner) return wsgi.Fault(outer)
def test_429_fault_json(self): # Test fault serialized to JSON via file-extension and/or header. requests = [ webob.Request.blank('/.json'), webob.Request.blank('/', headers={"Accept": "application/json"}), ] for request in requests: exc = webob.exc.HTTPTooManyRequests # NOTE(aloga): we intentionally pass an integer for the # 'Retry-After' header. It should be then converted to a str fault = wsgi.Fault( exc(explanation='sorry', headers={'Retry-After': 4})) response = request.get_response(fault) expected = { "overLimit": { "message": "sorry", "code": 429, "retryAfter": "4", }, } actual = jsonutils.loads(response.body) self.assertEqual(response.content_type, "application/json") self.assertEqual(expected, actual)
def __call__(self, req): try: return req.get_response(self.application) except Exception as ex: LOG.exception(_("Caught error: %s"), unicode(ex)) exc = webob.exc.HTTPInternalServerError() return wsgi.Fault(exc)
def test_xml_serializer(self): # Ensure that a v1.1 request responds with a v1.1 xmlns. request = webob.Request.blank('/v1.1', headers={"Accept": "application/xml"}) fault = wsgi.Fault(webob.exc.HTTPBadRequest(explanation='scram')) response = request.get_response(fault) self.assertTrue(common.XML_NS_V11 in response.body) self.assertEqual(response.content_type, "application/xml") self.assertEqual(response.status_int, 400)
def __call__(self, req): try: return req.get_response(self.application) except Exception as ex: LOG.exception(_("Caught error: %s"), unicode(ex)) msg_dict = dict(url=req.url, status=500) LOG.info(_("%(url)s returned with HTTP %(status)d") % msg_dict) exc = webob.exc.HTTPInternalServerError() # NOTE(johannes): We leave the explanation empty here on # purpose. It could possibly have sensitive information # that should not be returned back to the user. See # bugs 868360 and 874472 return wsgi.Fault(exc)
def rate_limited_request(self, req, application): """Rate limit the request. If the request should be rate limited, return a 413 status with a Retry-After header giving the time when the request would succeed. """ action_name = self.get_action_name(req) if not action_name: # Not rate limited return application delay = self.get_delay(action_name, req.environ['nova.context'].user_id) if delay: # TODO(gundlach): Get the retry-after format correct. exc = webob.exc.HTTPRequestEntityTooLarge( explanation=('Too many requests.'), headers={'Retry-After': time.time() + delay}) raise os_wsgi.Fault(exc) return application
def test_400_fault_json(self): # Test fault serialized to JSON via file-extension and/or header. requests = [ webob.Request.blank('/.json'), webob.Request.blank('/', headers={"Accept": "application/json"}), ] for request in requests: fault = wsgi.Fault(webob.exc.HTTPBadRequest(explanation='scram')) response = request.get_response(fault) expected = { "badRequest": { "message": "scram", "code": 400, }, } actual = jsonutils.loads(response.body) self.assertEqual(response.content_type, "application/json") self.assertEqual(expected, actual)
def _error(self, req, class_name, explanation, headers=None, status=500, safe=False): LOG.exception(_("Caught error: %s"), explanation) msg_dict = dict(url=req.url, status=status) LOG.info(_("%(url)s returned with HTTP %(status)d") % msg_dict) outer = self.status_to_type(status) if headers: outer.headers = headers # NOTE(johannes): We leave the explanation empty here on # purpose. It could possibly have sensitive information # that should not be returned back to the user. See # bugs 868360 and 874472 # NOTE(eglynn): However, it would be over-conservative and # inconsistent with the EC2 API to hide every exception, # including those that are safe to expose, see bug 1021373 if safe: outer.explanation = '%s: %s' % (class_name, explanation) return wsgi.Fault(outer)
def test_413_fault_json(self): # Test fault serialized to JSON via file-extension and/or header. requests = [ webob.Request.blank('/.json'), webob.Request.blank('/', headers={"Accept": "application/json"}), ] for request in requests: exc = webob.exc.HTTPRequestEntityTooLarge fault = wsgi.Fault( exc(explanation='sorry', headers={'Retry-After': 4})) response = request.get_response(fault) expected = { "overLimit": { "message": "sorry", "code": 413, "retryAfter": 4, }, } actual = jsonutils.loads(response.body) self.assertEqual(response.content_type, "application/json") self.assertEqual(expected, actual)
def raise_api_fault(req): exc = webob.exc.HTTPNotFound(explanation='Raised a webob.exc') return wsgi.Fault(exc)
def test_fault_has_status_int(self): # Ensure the status_int is set correctly on faults. fault = wsgi.Fault(webob.exc.HTTPBadRequest(explanation='what?')) self.assertEqual(fault.status_int, 400)
def raiser(req): raise wsgi.Fault(webob.exc.HTTPForbidden(explanation='whut?'))
def raiser(req): raise wsgi.Fault(webob.exc.HTTPNotFound(explanation='whut?'))