Add a way to create a signed URL from a queue

Add a new signed_url method on v2 Queue object which returns a signature
for that queue, and add a new auth backend which can use that signature.

Change-Id: Iceb3cd0ab99a5a53d54ab79172c6228c4e239d8c
This commit is contained in:
Thomas Herve 2016-01-19 17:48:34 +01:00
parent 8962837f88
commit 80474c762d
9 changed files with 87 additions and 23 deletions

View File

@ -16,10 +16,12 @@
from zaqarclient.auth import base
from zaqarclient.auth import keystone
from zaqarclient.auth import signed_url
_BACKENDS = {
'noauth': base.NoAuth,
'keystone': keystone.KeystoneAuth
'keystone': keystone.KeystoneAuth,
'signed-url': signed_url.SignedURLAuth,
}

View File

@ -0,0 +1,40 @@
# Copyright (c) 2016 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from zaqarclient.auth import base
class SignedURLAuth(base.AuthBackend):
"""Authenticate using signature.
The returned client will only work on one dedicated queue which has been
signed.
:params conf: A dictionary with the signed URL data:
- expires
- methods
- paths
- signature
- os_project_id
:type conf: `dict`
"""
def authenticate(self, api_version, request):
"""Set the necessary headers on the request."""
request.headers['URL-Expires'] = self.conf['expires']
request.headers['URL-Methods'] = ','.join(self.conf['methods'])
request.headers['URL-Paths'] = ','.join(self.conf['paths'])
request.headers['URL-Signature'] = self.conf['signature']
return request

View File

@ -42,6 +42,8 @@ class Client(object):
:type options: `dict`
"""
queues_module = queues
def __init__(self, url=None, version=1, conf=None):
self.conf = conf or {}
@ -96,7 +98,7 @@ class Client(object):
:returns: A queue instance
:rtype: `queues.Queue`
"""
return queues.Queue(self, ref, **kwargs)
return self.queues_module.Queue(self, ref, **kwargs)
def queues(self, **params):
"""Gets a list of queues from the server
@ -111,7 +113,7 @@ class Client(object):
return iterator._Iterator(self,
queue_list,
'queues',
queues.create_object(self))
self.queues_modules.create_object(self))
def follow(self, ref):
"""Follows ref.

View File

@ -23,6 +23,8 @@ from zaqarclient.queues.v1 import message
class Queue(object):
message_module = message
def __init__(self, client, name, auto_create=True, force_create=False):
"""Initialize queue object
@ -153,7 +155,7 @@ class Queue(object):
req, trans = self.client._request_and_transport()
msg = core.message_get(trans, req, self._name,
message_id)
return message.Message(self, **msg)
return self.message_module.Message(self, **msg)
def messages(self, *messages, **params):
"""Gets a list of messages from the server
@ -195,7 +197,7 @@ class Queue(object):
return iterator._Iterator(self.client,
msgs,
'messages',
message.create_object(self))
self.message_module.create_object(self))
def delete_messages(self, *messages):
"""Deletes a set of messages from the server
@ -223,7 +225,7 @@ class Queue(object):
return iterator._Iterator(self.client,
msgs,
'messages',
message.create_object(self))
self.message_module.create_object(self))
def claim(self, id=None, ttl=None, grace=None,
limit=None):

View File

@ -19,6 +19,7 @@ from zaqarclient.common import decorators
from zaqarclient.queues.v1 import client
from zaqarclient.queues.v1 import iterator
from zaqarclient.queues.v2 import core
from zaqarclient.queues.v2 import queues
from zaqarclient.queues.v2 import subscription
@ -38,6 +39,8 @@ class Client(client.Client):
:type options: `dict`
"""
queues_module = queues
def __init__(self, url=None, version=2, conf=None):
self.conf = conf or {}

View File

@ -43,6 +43,7 @@ queue_set_metadata = core.queue_set_metadata
queue_get_stats = core.queue_get_stats
queue_delete = core.queue_delete
queue_list = core.queue_list
message_get = core.message_get
message_list = core.message_list
message_post = core.message_post
message_delete = core.message_delete

View File

@ -13,7 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from zaqarclient import errors
from zaqarclient.queues.v1 import queues
from zaqarclient.queues.v2 import core
from zaqarclient.queues.v2 import message
@ -21,19 +20,13 @@ from zaqarclient.queues.v2 import message
class Queue(queues.Queue):
def message(self, message_id):
"""Gets a message by id
message_module = message
:param message_id: Message's reference
:type message_id: `six.text_type`
:returns: A message
:rtype: `dict`
"""
def signed_url(self, paths=None, ttl_seconds=None, methods=None):
req, trans = self.client._request_and_transport()
if self.client.api_version >= 2:
raise errors.InvalidOperation("Unavailable on versions >= 2")
else:
msg = core.message_get(trans, req, self._name,
message_id)
return message.Message(self, **msg)
return core.signed_url_create(trans, req, self._name, paths=paths,
ttl_seconds=ttl_seconds, methods=methods)
def create_object(parent):
return lambda args: Queue(parent, args["name"], auto_create=False)

View File

@ -75,7 +75,7 @@ class TestBase(testtools.TestCase):
def _setup_auth_params(self):
self.creds = self._credentials().get_auth_args()
# FIXME(flwang): Now we're hardcode the keystone auth versioin, since
# FIXME(flwang): Now we're hardcode the keystone auth version, since
# there is a 'bug' with the osc-config which is returning the auth_url
# without version. This should be fixed as long as the bug is fixed.
parsed_url = urllib_parse.urlparse(self.creds['auth_url'])

View File

@ -17,6 +17,7 @@ import json
import mock
from zaqarclient import errors
from zaqarclient.queues import client
from zaqarclient.queues.v1 import iterator
from zaqarclient.queues.v1 import message
from zaqarclient.tests.queues import base
@ -468,4 +469,24 @@ class QueuesV2QueueUnitTest(QueuesV1_1QueueUnitTest):
class QueuesV2QueueFunctionalTest(QueuesV1_1QueueFunctionalTest):
pass
def test_signed_url(self):
queue = self.client.queue('test_queue')
messages = [{'ttl': 300, 'body': 'Post It!'}]
queue.post(messages)
self.addCleanup(queue.delete)
signature = queue.signed_url()
opts = {
'paths': signature['paths'],
'expires': signature['expires'],
'methods': signature['methods'],
'signature': signature['signature'],
'os_project_id': signature['project'],
}
auth_opts = {'backend': 'signed-url',
'options': opts}
conf = {'auth_opts': auth_opts}
signed_client = client.Client(self.url, self.version, conf)
queue = signed_client.queue('test_queue')
[message] = list(queue.messages())
self.assertEqual('Post It!', message.body)