def __call__(self, target, creds, enforcer): """Check http: rules by calling to a remote server. This example implementation simply verifies that the response is exactly 'True'. """ url = ('http:' + self.match) % target data = {'target': jsonutils.dumps(target), 'credentials': jsonutils.dumps(creds)} post_data = urllib.urlencode(data) f = urllib2.urlopen(url, post_data) return f.read() == "True"
def __call__(self, target, creds, enforcer): """Check http: rules by calling to a remote server. This example implementation simply verifies that the response is exactly 'True'. """ url = ('http:' + self.match) % target data = {'target': jsonutils.dumps(target), 'credentials': jsonutils.dumps(creds)} post_data = urlparse.urlencode(data) f = urlrequest.urlopen(url, post_data) return f.read() == "True"
def _pack_json_msg(self, msg): """Qpid cannot serialize dicts containing strings longer than 65535 characters. This function dumps the message content to a JSON string, which Qpid is able to handle. :param msg: May be either a Qpid Message object or a bare dict. :returns: A Qpid Message with its content field JSON encoded. """ try: msg.content = jsonutils.dumps(msg.content) except AttributeError: # Need to have a Qpid message so we can set the content_type. msg = qpid_messaging.Message(jsonutils.dumps(msg)) msg.content_type = JSON_CONTENT_TYPE return msg
def __init__(self, session, callback, node_name, node_opts, link_name, link_opts): """Declare a queue on an amqp session. 'session' is the amqp session to use 'callback' is the callback to call when messages are received 'node_name' is the first part of the Qpid address string, before ';' 'node_opts' will be applied to the "x-declare" section of "node" in the address string. 'link_name' goes into the "name" field of the "link" in the address string 'link_opts' will be applied to the "x-declare" section of "link" in the address string. """ self.callback = callback self.receiver = None self.session = None addr_opts = { "create": "always", "node": {"type": "topic", "x-declare": {"durable": True, "auto-delete": True}}, "link": { "name": link_name, "durable": True, "x-declare": {"durable": False, "auto-delete": True, "exclusive": False}, }, } addr_opts["node"]["x-declare"].update(node_opts) addr_opts["link"]["x-declare"].update(link_opts) self.address = "%s ; %s" % (node_name, jsonutils.dumps(addr_opts)) self.reconnect(session)
def format(self, record): message = { "message": record.getMessage(), "asctime": self.formatTime(record, self.datefmt), "name": record.name, "msg": record.msg, "args": record.args, "levelname": record.levelname, "levelno": record.levelno, "pathname": record.pathname, "filename": record.filename, "module": record.module, "lineno": record.lineno, "funcname": record.funcName, "created": record.created, "msecs": record.msecs, "relative_created": record.relativeCreated, "thread": record.thread, "thread_name": record.threadName, "process_name": record.processName, "process": record.process, "traceback": None, } if hasattr(record, "extra"): message["extra"] = record.extra if record.exc_info: message["traceback"] = self.formatException(record.exc_info) return jsonutils.dumps(message)
def encode(self, version, target, json_msg): """This is the main encoding function. It takes a target and a message and returns a tuple consisting of a JSON serialized metadata object, a JSON serialized (and optionally encrypted) message, and a signature. :param version: the current envelope version :param target: The name of the target service (usually with hostname) :param json_msg: a serialized json message object """ ticket = self._get_ticket(target) metadata = jsonutils.dumps({'source': self._name, 'destination': target, 'timestamp': time.time(), 'nonce': _get_nonce(), 'esek': ticket.esek, 'encryption': self._encrypt}) message = json_msg if self._encrypt: message = self._crypto.encrypt(ticket.ekey, message) signature = self._crypto.sign(ticket.skey, version + metadata + message) return (metadata, message, signature)
def __init__(self, session, node_name, node_opts=None): """Init the Publisher class with the exchange_name, routing_key, and other options """ self.sender = None self.session = session addr_opts = { "create": "always", "node": { "type": "topic", "x-declare": { "durable": False, # auto-delete isn't implemented for exchanges in qpid, # but put in here anyway "auto-delete": True, }, }, } if node_opts: addr_opts["node"]["x-declare"].update(node_opts) self.address = "%s ; %s" % (node_name, jsonutils.dumps(addr_opts)) self.reconnect(session)
def format(self, record): message = { 'message': record.getMessage(), 'asctime': self.formatTime(record, self.datefmt), 'name': record.name, 'msg': record.msg, 'args': record.args, 'levelname': record.levelname, 'levelno': record.levelno, 'pathname': record.pathname, 'filename': record.filename, 'module': record.module, 'lineno': record.lineno, 'funcname': record.funcName, 'created': record.created, 'msecs': record.msecs, 'relative_created': record.relativeCreated, 'thread': record.thread, 'thread_name': record.threadName, 'process_name': record.processName, 'process': record.process, 'traceback': None } if hasattr(record, 'extra'): message['extra'] = record.extra if record.exc_info: message['traceback'] = self.formatException(record.exc_info) return jsonutils.dumps(message)
def get_ticket(self, source, target, crypto, key): # prepare metadata md = { 'requestor': source, 'target': target, 'timestamp': time.time(), 'nonce': struct.unpack('Q', os.urandom(8))[0] } metadata = base64.b64encode(jsonutils.dumps(md)) # sign metadata signature = crypto.sign(key, metadata) # HTTP request reply = self._get_ticket({ 'metadata': metadata, 'signature': signature }) # verify reply signature = crypto.sign(key, (reply['metadata'] + reply['ticket'])) if signature != reply['signature']: raise InvalidEncryptedTicket(md['source'], md['destination']) md = jsonutils.loads(base64.b64decode(reply['metadata'])) if ((md['source'] != source or md['destination'] != target or md['expiration'] < time.time())): raise InvalidEncryptedTicket(md['source'], md['destination']) # return ticket data tkt = jsonutils.loads(crypto.decrypt(key, reply['ticket'])) return tkt, md['expiration']
def serialize_remote_exception(failure_info): """Prepares exception data to be sent over rpc. Failure_info should be a sys.exc_info() tuple. """ tb = traceback.format_exception(*failure_info) failure = failure_info[1] LOG.error(_("Returning exception %s to caller"), unicode(failure)) LOG.error(tb) kwargs = {} if hasattr(failure, 'kwargs'): kwargs = failure.kwargs data = { 'class': str(failure.__class__.__name__), 'module': str(failure.__class__.__module__), 'message': unicode(failure), 'tb': tb, 'args': failure.args, 'kwargs': kwargs } json_data = jsonutils.dumps(data) return json_data
def get_ticket(self, source, target, crypto, key): # prepare metadata md = {'requestor': source, 'target': target, 'timestamp': time.time(), 'nonce': struct.unpack('Q', os.urandom(8))[0]} metadata = base64.b64encode(jsonutils.dumps(md)) # sign metadata signature = crypto.sign(key, metadata) # HTTP request reply = self._get_ticket({'metadata': metadata, 'signature': signature}) # verify reply signature = crypto.sign(key, (reply['metadata'] + reply['ticket'])) if signature != reply['signature']: raise InvalidEncryptedTicket(md['source'], md['destination']) md = jsonutils.loads(base64.b64decode(reply['metadata'])) if ((md['source'] != source or md['destination'] != target or md['expiration'] < time.time())): raise InvalidEncryptedTicket(md['source'], md['destination']) # return ticket data tkt = jsonutils.loads(crypto.decrypt(key, reply['ticket'])) return tkt, md['expiration']
def serialize_msg(raw_msg): # NOTE(russellb) See the docstring for _RPC_ENVELOPE_VERSION for more # information about this format. msg = {_VERSION_KEY: _RPC_ENVELOPE_VERSION, _MESSAGE_KEY: jsonutils.dumps(raw_msg)} return msg
def format(self, record): message = {'message': record.getMessage(), 'asctime': self.formatTime(record, self.datefmt), 'name': record.name, 'msg': record.msg, 'args': record.args, 'levelname': record.levelname, 'levelno': record.levelno, 'pathname': record.pathname, 'filename': record.filename, 'module': record.module, 'lineno': record.lineno, 'funcname': record.funcName, 'created': record.created, 'msecs': record.msecs, 'relative_created': record.relativeCreated, 'thread': record.thread, 'thread_name': record.threadName, 'process_name': record.processName, 'process': record.process, 'traceback': None} if hasattr(record, 'extra'): message['extra'] = record.extra if record.exc_info: message['traceback'] = self.formatException(record.exc_info) return jsonutils.dumps(message)
def serialize_remote_exception(failure_info, log_failure=True): """Prepares exception data to be sent over rpc. Failure_info should be a sys.exc_info() tuple. """ tb = traceback.format_exception(*failure_info) failure = failure_info[1] if log_failure: LOG.error(_("Returning exception %s to caller"), six.text_type(failure)) LOG.error(tb) kwargs = {} if hasattr(failure, 'kwargs'): kwargs = failure.kwargs data = { 'class': str(failure.__class__.__name__), 'module': str(failure.__class__.__module__), 'message': six.text_type(failure), 'tb': tb, 'args': failure.args, 'kwargs': kwargs } json_data = jsonutils.dumps(data) return json_data
def encode(self, version, target, json_msg): """This is the main encoding function. It takes a target and a message and returns a tuple consisting of a JSON serialized metadata object, a JSON serialized (and optionally encrypted) message, and a signature. :param version: the current envelope version :param target: The name of the target service (usually with hostname) :param json_msg: a serialized json message object """ ticket = self._get_ticket(target) metadata = jsonutils.dumps({ 'source': self._name, 'destination': target, 'timestamp': time.time(), 'nonce': _get_nonce(), 'esek': ticket.esek, 'encryption': self._encrypt }) message = json_msg if self._encrypt: message = self._crypto.encrypt(ticket.ekey, message) signature = self._crypto.sign(ticket.skey, version + metadata + message) return (metadata, message, signature)
def notify(_context, message): """Notifies the recipient of the desired event given the model. Log notifications using openstack's default logging system""" priority = message.get("priority", CONF.default_notification_level) priority = priority.lower() logger = logging.getLogger("heat.openstack.common.notification.%s" % message["event_type"]) getattr(logger, priority)(jsonutils.dumps(message))
def __init__(self, conf, session, callback, node_name, node_opts, link_name, link_opts): """Declare a queue on an amqp session. 'session' is the amqp session to use 'callback' is the callback to call when messages are received 'node_name' is the first part of the Qpid address string, before ';' 'node_opts' will be applied to the "x-declare" section of "node" in the address string. 'link_name' goes into the "name" field of the "link" in the address string 'link_opts' will be applied to the "x-declare" section of "link" in the address string. """ self.callback = callback self.receiver = None self.session = None if conf.qpid_topology_version == 1: addr_opts = { "create": "always", "node": { "type": "topic", "x-declare": { "durable": True, "auto-delete": True, }, }, "link": { "durable": True, "x-declare": { "durable": False, "auto-delete": True, "exclusive": False, }, }, } addr_opts["node"]["x-declare"].update(node_opts) elif conf.qpid_topology_version == 2: addr_opts = { "link": { "x-declare": { "auto-delete": True, "exclusive": False, }, }, } else: raise_invalid_topology_version() addr_opts["link"]["x-declare"].update(link_opts) if link_name: addr_opts["link"]["name"] = link_name self.address = "%s ; %s" % (node_name, jsonutils.dumps(addr_opts)) self.connect(session)
def notify(_context, message): """Notifies the recipient of the desired event given the model. Log notifications using openstack's default logging system""" priority = message.get('priority', CONF.default_notification_level) priority = priority.lower() logger = logging.getLogger('heat.openstack.common.notification.%s' % message['event_type']) getattr(logger, priority)(jsonutils.dumps(message))
def serialize_msg(raw_msg, force_envelope=False): if not _SEND_RPC_ENVELOPE and not force_envelope: return raw_msg # NOTE(russellb) See the docstring for _RPC_ENVELOPE_VERSION for more # information about this format. msg = {_VERSION_KEY: _RPC_ENVELOPE_VERSION, _MESSAGE_KEY: jsonutils.dumps(raw_msg)} return msg
def _serialize(data): """Serialization wrapper. We prefer using JSON, but it cannot encode all types. Error if a developer passes us bad data. """ try: return jsonutils.dumps(data, ensure_ascii=True) except TypeError: with excutils.save_and_reraise_exception(): LOG.error(_("JSON serialization failed."))
def serialize_msg(raw_msg, force_envelope=False): if not _SEND_RPC_ENVELOPE and not force_envelope: return raw_msg # NOTE(russellb) See the docstring for _RPC_ENVELOPE_VERSION for more # information about this format. msg = { _VERSION_KEY: _RPC_ENVELOPE_VERSION, _MESSAGE_KEY: jsonutils.dumps(raw_msg) } return msg
def __str__(self): """Dumps a string representation of the rules.""" # Start by building the canonical strings for the rules out_rules = {} for key, value in self.items(): # Use empty string for singleton TrueCheck instances if isinstance(value, TrueCheck): out_rules[key] = '' else: out_rules[key] = str(value) # Dump a pretty-printed JSON representation return jsonutils.dumps(out_rules, indent=4)
def _get_map(self, value, validate=False): if value is None: value = self.has_default() and self.default() or {} if not isinstance(value, collections.Mapping): # This is to handle passing Lists via Json parameters exposed # via a provider resource, in particular lists-of-dicts which # cannot be handled correctly via comma_delimited_list if self.schema.allow_conversion: if isinstance(value, six.string_types): return value elif isinstance(value, collections.Sequence): return jsonutils.dumps(value) raise TypeError(_('"%s" is not a map') % value) return dict(self._get_children(six.iteritems(value), validate=validate))
def _do_get(self, url, request): req_kwargs = dict() req_kwargs['headers'] = dict() req_kwargs['headers']['User-Agent'] = self.USER_AGENT req_kwargs['headers']['Content-Type'] = 'application/json' req_kwargs['data'] = jsonutils.dumps({'request': request}) if self.timeout is not None: req_kwargs['timeout'] = self.timeout try: resp = requests.get(url, **req_kwargs) except requests.ConnectionError as e: err = "Unable to establish connection. %s" % e raise CommunicationError(url, err) return resp
def _populate_deployments_metadata(self, meta): meta['deployments'] = meta.get('deployments', []) if self.transport_poll_server_heat(): meta['os-collect-config'] = { 'heat': { 'user_id': self._get_user_id(), 'password': self.password, 'auth_url': self.context.auth_url, 'project_id': self.stack.stack_user_project_id, 'stack_id': self.stack.identifier().stack_path(), 'resource_name': self.name } } elif self.transport_poll_server_cfn(): meta['os-collect-config'] = { 'cfn': { 'metadata_url': '%s/v1/' % cfg.CONF.heat_metadata_server_url, 'access_key_id': self.access_key, 'secret_access_key': self.secret_key, 'stack_name': self.stack.name, 'path': '%s.Metadata' % self.name } } elif self.transport_poll_temp_url(): container = self.physical_resource_name() object_name = str(uuid.uuid4()) self.client('swift').put_container(container) url = self.client_plugin('swift').get_temp_url(container, object_name, method='GET') put_url = self.client_plugin('swift').get_temp_url( container, object_name) self.data_set('metadata_put_url', put_url) self.data_set('metadata_object_name', object_name) meta['os-collect-config'] = {'request': {'metadata_url': url}} self.client('swift').put_object(container, object_name, jsonutils.dumps(meta)) self.metadata_set(meta)
def serialize_remote_exception(failure_info, log_failure=True): """Prepares exception data to be sent over rpc. Failure_info should be a sys.exc_info() tuple. """ tb = traceback.format_exception(*failure_info) failure = failure_info[1] if log_failure: LOG.error(_("Returning exception %s to caller"), six.text_type(failure)) LOG.error(tb) kwargs = {} if hasattr(failure, 'kwargs'): kwargs = failure.kwargs # NOTE(matiu): With cells, it's possible to re-raise remote, remote # exceptions. Lets turn it back into the original exception type. cls_name = str(failure.__class__.__name__) mod_name = str(failure.__class__.__module__) if (cls_name.endswith(_REMOTE_POSTFIX) and mod_name.endswith(_REMOTE_POSTFIX)): cls_name = cls_name[:-len(_REMOTE_POSTFIX)] mod_name = mod_name[:-len(_REMOTE_POSTFIX)] data = { 'class': cls_name, 'module': mod_name, 'message': six.text_type(failure), 'tb': tb, 'args': failure.args, 'kwargs': kwargs } json_data = jsonutils.dumps(data) return json_data
def _populate_deployments_metadata(self, meta): meta['deployments'] = meta.get('deployments', []) if self.transport_poll_server_heat(): meta['os-collect-config'] = {'heat': { 'user_id': self._get_user_id(), 'password': self.password, 'auth_url': self.context.auth_url, 'project_id': self.stack.stack_user_project_id, 'stack_id': self.stack.identifier().stack_path(), 'resource_name': self.name} } elif self.transport_poll_server_cfn(): meta['os-collect-config'] = {'cfn': { 'metadata_url': '%s/v1/' % cfg.CONF.heat_metadata_server_url, 'access_key_id': self.access_key, 'secret_access_key': self.secret_key, 'stack_name': self.stack.name, 'path': '%s.Metadata' % self.name} } elif self.transport_poll_temp_url(): container = self.physical_resource_name() object_name = str(uuid.uuid4()) self.client('swift').put_container(container) url = self.client_plugin('swift').get_temp_url( container, object_name, method='GET') put_url = self.client_plugin('swift').get_temp_url( container, object_name) self.data_set('metadata_put_url', put_url) self.data_set('metadata_object_name', object_name) meta['os-collect-config'] = {'request': { 'metadata_url': url} } self.client('swift').put_object( container, object_name, jsonutils.dumps(meta)) self.metadata_set(meta)
def __call__(self, req): # Read request signature and access id. # If we find X-Auth-User in the headers 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 'X-Auth-User' in req.headers: return self.application else: raise exception.HeatIncompleteSignatureError() try: access = req.params['AWSAccessKeyId'] except KeyError: logger.info("No AWSAccessKeyId found.") if 'X-Auth-User' in req.headers: 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. 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 = None try: creds_json = json.dumps(creds) except TypeError: creds_json = json.dumps(json.to_primitive(creds)) headers = {'Content-Type': 'application/json'} # Disable 'has no x member' pylint error # for httplib and urlparse # pylint: disable-msg=E1101 logger.info('Authenticating with %s' % self.conf['keystone_ec2_uri']) o = urlparse.urlparse(self.conf['keystone_ec2_uri']) 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 = json.loads(response) try: token_id = result['access']['token']['id'] logger.info("AWS authentication successful.") except (AttributeError, KeyError): logger.info("AWS authentication failure.") # 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! ec2_creds = {'ec2Credentials': {'access': access, 'signature': signature}} req.headers['X-Auth-EC2-Creds'] = json.dumps(ec2_creds) req.headers['X-Auth-Token'] = token_id req.headers['X-Auth-URL'] = self.conf['auth_uri'] req.headers['X-Auth-EC2_URL'] = self.conf['keystone_ec2_uri'] return self.application
def __call__(self, req): # Read request signature and access id. # If we find X-Auth-User in the headers 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..") signature = self._get_signature(req) if not signature: if "X-Auth-User" in req.headers: return self.application else: logger.info("No AWS Signature found.") raise exception.HeatIncompleteSignatureError() access = self._get_access(req) if not access: if "X-Auth-User" in req.headers: return self.application else: logger.info("No AWSAccessKeyId/Authorization Credential") raise exception.HeatMissingAuthenticationTokenError() logger.info("AWS credentials found, checking against keystone.") # Make a copy of args for authentication and signature verification. auth_params = dict(req.params) # 'Signature' param Not part of authentication args auth_params.pop("Signature", None) # Authenticate the request. # AWS v4 authentication requires a hash of the body body_hash = hashlib.sha256(req.body).hexdigest() creds = { "ec2Credentials": { "access": access, "signature": signature, "host": req.host, "verb": req.method, "path": req.path, "params": auth_params, "headers": req.headers, "body_hash": body_hash, } } creds_json = json.dumps(creds) headers = {"Content-Type": "application/json"} # Disable 'has no x member' pylint error # for httplib and urlparse # pylint: disable-msg=E1101 keystone_ec2_uri = self._conf_get("keystone_ec2_uri") logger.info("Authenticating with %s" % keystone_ec2_uri) o = urlparse.urlparse(keystone_ec2_uri) 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 = json.loads(response) try: token_id = result["access"]["token"]["id"] logger.info("AWS authentication successful.") except (AttributeError, KeyError): logger.info("AWS authentication failure.") # 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! ec2_creds = {"ec2Credentials": {"access": access, "signature": signature}} req.headers["X-Auth-EC2-Creds"] = json.dumps(ec2_creds) req.headers["X-Auth-Token"] = token_id req.headers["X-Auth-URL"] = self._conf_get("auth_uri") req.headers["X-Auth-EC2_URL"] = keystone_ec2_uri return self.application
def check_serialize(msg): """Make sure a message intended for rpc can be serialized.""" jsonutils.dumps(msg)
def _authorize(self, req, auth_uri): # Read request signature and access id. # If we find X-Auth-User in the headers 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..")) signature = self._get_signature(req) if not signature: if 'X-Auth-User' in req.headers: return self.application else: logger.info(_("No AWS Signature found.")) raise exception.HeatIncompleteSignatureError() access = self._get_access(req) if not access: if 'X-Auth-User' in req.headers: return self.application else: logger.info(_("No AWSAccessKeyId/Authorization Credential")) raise exception.HeatMissingAuthenticationTokenError() logger.info(_("AWS credentials found, checking against keystone.")) if not auth_uri: logger.error(_("Ec2Token authorization failed, no auth_uri " "specified in config file")) raise exception.HeatInternalFailureError(_('Service ' 'misconfigured')) # Make a copy of args for authentication and signature verification. auth_params = dict(req.params) # 'Signature' param Not part of authentication args auth_params.pop('Signature', None) # Authenticate the request. # AWS v4 authentication requires a hash of the body body_hash = hashlib.sha256(req.body).hexdigest() creds = {'ec2Credentials': {'access': access, 'signature': signature, 'host': req.host, 'verb': req.method, 'path': req.path, 'params': auth_params, 'headers': req.headers, 'body_hash': body_hash }} creds_json = json.dumps(creds) headers = {'Content-Type': 'application/json'} keystone_ec2_uri = self._conf_get_keystone_ec2_uri(auth_uri) logger.info(_('Authenticating with %s') % keystone_ec2_uri) response = requests.post(keystone_ec2_uri, data=creds_json, headers=headers) result = response.json() try: token_id = result['access']['token']['id'] tenant = result['access']['token']['tenant']['name'] tenant_id = result['access']['token']['tenant']['id'] logger.info(_("AWS authentication successful.")) except (AttributeError, KeyError): logger.info(_("AWS authentication failure.")) # 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! ec2_creds = {'ec2Credentials': {'access': access, 'signature': signature}} req.headers['X-Auth-EC2-Creds'] = json.dumps(ec2_creds) req.headers['X-Auth-Token'] = token_id req.headers['X-Tenant-Name'] = tenant req.headers['X-Tenant-Id'] = tenant_id req.headers['X-Auth-URL'] = auth_uri metadata = result['access'].get('metadata', {}) roles = metadata.get('roles', []) req.headers['X-Roles'] = ','.join(roles) return self.application
def _authorize(self, req, auth_uri): # Read request signature and access id. # If we find X-Auth-User in the headers 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..")) signature = self._get_signature(req) if not signature: if "X-Auth-User" in req.headers: return self.application else: logger.info(_("No AWS Signature found.")) raise exception.HeatIncompleteSignatureError() access = self._get_access(req) if not access: if "X-Auth-User" in req.headers: return self.application else: logger.info(_("No AWSAccessKeyId/Authorization Credential")) raise exception.HeatMissingAuthenticationTokenError() logger.info(_("AWS credentials found, checking against keystone.")) if not auth_uri: logger.error(_("Ec2Token authorization failed, no auth_uri " "specified in config file")) raise exception.HeatInternalFailureError(_("Service " "misconfigured")) # Make a copy of args for authentication and signature verification. auth_params = dict(req.params) # 'Signature' param Not part of authentication args auth_params.pop("Signature", None) # Authenticate the request. # AWS v4 authentication requires a hash of the body body_hash = hashlib.sha256(req.body).hexdigest() creds = { "ec2Credentials": { "access": access, "signature": signature, "host": req.host, "verb": req.method, "path": req.path, "params": auth_params, "headers": req.headers, "body_hash": body_hash, } } creds_json = json.dumps(creds) headers = {"Content-Type": "application/json"} keystone_ec2_uri = self._conf_get_keystone_ec2_uri(auth_uri) logger.info(_("Authenticating with %s") % keystone_ec2_uri) response = requests.post(keystone_ec2_uri, data=creds_json, headers=headers) result = response.json() try: token_id = result["access"]["token"]["id"] tenant = result["access"]["token"]["tenant"]["name"] tenant_id = result["access"]["token"]["tenant"]["id"] logger.info(_("AWS authentication successful.")) except (AttributeError, KeyError): logger.info(_("AWS authentication failure.")) # 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! ec2_creds = {"ec2Credentials": {"access": access, "signature": signature}} req.headers["X-Auth-EC2-Creds"] = json.dumps(ec2_creds) req.headers["X-Auth-Token"] = token_id req.headers["X-Tenant-Name"] = tenant req.headers["X-Tenant-Id"] = tenant_id req.headers["X-Auth-URL"] = auth_uri metadata = result["access"].get("metadata", {}) roles = metadata.get("roles", []) req.headers["X-Roles"] = ",".join(roles) return self.application
def __call__(self, req): # Read request signature and access id. # If we find X-Auth-User in the headers 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..") signature = self._get_signature(req) if not signature: if 'X-Auth-User' in req.headers: return self.application else: logger.info("No AWS Signature found.") raise exception.HeatIncompleteSignatureError() access = self._get_access(req) if not access: if 'X-Auth-User' in req.headers: return self.application else: logger.info("No AWSAccessKeyId/Authorization Credential") raise exception.HeatMissingAuthenticationTokenError() logger.info("AWS credentials found, checking against keystone.") # Make a copy of args for authentication and signature verification. auth_params = dict(req.params) # 'Signature' param Not part of authentication args auth_params.pop('Signature', None) # Authenticate the request. # AWS v4 authentication requires a hash of the body body_hash = hashlib.sha256(req.body).hexdigest() creds = {'ec2Credentials': {'access': access, 'signature': signature, 'host': req.host, 'verb': req.method, 'path': req.path, 'params': auth_params, 'headers': req.headers, 'body_hash': body_hash }} creds_json = json.dumps(creds) headers = {'Content-Type': 'application/json'} # Disable 'has no x member' pylint error # for httplib and urlparse # pylint: disable-msg=E1101 keystone_ec2_uri = self._conf_get_keystone_ec2_uri() logger.info('Authenticating with %s' % keystone_ec2_uri) o = urlparse.urlparse(keystone_ec2_uri) 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 = json.loads(response) try: token_id = result['access']['token']['id'] tenant = result['access']['token']['tenant']['name'] tenant_id = result['access']['token']['tenant']['id'] logger.info("AWS authentication successful.") except (AttributeError, KeyError): logger.info("AWS authentication failure.") # 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! ec2_creds = {'ec2Credentials': {'access': access, 'signature': signature}} req.headers['X-Auth-EC2-Creds'] = json.dumps(ec2_creds) req.headers['X-Auth-Token'] = token_id req.headers['X-Tenant-Name'] = tenant req.headers['X-Tenant-Id'] = tenant_id req.headers['X-Auth-URL'] = self._conf_get('auth_uri') metadata = result['access'].get('metadata', {}) roles = metadata.get('roles', []) req.headers['X-Roles'] = ','.join(roles) return self.application