def test_signed_url_wrong_method(self): send_mock = mock.Mock() self.protocol.sendMessage = send_mock data = urls.create_signed_url('secret', ['/v2/queues/myqueue/messages'], project=self.project_id, methods=['GET']) headers = self.headers.copy() headers.update({ 'URL-Signature': data['signature'], 'URL-Expires': data['expires'], 'URL-Methods': ['GET'], 'URL-Paths': ['/v2/queues/myqueue/messages'] }) req = json.dumps({ 'action': 'message_delete', 'body': { 'queue_name': 'myqueue', 'message_id': '123' }, 'headers': headers }) self.protocol.onMessage(req, False) self.assertEqual(1, send_mock.call_count) resp = json.loads(send_mock.call_args[0][0]) self.assertEqual(403, resp['headers']['status'])
def test_create_signed_url_multiple_paths(self): timeutils.set_time_override() self.addCleanup(timeutils.clear_time_override) key = six.b('test') methods = ['POST'] project = 'my-project' paths = [ '/v2/queues/shared/messages', '/v2/queues/shared/subscriptions' ] expires = timeutils.utcnow() + datetime.timedelta(days=1) expires_str = expires.strftime(urls._DATE_FORMAT) hmac_body = six.b( r'%(paths)s\n%(methods)s\n' r'%(project)s\n%(expires)s' % { 'paths': ','.join(paths), 'methods': ','.join(methods), 'project': project, 'expires': expires_str }) expected = hmac.new(key, hmac_body, hashlib.sha256).hexdigest() actual = urls.create_signed_url(key, paths, methods=['POST'], project=project) self.assertEqual(expected, actual['signature'])
def test_create_signed_url_utc(self): """Test that the method converts the TZ to UTC.""" date_str = '2100-05-31T19:00:17+02' date_str_utc = '2100-05-31T17:00:17' key = six.b('test') project = None methods = ['GET'] paths = ['/v2/queues/shared/messages'] parsed = timeutils.parse_isotime(date_str_utc) expires = timeutils.normalize_time(parsed) expires_str = expires.strftime(urls._DATE_FORMAT) hmac_body = six.b( '%(paths)s\\n%(methods)s\\n' '%(project)s\\n%(expires)s' % { 'paths': ','.join(paths), 'methods': ','.join(methods), 'project': project, 'expires': expires_str }) expected = hmac.new(key, hmac_body, hashlib.sha256).hexdigest() actual = urls.create_signed_url(key, paths, expires=date_str) self.assertEqual(expected, actual['signature'])
def on_post(self, req, resp, project_id, queue_name): LOG.debug(u'Pre-Signed URL Creation for queue: %(queue)s, ' u'project: %(project)s', {'queue': queue_name, 'project': project_id}) try: document = wsgi_utils.deserialize(req.stream, req.content_length) except ValueError as ex: LOG.debug(ex) raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex)) diff = set(document.keys()) - _KNOWN_KEYS if diff: msg = six.text_type(_LE('Unknown keys: %s') % diff) raise wsgi_errors.HTTPBadRequestAPI(msg) key = self._conf.signed_url.secret_key paths = document.pop('paths', None) if not paths: paths = [os.path.join(req.path[:-6], 'messages')] else: diff = set(paths) - _VALID_PATHS if diff: msg = six.text_type(_LE('Invalid paths: %s') % diff) raise wsgi_errors.HTTPBadRequestAPI(msg) paths = [os.path.join(req.path[:-6], path) for path in paths] try: data = urls.create_signed_url(key, paths, project=project_id, **document) except ValueError as err: raise wsgi_errors.HTTPBadRequestAPI(str(err)) resp.body = utils.to_json(data)
def send_confirm_notification(self, queue, subscription, conf, project=None, expires=None, api_version=None, is_unsubscribed=False): # NOTE(flwang): If the confirmation feature isn't enabled, just do # nothing. Here we're getting the require_confirmation from conf # object instead of using self.require_confirmation, because the # variable from self object really depends on the kwargs when # initializing the NotifierDriver object. See bug 1655812 for more # information. if not conf.notification.require_confirmation: return key = conf.signed_url.secret_key if not key: LOG.error("Can't send confirm notification due to the value of" " secret_key option is None") return url = "/%s/queues/%s/subscriptions/%s/confirm" % (api_version, queue, subscription['id']) pre_url = urls.create_signed_url(key, [url], project=project, expires=expires, methods=['PUT']) message = None if is_unsubscribed: message_type = MessageType.UnsubscribeConfirmation.name message = ('You have unsubscribed successfully to the queue: %s, ' 'you can resubscribe it by using confirmed=True.' % queue) else: message_type = MessageType.SubscriptionConfirmation.name message = 'You have chosen to subscribe to the queue: %s' % queue messages = {} endpoint_dict = auth.get_public_endpoint() if endpoint_dict: wsgi_endpoint = endpoint_dict.get('zaqar') if wsgi_endpoint: wsgi_subscribe_url = urllib_parse.urljoin( wsgi_endpoint, url) messages['WSGISubscribeURL'] = wsgi_subscribe_url websocket_endpoint = endpoint_dict.get('zaqar-websocket') if websocket_endpoint: websocket_subscribe_url = urllib_parse.urljoin( websocket_endpoint, url) messages['WebSocketSubscribeURL'] = websocket_subscribe_url messages.update({'Message_Type': message_type, 'Message': message, 'URL-Signature': pre_url['signature'], 'URL-Methods': pre_url['methods'][0], 'URL-Paths': pre_url['paths'][0], 'X-Project-ID': pre_url['project'], 'URL-Expires': pre_url['expires'], 'SubscribeBody': {'confirmed': True}, 'UnsubscribeBody': {'confirmed': False}}) s_type = urllib_parse.urlparse(subscription['subscriber']).scheme LOG.info('Begin to send %(type)s confirm/unsubscribe notification.' ' The request body is %(messages)s', {'type': s_type, 'messages': messages}) self._execute(s_type, subscription, [messages], conf)
def send_confirm_notification(self, queue, subscription, conf, project=None, expires=None, api_version=None): key = conf.signed_url.secret_key if not key: LOG.error(_LE("Can't send confirm notification due to the value of" " secret_key option is None")) return url = "/%s/queues/%s/subscriptions/%s/confirm" % (api_version, queue, subscription['id']) pre_url = urls.create_signed_url(key, [url], project=project, expires=expires, methods=['PUT']) message_type = MessageType.SubscriptionConfirmation.name messages = {} endpoint_dict = auth.get_public_endpoint() if endpoint_dict: wsgi_endpoint = endpoint_dict.get('zaqar', None) if wsgi_endpoint: wsgi_subscribe_url = urllib_parse.urljoin( wsgi_endpoint, url) messages['WSGISubscribeURL'] = wsgi_subscribe_url websocket_endpoint = endpoint_dict.get('zaqar-websocket', None) if websocket_endpoint: websocket_subscribe_url = urllib_parse.urljoin( websocket_endpoint, url) messages['WebSocketSubscribeURL'] = websocket_subscribe_url messages.update({'Message_Type': message_type, 'Message': 'You have chosen to subscribe to the ' 'queue: %s' % queue, 'URL-Signature': pre_url['signature'], 'URL-Methods': pre_url['methods'][0], 'URL-Paths': pre_url['paths'][0], 'X-Project-ID': pre_url['project'], 'URL-Expires': pre_url['expires'], 'SubscribeBody': {'confirmed': True}, 'UnsubscribeBody': {'confirmed': False}}) s_type = urllib_parse.urlparse(subscription['subscriber']).scheme LOG.info(_LI('Begin to send %(type)s confirm notification. The request' 'body is %(messages)s'), {'type': s_type, 'messages': messages}) self._execute(s_type, subscription, [messages], conf)
def test_create_signed_url(self): timeutils.set_time_override() self.addCleanup(timeutils.clear_time_override) key = six.b('test') methods = ['POST'] project = 'my-project' paths = ['/v2/queues/shared/messages'] expires = timeutils.utcnow() + datetime.timedelta(days=1) expires_str = expires.strftime(urls._DATE_FORMAT) hmac_body = six.b(r'%(paths)s\n%(methods)s\n' r'%(project)s\n%(expires)s' % {'paths': ','.join(paths), 'methods': ','.join(methods), 'project': project, 'expires': expires_str}) expected = hmac.new(key, hmac_body, hashlib.sha256).hexdigest() actual = urls.create_signed_url(key, paths, methods=['POST'], project=project) self.assertEqual(expected, actual['signature'])
def test_create_signed_url_utc(self): """Test that the method converts the TZ to UTC.""" date_str = '2100-05-31T19:00:17+02' date_str_utc = '2100-05-31T17:00:17' key = six.b('test') project = None methods = ['GET'] paths = ['/v2/queues/shared/messages'] parsed = timeutils.parse_isotime(date_str_utc) expires = timeutils.normalize_time(parsed) expires_str = expires.strftime(urls._DATE_FORMAT) hmac_body = six.b('%(paths)s\\n%(methods)s\\n' '%(project)s\\n%(expires)s' % {'paths': ','.join(paths), 'methods': ','.join(methods), 'project': project, 'expires': expires_str}) expected = hmac.new(key, hmac_body, hashlib.sha256).hexdigest() actual = urls.create_signed_url(key, paths, expires=date_str) self.assertEqual(expected, actual['signature'])
def on_post(self, req, resp, project_id, queue_name): LOG.debug( u'Pre-Signed URL Creation for queue: %(queue)s, ' u'project: %(project)s', { 'queue': queue_name, 'project': project_id }) try: document = wsgi_utils.deserialize(req.stream, req.content_length) except ValueError as ex: LOG.debug(ex) raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex)) diff = set(document.keys()) - _KNOWN_KEYS if diff: msg = six.text_type(_LE('Unknown keys: %s') % diff) raise wsgi_errors.HTTPBadRequestAPI(msg) key = self._conf.signed_url.secret_key paths = document.pop('paths', None) if not paths: paths = [os.path.join(req.path[:-6], 'messages')] else: diff = set(paths) - _VALID_PATHS if diff: msg = six.text_type(_LE('Invalid paths: %s') % diff) raise wsgi_errors.HTTPBadRequestAPI(msg) paths = [os.path.join(req.path[:-6], path) for path in paths] try: data = urls.create_signed_url(key, paths, project=project_id, **document) except ValueError as err: raise wsgi_errors.HTTPBadRequestAPI(str(err)) resp.body = utils.to_json(data)
def test_signed_url_wrong_queue(self): send_mock = mock.Mock() self.protocol.sendMessage = send_mock data = urls.create_signed_url( 'secret', ['/v2/queues/myqueue/messages'], project=self.project_id, methods=['GET']) headers = self.headers.copy() headers.update({ 'URL-Signature': data['signature'], 'URL-Expires': data['expires'], 'URL-Methods': ['GET'], 'URL-Paths': ['/v2/queues/otherqueue/messages'] }) req = json.dumps({'action': 'message_list', 'body': {'queue_name': 'otherqueue'}, 'headers': headers}) self.protocol.onMessage(req, False) self.assertEqual(1, send_mock.call_count) resp = json.loads(send_mock.call_args[0][0]) self.assertEqual(403, resp['headers']['status'])
def send_confirm_notification(self, queue, subscription, conf, project=None, expires=None, api_version=None, is_unsubscribed=False): # NOTE(flwang): If the confirmation feature isn't enabled, just do # nothing. Here we're getting the require_confirmation from conf # object instead of using self.require_confirmation, because the # variable from self object really depends on the kwargs when # initializing the NotifierDriver object. See bug 1655812 for more # information. if not conf.notification.require_confirmation: return key = conf.signed_url.secret_key if not key: LOG.error("Can't send confirm notification due to the value of" " secret_key option is None") return url = "/%s/queues/%s/subscriptions/%s/confirm" % (api_version, queue, subscription['id']) pre_url = urls.create_signed_url(key, [url], project=project, expires=expires, methods=['PUT']) message = None if is_unsubscribed: message_type = MessageType.UnsubscribeConfirmation.name message = ('You have unsubscribed successfully to the queue: %s, ' 'you can resubscribe it by using confirmed=True.' % queue) else: message_type = MessageType.SubscriptionConfirmation.name message = 'You have chosen to subscribe to the queue: %s' % queue messages = {} endpoint_dict = auth.get_public_endpoint() if endpoint_dict: wsgi_endpoint = endpoint_dict.get('zaqar') if wsgi_endpoint: wsgi_subscribe_url = urllib_parse.urljoin(wsgi_endpoint, url) messages['WSGISubscribeURL'] = wsgi_subscribe_url websocket_endpoint = endpoint_dict.get('zaqar-websocket') if websocket_endpoint: websocket_subscribe_url = urllib_parse.urljoin( websocket_endpoint, url) messages['WebSocketSubscribeURL'] = websocket_subscribe_url messages.update({ 'Message_Type': message_type, 'Message': message, 'URL-Signature': pre_url['signature'], 'URL-Methods': pre_url['methods'][0], 'URL-Paths': pre_url['paths'][0], 'X-Project-ID': pre_url['project'], 'URL-Expires': pre_url['expires'], 'SubscribeBody': { 'confirmed': True }, 'UnsubscribeBody': { 'confirmed': False } }) s_type = urllib_parse.urlparse(subscription['subscriber']).scheme LOG.info( 'Begin to send %(type)s confirm/unsubscribe notification.' ' The request body is %(messages)s', { 'type': s_type, 'messages': messages }) self._execute(s_type, subscription, [messages], conf)
def send_confirm_notification(self, queue, subscription, conf, project=None, expires=None, api_version=None): key = conf.signed_url.secret_key if not key: LOG.error( _LE("Can't send confirm notification due to the value of" " secret_key option is None")) return url = "/%s/queues/%s/subscriptions/%s/confirm" % (api_version, queue, subscription['id']) pre_url = urls.create_signed_url(key, [url], project=project, expires=expires, methods=['PUT']) message_type = MessageType.SubscriptionConfirmation.name messages = {} endpoint_dict = auth.get_public_endpoint() if endpoint_dict: wsgi_endpoint = endpoint_dict.get('zaqar', None) if wsgi_endpoint: wsgi_subscribe_url = urllib_parse.urljoin(wsgi_endpoint, url) messages['WSGISubscribeURL'] = wsgi_subscribe_url websocket_endpoint = endpoint_dict.get('zaqar-websocket', None) if websocket_endpoint: websocket_subscribe_url = urllib_parse.urljoin( websocket_endpoint, url) messages['WebSocketSubscribeURL'] = websocket_subscribe_url messages.update({ 'Message_Type': message_type, 'Message': 'You have chosen to subscribe to the ' 'queue: %s' % queue, 'URL-Signature': pre_url['signature'], 'URL-Methods': pre_url['methods'][0], 'URL-Paths': pre_url['paths'][0], 'X-Project-ID': pre_url['project'], 'URL-Expires': pre_url['expires'], 'SubscribeBody': { 'confirmed': True }, 'UnsubscribeBody': { 'confirmed': False } }) s_type = urllib_parse.urlparse(subscription['subscriber']).scheme LOG.info( _LI('Begin to send %(type)s confirm notification. The request' 'body is %(messages)s'), { 'type': s_type, 'messages': messages }) self._execute(s_type, subscription, [messages], conf)