Adds support for username to match the v2 spec
The v2.0 API spec uses username for the user's name, whereas the v3 API just uses name. The v2.0 implementaion incorrectly used name instead of username, but did allow a username to be specified and stored in the extras. This patch makes the implementation more closely conform to the API without breaking backward compatibility. Anyone using name in the v2.0 API can continue to do so. They can even specify a username that will still get stored in the extras. Users can now use the documented username instead of name and the API will work for them as well. Both name and username will always be returned for the v2 API calls. DocImpact Change-Id: Ia95aa5d442a8311925399fa59e5022d31f68d374 Closes-Bug: #1214686
This commit is contained in:
parent
607b850e8f
commit
6f8bdae1db
|
@ -313,6 +313,30 @@ class V2Controller(wsgi.Application):
|
|||
ref.pop('domain_id', None)
|
||||
return ref
|
||||
|
||||
@staticmethod
|
||||
def normalize_username_in_response(ref):
|
||||
"""Adds username to outgoing user refs to match the v2 spec.
|
||||
|
||||
Internally we use `name` to represent a user's name. The v2 spec
|
||||
requires the use of `username` instead.
|
||||
|
||||
"""
|
||||
if 'username' not in ref and 'name' in ref:
|
||||
ref['username'] = ref['name']
|
||||
return ref
|
||||
|
||||
@staticmethod
|
||||
def normalize_username_in_request(ref):
|
||||
"""Adds name in incoming user refs to match the v2 spec.
|
||||
|
||||
Internally we use `name` to represent a user's name. The v2 spec
|
||||
requires the use of `username` instead.
|
||||
|
||||
"""
|
||||
if 'name' not in ref and 'username' in ref:
|
||||
ref['name'] = ref.pop('username')
|
||||
return ref
|
||||
|
||||
|
||||
class V3Controller(V2Controller):
|
||||
"""Base controller class for Identity API v3.
|
||||
|
|
|
@ -192,6 +192,7 @@ class User(controller.V2Controller):
|
|||
# CRUD extension
|
||||
def create_user(self, context, user):
|
||||
user = self._normalize_OSKSADM_password_on_request(user)
|
||||
user = self.normalize_username_in_request(user)
|
||||
user = self._normalize_dict(user)
|
||||
self.assert_admin(context)
|
||||
|
||||
|
@ -221,6 +222,7 @@ class User(controller.V2Controller):
|
|||
|
||||
def update_user(self, context, user_id, user):
|
||||
# NOTE(termie): this is really more of a patch than a put
|
||||
user = self.normalize_username_in_request(user)
|
||||
self.assert_admin(context)
|
||||
|
||||
if 'enabled' in user and not isinstance(user['enabled'], bool):
|
||||
|
|
|
@ -213,6 +213,7 @@ class Manager(manager.Manager):
|
|||
|
||||
* v2.0 users are not domain aware, and should have domain_id removed
|
||||
* v2.0 users expect the use of tenantId instead of default_project_id
|
||||
* v2.0 users have a username attribute
|
||||
|
||||
This method should only be applied to user_refs being returned from the
|
||||
v2.0 controller(s).
|
||||
|
@ -237,6 +238,7 @@ class Manager(manager.Manager):
|
|||
"""Run through the various filter/normalization methods."""
|
||||
_format_default_project_id(ref)
|
||||
controller.V2Controller.filter_domain_id(ref)
|
||||
controller.V2Controller.normalize_username_in_response(ref)
|
||||
return ref
|
||||
|
||||
if isinstance(ref, dict):
|
||||
|
|
|
@ -783,6 +783,161 @@ class LegacyV2UsernameTests(object):
|
|||
user = self.get_user_from_response(r)
|
||||
self.assertEqual(user.get('username'), 'new_username')
|
||||
|
||||
def test_username_is_always_returned_create(self):
|
||||
"""Username is set as the value of name if no username is provided.
|
||||
|
||||
This matches the v2.0 spec where we really should be using username
|
||||
and not name.
|
||||
"""
|
||||
r = self.create_user()
|
||||
|
||||
self.assertValidUserResponse(r)
|
||||
|
||||
user = self.get_user_from_response(r)
|
||||
self.assertEqual(user.get('name'), user.get('username'))
|
||||
|
||||
def test_username_is_always_returned_get(self):
|
||||
"""Username is set as the value of name if no username is provided.
|
||||
|
||||
This matches the v2.0 spec where we really should be using username
|
||||
and not name.
|
||||
"""
|
||||
token = self.get_scoped_token()
|
||||
|
||||
r = self.create_user()
|
||||
|
||||
id_ = self.get_user_attribute_from_response(r, 'id')
|
||||
r = self.admin_request(path='/v2.0/users/%s' % id_, token=token)
|
||||
|
||||
self.assertValidUserResponse(r)
|
||||
|
||||
user = self.get_user_from_response(r)
|
||||
self.assertEqual(user.get('name'), user.get('username'))
|
||||
|
||||
def test_username_is_always_returned_get_by_name(self):
|
||||
"""Username is set as the value of name if no username is provided.
|
||||
|
||||
This matches the v2.0 spec where we really should be using username
|
||||
and not name.
|
||||
"""
|
||||
token = self.get_scoped_token()
|
||||
|
||||
r = self.create_user()
|
||||
|
||||
name = self.get_user_attribute_from_response(r, 'name')
|
||||
r = self.admin_request(path='/v2.0/users?name=%s' % name, token=token)
|
||||
|
||||
self.assertValidUserResponse(r)
|
||||
|
||||
user = self.get_user_from_response(r)
|
||||
self.assertEqual(user.get('name'), user.get('username'))
|
||||
|
||||
def test_username_is_always_returned_update_no_username_provided(self):
|
||||
"""Username is set as the value of name if no username is provided.
|
||||
|
||||
This matches the v2.0 spec where we really should be using username
|
||||
and not name.
|
||||
"""
|
||||
token = self.get_scoped_token()
|
||||
|
||||
r = self.create_user()
|
||||
|
||||
id_ = self.get_user_attribute_from_response(r, 'id')
|
||||
name = self.get_user_attribute_from_response(r, 'name')
|
||||
enabled = self.get_user_attribute_from_response(r, 'enabled')
|
||||
r = self.admin_request(
|
||||
method='PUT',
|
||||
path='/v2.0/users/%s' % id_,
|
||||
token=token,
|
||||
body={
|
||||
'user': {
|
||||
'name': name,
|
||||
'enabled': enabled,
|
||||
},
|
||||
},
|
||||
expected_status=200)
|
||||
|
||||
self.assertValidUserResponse(r)
|
||||
|
||||
user = self.get_user_from_response(r)
|
||||
self.assertEqual(user.get('name'), user.get('username'))
|
||||
|
||||
def test_updated_username_is_returned(self):
|
||||
"""Username is set as the value of name if no username is provided.
|
||||
|
||||
This matches the v2.0 spec where we really should be using username
|
||||
and not name.
|
||||
"""
|
||||
token = self.get_scoped_token()
|
||||
|
||||
r = self.create_user()
|
||||
|
||||
id_ = self.get_user_attribute_from_response(r, 'id')
|
||||
name = self.get_user_attribute_from_response(r, 'name')
|
||||
enabled = self.get_user_attribute_from_response(r, 'enabled')
|
||||
r = self.admin_request(
|
||||
method='PUT',
|
||||
path='/v2.0/users/%s' % id_,
|
||||
token=token,
|
||||
body={
|
||||
'user': {
|
||||
'name': name,
|
||||
'enabled': enabled,
|
||||
},
|
||||
},
|
||||
expected_status=200)
|
||||
|
||||
self.assertValidUserResponse(r)
|
||||
|
||||
user = self.get_user_from_response(r)
|
||||
self.assertEqual(user.get('name'), user.get('username'))
|
||||
|
||||
def test_username_can_be_used_instead_of_name_create(self):
|
||||
token = self.get_scoped_token()
|
||||
|
||||
r = self.admin_request(
|
||||
method='POST',
|
||||
path='/v2.0/users',
|
||||
token=token,
|
||||
body={
|
||||
'user': {
|
||||
'username': uuid.uuid4().hex,
|
||||
'enabled': True,
|
||||
},
|
||||
},
|
||||
expected_status=200)
|
||||
|
||||
self.assertValidUserResponse(r)
|
||||
|
||||
user = self.get_user_from_response(r)
|
||||
self.assertEqual(user.get('name'), user.get('username'))
|
||||
|
||||
def test_username_can_be_used_instead_of_name_update(self):
|
||||
token = self.get_scoped_token()
|
||||
|
||||
r = self.create_user()
|
||||
|
||||
id_ = self.get_user_attribute_from_response(r, 'id')
|
||||
new_username = uuid.uuid4().hex
|
||||
enabled = self.get_user_attribute_from_response(r, 'enabled')
|
||||
r = self.admin_request(
|
||||
method='PUT',
|
||||
path='/v2.0/users/%s' % id_,
|
||||
token=token,
|
||||
body={
|
||||
'user': {
|
||||
'username': new_username,
|
||||
'enabled': enabled,
|
||||
},
|
||||
},
|
||||
expected_status=200)
|
||||
|
||||
self.assertValidUserResponse(r)
|
||||
|
||||
user = self.get_user_from_response(r)
|
||||
self.assertEqual(user.get('name'), new_username)
|
||||
self.assertEqual(user.get('name'), user.get('username'))
|
||||
|
||||
|
||||
class RestfulTestCase(rest.RestfulTestCase):
|
||||
|
||||
|
|
|
@ -1623,11 +1623,13 @@ class TestV3toV2Methods(tests.TestCase):
|
|||
# Expected result if the user is meant to have a tenantId element
|
||||
self.expected_user = {'id': self.user_id,
|
||||
'name': self.user_id,
|
||||
'username': self.user_id,
|
||||
'tenantId': self.default_project_id}
|
||||
|
||||
# Expected result if the user is not meant ot have a tenantId element
|
||||
self.expected_user_no_tenant_id = {'id': self.user_id,
|
||||
'name': self.user_id}
|
||||
'name': self.user_id,
|
||||
'username': self.user_id}
|
||||
|
||||
def test_v3_to_v2_user_method(self):
|
||||
|
||||
|
|
Loading…
Reference in New Issue