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:
parent
8962837f88
commit
80474c762d
@ -16,10 +16,12 @@
|
|||||||
|
|
||||||
from zaqarclient.auth import base
|
from zaqarclient.auth import base
|
||||||
from zaqarclient.auth import keystone
|
from zaqarclient.auth import keystone
|
||||||
|
from zaqarclient.auth import signed_url
|
||||||
|
|
||||||
_BACKENDS = {
|
_BACKENDS = {
|
||||||
'noauth': base.NoAuth,
|
'noauth': base.NoAuth,
|
||||||
'keystone': keystone.KeystoneAuth
|
'keystone': keystone.KeystoneAuth,
|
||||||
|
'signed-url': signed_url.SignedURLAuth,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
40
zaqarclient/auth/signed_url.py
Normal file
40
zaqarclient/auth/signed_url.py
Normal 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
|
@ -42,6 +42,8 @@ class Client(object):
|
|||||||
:type options: `dict`
|
:type options: `dict`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
queues_module = queues
|
||||||
|
|
||||||
def __init__(self, url=None, version=1, conf=None):
|
def __init__(self, url=None, version=1, conf=None):
|
||||||
self.conf = conf or {}
|
self.conf = conf or {}
|
||||||
|
|
||||||
@ -96,7 +98,7 @@ class Client(object):
|
|||||||
:returns: A queue instance
|
:returns: A queue instance
|
||||||
:rtype: `queues.Queue`
|
:rtype: `queues.Queue`
|
||||||
"""
|
"""
|
||||||
return queues.Queue(self, ref, **kwargs)
|
return self.queues_module.Queue(self, ref, **kwargs)
|
||||||
|
|
||||||
def queues(self, **params):
|
def queues(self, **params):
|
||||||
"""Gets a list of queues from the server
|
"""Gets a list of queues from the server
|
||||||
@ -111,7 +113,7 @@ class Client(object):
|
|||||||
return iterator._Iterator(self,
|
return iterator._Iterator(self,
|
||||||
queue_list,
|
queue_list,
|
||||||
'queues',
|
'queues',
|
||||||
queues.create_object(self))
|
self.queues_modules.create_object(self))
|
||||||
|
|
||||||
def follow(self, ref):
|
def follow(self, ref):
|
||||||
"""Follows ref.
|
"""Follows ref.
|
||||||
|
@ -23,6 +23,8 @@ from zaqarclient.queues.v1 import message
|
|||||||
|
|
||||||
class Queue(object):
|
class Queue(object):
|
||||||
|
|
||||||
|
message_module = message
|
||||||
|
|
||||||
def __init__(self, client, name, auto_create=True, force_create=False):
|
def __init__(self, client, name, auto_create=True, force_create=False):
|
||||||
"""Initialize queue object
|
"""Initialize queue object
|
||||||
|
|
||||||
@ -153,7 +155,7 @@ class Queue(object):
|
|||||||
req, trans = self.client._request_and_transport()
|
req, trans = self.client._request_and_transport()
|
||||||
msg = core.message_get(trans, req, self._name,
|
msg = core.message_get(trans, req, self._name,
|
||||||
message_id)
|
message_id)
|
||||||
return message.Message(self, **msg)
|
return self.message_module.Message(self, **msg)
|
||||||
|
|
||||||
def messages(self, *messages, **params):
|
def messages(self, *messages, **params):
|
||||||
"""Gets a list of messages from the server
|
"""Gets a list of messages from the server
|
||||||
@ -195,7 +197,7 @@ class Queue(object):
|
|||||||
return iterator._Iterator(self.client,
|
return iterator._Iterator(self.client,
|
||||||
msgs,
|
msgs,
|
||||||
'messages',
|
'messages',
|
||||||
message.create_object(self))
|
self.message_module.create_object(self))
|
||||||
|
|
||||||
def delete_messages(self, *messages):
|
def delete_messages(self, *messages):
|
||||||
"""Deletes a set of messages from the server
|
"""Deletes a set of messages from the server
|
||||||
@ -223,7 +225,7 @@ class Queue(object):
|
|||||||
return iterator._Iterator(self.client,
|
return iterator._Iterator(self.client,
|
||||||
msgs,
|
msgs,
|
||||||
'messages',
|
'messages',
|
||||||
message.create_object(self))
|
self.message_module.create_object(self))
|
||||||
|
|
||||||
def claim(self, id=None, ttl=None, grace=None,
|
def claim(self, id=None, ttl=None, grace=None,
|
||||||
limit=None):
|
limit=None):
|
||||||
|
@ -19,6 +19,7 @@ from zaqarclient.common import decorators
|
|||||||
from zaqarclient.queues.v1 import client
|
from zaqarclient.queues.v1 import client
|
||||||
from zaqarclient.queues.v1 import iterator
|
from zaqarclient.queues.v1 import iterator
|
||||||
from zaqarclient.queues.v2 import core
|
from zaqarclient.queues.v2 import core
|
||||||
|
from zaqarclient.queues.v2 import queues
|
||||||
from zaqarclient.queues.v2 import subscription
|
from zaqarclient.queues.v2 import subscription
|
||||||
|
|
||||||
|
|
||||||
@ -38,6 +39,8 @@ class Client(client.Client):
|
|||||||
:type options: `dict`
|
:type options: `dict`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
queues_module = queues
|
||||||
|
|
||||||
def __init__(self, url=None, version=2, conf=None):
|
def __init__(self, url=None, version=2, conf=None):
|
||||||
self.conf = conf or {}
|
self.conf = conf or {}
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ queue_set_metadata = core.queue_set_metadata
|
|||||||
queue_get_stats = core.queue_get_stats
|
queue_get_stats = core.queue_get_stats
|
||||||
queue_delete = core.queue_delete
|
queue_delete = core.queue_delete
|
||||||
queue_list = core.queue_list
|
queue_list = core.queue_list
|
||||||
|
message_get = core.message_get
|
||||||
message_list = core.message_list
|
message_list = core.message_list
|
||||||
message_post = core.message_post
|
message_post = core.message_post
|
||||||
message_delete = core.message_delete
|
message_delete = core.message_delete
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from zaqarclient import errors
|
|
||||||
from zaqarclient.queues.v1 import queues
|
from zaqarclient.queues.v1 import queues
|
||||||
from zaqarclient.queues.v2 import core
|
from zaqarclient.queues.v2 import core
|
||||||
from zaqarclient.queues.v2 import message
|
from zaqarclient.queues.v2 import message
|
||||||
@ -21,19 +20,13 @@ from zaqarclient.queues.v2 import message
|
|||||||
|
|
||||||
class Queue(queues.Queue):
|
class Queue(queues.Queue):
|
||||||
|
|
||||||
def message(self, message_id):
|
message_module = message
|
||||||
"""Gets a message by id
|
|
||||||
|
|
||||||
:param message_id: Message's reference
|
def signed_url(self, paths=None, ttl_seconds=None, methods=None):
|
||||||
:type message_id: `six.text_type`
|
|
||||||
|
|
||||||
:returns: A message
|
|
||||||
:rtype: `dict`
|
|
||||||
"""
|
|
||||||
req, trans = self.client._request_and_transport()
|
req, trans = self.client._request_and_transport()
|
||||||
if self.client.api_version >= 2:
|
return core.signed_url_create(trans, req, self._name, paths=paths,
|
||||||
raise errors.InvalidOperation("Unavailable on versions >= 2")
|
ttl_seconds=ttl_seconds, methods=methods)
|
||||||
else:
|
|
||||||
msg = core.message_get(trans, req, self._name,
|
|
||||||
message_id)
|
def create_object(parent):
|
||||||
return message.Message(self, **msg)
|
return lambda args: Queue(parent, args["name"], auto_create=False)
|
||||||
|
@ -75,7 +75,7 @@ class TestBase(testtools.TestCase):
|
|||||||
def _setup_auth_params(self):
|
def _setup_auth_params(self):
|
||||||
self.creds = self._credentials().get_auth_args()
|
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
|
# 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.
|
# without version. This should be fixed as long as the bug is fixed.
|
||||||
parsed_url = urllib_parse.urlparse(self.creds['auth_url'])
|
parsed_url = urllib_parse.urlparse(self.creds['auth_url'])
|
||||||
|
@ -17,6 +17,7 @@ import json
|
|||||||
import mock
|
import mock
|
||||||
|
|
||||||
from zaqarclient import errors
|
from zaqarclient import errors
|
||||||
|
from zaqarclient.queues import client
|
||||||
from zaqarclient.queues.v1 import iterator
|
from zaqarclient.queues.v1 import iterator
|
||||||
from zaqarclient.queues.v1 import message
|
from zaqarclient.queues.v1 import message
|
||||||
from zaqarclient.tests.queues import base
|
from zaqarclient.tests.queues import base
|
||||||
@ -468,4 +469,24 @@ class QueuesV2QueueUnitTest(QueuesV1_1QueueUnitTest):
|
|||||||
|
|
||||||
|
|
||||||
class QueuesV2QueueFunctionalTest(QueuesV1_1QueueFunctionalTest):
|
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)
|
||||||
|
Loading…
Reference in New Issue
Block a user