def __call__(self, req): request_id = context.generate_request_id() signature = req.params.get('Signature') if not signature: msg = _("Signature not provided") return ec2_error(req, request_id, "Unauthorized", msg) access = req.params.get('AWSAccessKeyId') if not access: msg = _("Access key not provided") return ec2_error(req, request_id, "Unauthorized", msg) # 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 ec2_error(req, request_id, "Unauthorized", msg) 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), e: LOG.exception("Keystone failure: %s" % e) msg = _("Failure communicating with keystone") return ec2_error(req, request_id, "Unauthorized", msg)
class EC2Token(wsgi.Middleware): """Authenticate an EC2 request with keystone and convert to token.""" @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): # Read request signature and access id. try: signature = req.params['Signature'] access = req.params['AWSAccessKeyId'] except KeyError, e: LOG.exception(e) raise webob.exc.HTTPBadRequest() # Make a copy of args for authentication and signature verification. auth_params = dict(req.params) # Not part of authentication args auth_params.pop('Signature') # Authenticate the request. creds = { 'ec2Credentials': { 'access': access, 'signature': signature, 'host': req.host, 'verb': req.method, 'path': req.path, 'params': auth_params, } } creds_json = utils.dumps(creds) headers = {'Content-Type': 'application/json'} # Disable "has no x member" pylint error # for httplib and urlparse # pylint: disable-msg=E1101 o = urlparse.urlparse(FLAGS.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().read() conn.close() # NOTE(vish): We could save a call to keystone by # having keystone return token, tenant, # user, and roles from this call. result = utils.loads(response) try: token_id = result['access']['token']['id'] except (AttributeError, KeyError), e: LOG.exception(e) raise webob.exc.HTTPBadRequest()
def __call__(self, req): # Read request signature and access id. try: signature = req.params['Signature'] access = req.params['AWSAccessKeyId'] except KeyError: raise webob.exc.HTTPBadRequest() # Make a copy of args for authentication and signature verification. auth_params = dict(req.params) # Not part of authentication args auth_params.pop('Signature') # Authenticate the request. creds = { 'ec2Credentials': { 'access': access, 'signature': signature, 'host': req.host, 'verb': req.method, 'path': req.path, 'params': auth_params, } } creds_json = jsonutils.dumps(creds) headers = {'Content-Type': 'application/json'} # Disable 'has no x member' pylint error # for httplib and urlparse # pylint: disable-msg=E1101 o = urllib.parse.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().read() conn.close() # NOTE(vish): We could save a call to keystone by # having keystone return token, tenant, # user, and roles from this call. result = jsonutils.loads(response) try: token_id = result['access']['token']['id'] except (AttributeError, KeyError): raise webob.exc.HTTPBadRequest() # Authenticated! req.headers['X-Auth-Token'] = token_id return self.application
def http_connect_raw(ipaddr, port, method, path, headers=None, query_string=None, ssl=False, key_file=None, cert_file=None): """Helper function to create an HTTPConnection object. If ssl is set True, HTTPSConnection will be used. However, if ssl=False, BufferedHTTPConnection will be used, which is buffered for backend Swift services. :param ipaddr: IPv4 address to connect to :param port: port to connect to :param method: HTTP method to request ('GET', 'PUT', 'POST', etc.) :param path: request path :param headers: dictionary of headers :param query_string: request query string :param ssl: set True if SSL should be used (default: False) :param key_file Private key file (not needed if cert_file has private key) :param cert_file Certificate file (Keystore) :returns: HTTPConnection object """ if ssl: conn = httplib.HTTPSConnection('%s:%s' % (ipaddr, port), key_file=key_file, cert_file=cert_file) else: conn = BufferedHTTPConnection('%s:%s' % (ipaddr, port)) if query_string: path += '?' + query_string conn.path = path conn.putrequest(method, path) if headers: for header, value in headers.iteritems(): conn.putheader(header, value) conn.endheaders() return conn
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 = 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'} 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 execute_task(task, headers, args): """ Executes a task to a url with the given args. Args: task: A celery Task instance. headers: A dictionary of headers for the task. args: A dictionary of arguments for the request. Contains the task body. Returns: The status code of the task fetch upon success. Raises: The current function to retry. """ start_time = datetime.datetime.utcnow() content_length = len(args['body']) loggable_args = {key: args[key] for key in args if key not in ['task_name', 'body', 'payload']} loggable_args['body_length'] = content_length logger.info('Running {}\n' 'Headers: {}\n' 'Args: {}'.format(args['task_name'], headers, loggable_args)) url = urlparse(args['url']) redirects_left = 1 while True: urlpath = url.path if url.query: urlpath += "?" + url.query method = args['method'] if args['expires'] <= datetime.datetime.now(): # We do this check because the expires attribute in # celery is not passed to retried tasks. This is a # documented bug in celery. logger.error( "Task %s with id %s has expired with expiration date %s" % ( args['task_name'], task.request.id, args['expires'])) item = TaskName.get_by_key_name(args['task_name']) celery.control.revoke(task.request.id) db.delete(item) return if (args['max_retries'] != 0 and task.request.retries >= args['max_retries']): logger.error("Task %s with id %s has exceeded retries: %s" % ( args['task_name'], task.request.id, args['max_retries'])) item = TaskName.get_by_key_name(args['task_name']) celery.control.revoke(task.request.id) db.delete(item) return # Targets do not get X-Forwarded-Proto from nginx, they use haproxy port. headers['X-Forwarded-Proto'] = url.scheme if url.scheme == 'http': connection = httplib.HTTPConnection(remote_host, url.port) elif url.scheme == 'https': connection = httplib.HTTPSConnection(remote_host, url.port) else: logger.error("Task %s tried to use url scheme %s, " "which is not supported." % ( args['task_name'], url.scheme)) skip_host = False if 'host' in headers or 'Host' in headers: skip_host = True skip_accept_encoding = False if 'accept-encoding' in headers or 'Accept-Encoding' in headers: skip_accept_encoding = True connection.putrequest(method, urlpath, skip_host=skip_host, skip_accept_encoding=skip_accept_encoding) # Update the task headers headers['X-AppEngine-TaskRetryCount'] = str(task.request.retries) headers['X-AppEngine-TaskExecutionCount'] = str(task.request.retries) for header in headers: connection.putheader(header, headers[header]) if 'content-type' not in headers or 'Content-Type' not in headers: if url.query: connection.putheader('content-type', 'application/octet-stream') else: connection.putheader('content-type', 'application/x-www-form-urlencoded') connection.putheader("Content-Length", str(content_length)) retries = int(task.request.retries) + 1 wait_time = get_wait_time(retries, args) try: connection.endheaders() if args["body"]: connection.send(args['body']) response = connection.getresponse() response.read() response.close() except (BadStatusLine, SocketError): logger.warning( '{task} failed before receiving response. It will retry in {wait} ' 'seconds.'.format(task=args['task_name'], wait=wait_time)) raise task.retry(countdown=wait_time) if 200 <= response.status < 300: # Task successful. item = TaskName.get_by_key_name(args['task_name']) db.delete(item) time_elapsed = datetime.datetime.utcnow() - start_time logger.info( '{task} received status {status} from {url} [time elapsed: {te}]'. \ format(task=args['task_name'], status=response.status, url=url, te=str(time_elapsed))) return response.status elif response.status == 302: redirect_url = response.getheader('Location') logger.info( "Task %s asked us to redirect to %s, so retrying there." % ( args['task_name'], redirect_url)) url = urlparse(redirect_url) if redirects_left == 0: raise task.retry(countdown=wait_time) redirects_left -= 1 else: message = ('Received a {status} for {task}. ' 'Retrying in {wait} secs.'.format(status=response.status, task=args['task_name'], wait=wait_time)) logger.warning(message) raise task.retry(countdown=wait_time)
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, "Unauthorized", msg, status=400) access = req.params.get('AWSAccessKeyId') if not access: msg = _("Access key not provided") return faults.ec2_error_response(request_id, "Unauthorized", 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 FLAGS.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(FLAGS.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, "Unauthorized", msg, status=400) 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.exception(_("Keystone failure: %s") % e) msg = _("Failure communicating with keystone") return faults.ec2_error_response(request_id, "Unauthorized", msg, status=400) remote_address = req.remote_addr if FLAGS.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['synaps.context'] = ctxt return self.application