Merge "Remove format constraint of client id"
This commit is contained in:
commit
3dff7fd057
@ -1,16 +1,21 @@
|
||||
#### variables in header #####################################################
|
||||
|
||||
client_id:
|
||||
type: UUID
|
||||
type: string
|
||||
in: header
|
||||
description: |
|
||||
A UUID for each client instance. The UUID must be submitted in its
|
||||
The identification for each client instance. The format of client id is
|
||||
UUID by default, but Zaqar also supports a Non-UUID string by setting
|
||||
configuration "client_id_uuid_safe=off". The UUID must be submitted in its
|
||||
canonical form (for example, 3381af92-2b9e-11e3-b191-71861300734c). The
|
||||
client generates the Client-ID once. Client-ID persists between restarts
|
||||
of the client so the client should reuse that same Client-ID. Note: All
|
||||
message-related operations require the use of ``Client-ID`` in the headers
|
||||
to ensure that messages are not echoed back to the client that posted
|
||||
them, unless the client explicitly requests this.
|
||||
string must be longer than "min_length_client_id=20" and smaller than
|
||||
"max_length_client_id=300" by default. User can control the length of
|
||||
client id by using those two options. The client generates the Client-ID
|
||||
once. Client-ID persists between restarts of the client so the client
|
||||
should reuse that same Client-ID. Note: All message-related operations
|
||||
require the use of ``Client-ID`` in the headers to ensure that messages
|
||||
are not echoed back to the client that posted them, unless the client
|
||||
explicitly requests this.
|
||||
|
||||
#### variables in path #######################################################
|
||||
|
||||
|
@ -0,0 +1,9 @@
|
||||
---
|
||||
features:
|
||||
- Since some clients use different format of client id not only uuid, like
|
||||
user id of ldap, so Zaqar will remove the format contrain of client id.
|
||||
Add one option 'client_id_uuid_safe' to allow user to control the
|
||||
validation of client id. Add two options 'min_length_client_id' and
|
||||
'max_length_client_id' to allow user to control the length of client id
|
||||
if not using uuid. This also requires user to ensure the client id is
|
||||
immutable.
|
@ -293,6 +293,7 @@ class Endpoints(object):
|
||||
try:
|
||||
kwargs = api_utils.get_headers(req)
|
||||
|
||||
self._validate.client_id_uuid_safe(req._headers.get('Client-ID'))
|
||||
client_uuid = api_utils.get_client_uuid(req)
|
||||
|
||||
self._validate.message_listing(**kwargs)
|
||||
@ -468,6 +469,7 @@ class Endpoints(object):
|
||||
return api_utils.error_response(req, ex, headers)
|
||||
|
||||
try:
|
||||
self._validate.client_id_uuid_safe(req._headers.get('Client-ID'))
|
||||
client_uuid = api_utils.get_client_uuid(req)
|
||||
|
||||
self._validate.message_posting(messages)
|
||||
@ -477,7 +479,8 @@ class Endpoints(object):
|
||||
messages=messages,
|
||||
project=project_id,
|
||||
client_uuid=client_uuid)
|
||||
except (api_errors.BadRequest, validation.ValidationFailed) as ex:
|
||||
except (ValueError, api_errors.BadRequest,
|
||||
validation.ValidationFailed) as ex:
|
||||
LOG.debug(ex)
|
||||
headers = {'status': 400}
|
||||
return api_utils.error_response(req, ex, headers)
|
||||
|
@ -136,16 +136,13 @@ def get_client_uuid(req):
|
||||
"""Read a required Client-ID from a request.
|
||||
|
||||
:param req: Request object
|
||||
:raises BadRequest: if the Client-ID header is missing or
|
||||
does not represent a valid UUID
|
||||
:returns: A UUID object
|
||||
:returns: A UUID object or A string of client id
|
||||
"""
|
||||
|
||||
try:
|
||||
return uuid.UUID(req._headers.get('Client-ID'))
|
||||
except ValueError:
|
||||
description = _(u'Malformed hexadecimal UUID.')
|
||||
raise api_errors.BadRequest(description)
|
||||
return req._headers.get('Client-ID')
|
||||
|
||||
|
||||
def get_headers(req):
|
||||
|
@ -67,17 +67,13 @@ def get_client_uuid(req):
|
||||
"""Read a required Client-ID from a request.
|
||||
|
||||
:param req: A falcon.Request object
|
||||
:raises HTTPBadRequest: if the Client-ID header is missing or
|
||||
does not represent a valid UUID
|
||||
:returns: A UUID object
|
||||
:returns: A UUID object or A string of client id
|
||||
"""
|
||||
|
||||
try:
|
||||
return uuid.UUID(req.get_header('Client-ID', required=True))
|
||||
|
||||
except ValueError:
|
||||
description = _(u'Malformed hexadecimal UUID.')
|
||||
raise falcon.HTTPBadRequest('Wrong UUID value', description)
|
||||
return req.get_header('Client-ID', required=True)
|
||||
|
||||
|
||||
def extract_project_id(req, resp, params):
|
||||
@ -112,10 +108,13 @@ def extract_project_id(req, resp, params):
|
||||
_(u'The header X-PROJECT-ID was missing'))
|
||||
|
||||
|
||||
def require_client_id(req, resp, params):
|
||||
def require_client_id(validate, req, resp, params):
|
||||
"""Makes sure the header `Client-ID` is present in the request
|
||||
|
||||
Use as a before hook.
|
||||
:param validate: A validator function that will
|
||||
be used to check the format of client id against configured
|
||||
limits.
|
||||
:param req: request sent
|
||||
:type req: falcon.request.Request
|
||||
:param resp: response object to return
|
||||
@ -126,9 +125,24 @@ def require_client_id(req, resp, params):
|
||||
"""
|
||||
|
||||
if req.path.startswith('/v1.1/') or req.path.startswith('/v2/'):
|
||||
# NOTE(flaper87): `get_client_uuid` already raises 400
|
||||
# it the header is missing.
|
||||
get_client_uuid(req)
|
||||
try:
|
||||
validate(req.get_header('Client-ID', required=True))
|
||||
except ValueError:
|
||||
description = _(u'Malformed hexadecimal UUID.')
|
||||
raise falcon.HTTPBadRequest('Wrong UUID value', description)
|
||||
except validation.ValidationFailed as ex:
|
||||
raise falcon.HTTPBadRequest(six.text_type(ex))
|
||||
else:
|
||||
# NOTE(wanghao): Since we changed the get_client_uuid to support
|
||||
# other format of client id, so need to check the uuid here for
|
||||
# v1 API.
|
||||
try:
|
||||
client_id = req.get_header('Client-ID')
|
||||
if client_id or client_id == '':
|
||||
uuid.UUID(client_id)
|
||||
except ValueError:
|
||||
description = _(u'Malformed hexadecimal UUID.')
|
||||
raise falcon.HTTPBadRequest('Wrong UUID value', description)
|
||||
|
||||
|
||||
def validate_queue_identification(validate, req, resp, params):
|
||||
|
@ -124,6 +124,25 @@ max_pools_per_page = cfg.IntOpt(
|
||||
help='Defines the maximum number of pools per page.')
|
||||
|
||||
|
||||
client_id_uuid_safe = cfg.StrOpt(
|
||||
'client_id_uuid_safe', default='strict', choices=['strict', 'off'],
|
||||
help='Defines the format of client id, the value could be '
|
||||
'"strict" or "off". "strict" means the format of client id'
|
||||
' must be uuid, "off" means the restriction be removed.')
|
||||
|
||||
|
||||
min_length_client_id = cfg.IntOpt(
|
||||
'min_length_client_id', default='10',
|
||||
help='Defines the minimum length of client id if remove the '
|
||||
'uuid restriction. Default is 10.')
|
||||
|
||||
|
||||
max_length_client_id = cfg.IntOpt(
|
||||
'max_length_client_id', default='36',
|
||||
help='Defines the maximum length of client id if remove the '
|
||||
'uuid restriction. Default is 36.')
|
||||
|
||||
|
||||
GROUP_NAME = 'transport'
|
||||
ALL_OPTS = [
|
||||
default_message_ttl,
|
||||
@ -143,7 +162,10 @@ ALL_OPTS = [
|
||||
max_claim_grace,
|
||||
subscriber_types,
|
||||
max_flavors_per_page,
|
||||
max_pools_per_page
|
||||
max_pools_per_page,
|
||||
client_id_uuid_safe,
|
||||
min_length_client_id,
|
||||
max_length_client_id
|
||||
]
|
||||
|
||||
|
||||
|
@ -127,6 +127,54 @@ class TestQueueLifecycleMongoDB(base.V2Base):
|
||||
self.simulate_get(gumshoe_queue_path_stats, headers=headers)
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
|
||||
@ddt.data('1234567890', '11111111111111111111111111111111111')
|
||||
def test_basics_thoroughly_with_different_client_id(self, client_id):
|
||||
self.conf.set_override('client_id_uuid_safe', 'off', 'transport')
|
||||
headers = {
|
||||
'Client-ID': client_id,
|
||||
'X-Project-ID': '480924'
|
||||
}
|
||||
gumshoe_queue_path_stats = self.gumshoe_queue_path + '/stats'
|
||||
|
||||
# Stats are empty - queue not created yet
|
||||
self.simulate_get(gumshoe_queue_path_stats, headers=headers)
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
|
||||
# Create
|
||||
doc = '{"messages": {"ttl": 600}}'
|
||||
self.simulate_put(self.gumshoe_queue_path,
|
||||
headers=headers, body=doc)
|
||||
self.assertEqual(falcon.HTTP_201, self.srmock.status)
|
||||
|
||||
location = self.srmock.headers_dict['Location']
|
||||
self.assertEqual(location, self.gumshoe_queue_path)
|
||||
|
||||
# Fetch metadata
|
||||
result = self.simulate_get(self.gumshoe_queue_path,
|
||||
headers=headers)
|
||||
result_doc = jsonutils.loads(result[0])
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
ref_doc = jsonutils.loads(doc)
|
||||
ref_doc['_default_message_ttl'] = 3600
|
||||
ref_doc['_max_messages_post_size'] = 262144
|
||||
ref_doc['_default_message_delay'] = 0
|
||||
ref_doc['_dead_letter_queue'] = None
|
||||
ref_doc['_dead_letter_queue_messages_ttl'] = None
|
||||
ref_doc['_max_claim_count'] = None
|
||||
self.assertEqual(ref_doc, result_doc)
|
||||
|
||||
# Stats empty queue
|
||||
self.simulate_get(gumshoe_queue_path_stats, headers=headers)
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
|
||||
# Delete
|
||||
self.simulate_delete(self.gumshoe_queue_path, headers=headers)
|
||||
self.assertEqual(falcon.HTTP_204, self.srmock.status)
|
||||
|
||||
# Get non-existent stats
|
||||
self.simulate_get(gumshoe_queue_path_stats, headers=headers)
|
||||
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
||||
|
||||
def test_name_restrictions(self):
|
||||
self.simulate_put(self.queue_path + '/Nice-Boat_2',
|
||||
headers=self.headers)
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
import datetime
|
||||
import re
|
||||
import uuid
|
||||
|
||||
from oslo_utils import timeutils
|
||||
import six
|
||||
@ -649,3 +650,21 @@ class Validator(object):
|
||||
if limit is not None and not (0 < limit <= uplimit):
|
||||
msg = _(u'Limit must be at least 1 and no greater than {0}.')
|
||||
raise ValidationFailed(msg, self._limits_conf.max_pools_per_page)
|
||||
|
||||
def client_id_uuid_safe(self, client_id):
|
||||
"""Restrictions the format of client id
|
||||
|
||||
:param client_id: the client id of request
|
||||
:raises ValidationFailed: if the limit is exceeded
|
||||
"""
|
||||
|
||||
if self._limits_conf.client_id_uuid_safe == 'off':
|
||||
if (len(client_id) < self._limits_conf.min_length_client_id) or \
|
||||
(len(client_id) > self._limits_conf.max_length_client_id):
|
||||
msg = _(u'Length of client id must be at least {0} and no '
|
||||
'greater than {1}.')
|
||||
raise ValidationFailed(msg,
|
||||
self._limits_conf.min_length_client_id,
|
||||
self._limits_conf.max_length_client_id)
|
||||
if self._limits_conf.client_id_uuid_safe == 'strict':
|
||||
uuid.UUID(client_id)
|
||||
|
@ -72,6 +72,10 @@ class Driver(transport.DriverBase):
|
||||
return helpers.validate_queue_identification(
|
||||
self._validate.queue_identification, req, resp, params)
|
||||
|
||||
def _require_client_id(self, req, resp, params):
|
||||
return helpers.require_client_id(
|
||||
self._validate.client_id_uuid_safe, req, resp, params)
|
||||
|
||||
@decorators.lazy_property(write=False)
|
||||
def before_hooks(self):
|
||||
"""Exposed to facilitate unit testing."""
|
||||
@ -79,7 +83,7 @@ class Driver(transport.DriverBase):
|
||||
self._verify_pre_signed_url,
|
||||
helpers.require_content_type_be_non_urlencoded,
|
||||
helpers.require_accepts_json,
|
||||
helpers.require_client_id,
|
||||
self._require_client_id,
|
||||
helpers.extract_project_id,
|
||||
|
||||
# NOTE(jeffrey4l): Depends on the project_id and client_id being
|
||||
|
Loading…
Reference in New Issue
Block a user