Skip to content

Commit

Permalink
Treats OS-KSADM:password as password in v2 APIs
Browse files Browse the repository at this point in the history
The OS-KSADM Extension documentation uses OS-KSADM:password
instead of password in some of its examples.  This makes
those examples work.

Change-Id: Ibdfab0b9f085548a86eb391e7dc3b279dfc7b420
Fixes-Bug: #1226466
  • Loading branch information
dstanek committed Oct 9, 2013
1 parent a513ea7 commit efd46cc
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 5 deletions.
10 changes: 5 additions & 5 deletions keystone/common/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ def _deserialize_links(self, links):
return dict((x.attrib['rel'], x.attrib['href']) for x in links)

@staticmethod
def _tag_name(tag, namespace):
"""Returns a tag name.
def _qualified_name(tag, namespace):
"""Returns a qualified tag name.
The tag name may contain the namespace prefix or not, which can
be determined by specifying the parameter namespace.
Expand Down Expand Up @@ -115,15 +115,15 @@ def walk_element(self, element, namespace=False):
elif v in ['false']:
v = False

values[k] = v
values[self._qualified_name(k, namespace)] = v

text = None
if element.text is not None:
text = element.text.strip()

# current spec does not have attributes on an element with text
values = values or text or {}
decoded_tag = XmlDeserializer._tag_name(element.tag, namespace)
decoded_tag = XmlDeserializer._qualified_name(element.tag, namespace)
list_item_tag = None
if (decoded_tag[-1] == 's' and len(values) == 0 and
decoded_tag != 'access'):
Expand Down Expand Up @@ -158,7 +158,7 @@ def walk_element(self, element, namespace=False):
if not values:
values = ""

d = {XmlDeserializer._tag_name(element.tag, namespace): values}
d = {XmlDeserializer._qualified_name(element.tag, namespace): values}

if links:
d['links'] = links
Expand Down
14 changes: 14 additions & 0 deletions keystone/identity/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ def get_user_by_name(self, context, user_name):

# CRUD extension
def create_user(self, context, user):
user = self._normalize_OSKSADM_password_on_request(user)
user = self._normalize_dict(user)
self.assert_admin(context)

Expand Down Expand Up @@ -295,8 +296,21 @@ def set_user_enabled(self, context, user_id, user):
return self.update_user(context, user_id, user)

def set_user_password(self, context, user_id, user):
user = self._normalize_OSKSADM_password_on_request(user)
return self.update_user(context, user_id, user)

@staticmethod
def _normalize_OSKSADM_password_on_request(ref):
"""Sets the password from the OS-KSADM Admin Extension.
The OS-KSADM Admin Extension documentation says that
`OS-KSADM:password` can be used in place of `password`.
"""
if 'OS-KSADM:password' in ref:
ref['password'] = ref.pop('OS-KSADM:password')
return ref


class Role(controller.V2Controller):
# COMPAT(essex-3)
Expand Down
121 changes: 121 additions & 0 deletions keystone/tests/test_content_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,80 @@ def test_create_update_user_json_invalid_enabled_type(self):
expected_status=400)
self.assertValidErrorResponse(r)

def test_authenticating_a_user_with_an_OSKSADM_password(self):
token = self.get_scoped_token()

username = uuid.uuid4().hex
password = uuid.uuid4().hex

# create the user
r = self.admin_request(
method='POST',
path='/v2.0/users',
body={
'user': {
'name': username,
'OS-KSADM:password': password,
'enabled': True,
},
},
token=token)

# successfully authenticate
self.public_request(
method='POST',
path='/v2.0/tokens',
body={
'auth': {
'passwordCredentials': {
'username': username,
'password': password,
},
},
},
expected_status=200)

# ensure password doesn't leak
user_id = r.result['user']['id']
r = self.admin_request(
method='GET',
path='/v2.0/users/%s' % user_id,
token=token,
expected_status=200)
self.assertNotIn('OS-KSADM:password', r.result['user'])

def test_updating_a_user_with_an_OSKSADM_password(self):
token = self.get_scoped_token()

user_id = self.user_foo['id']
password = uuid.uuid4().hex

# update the user
self.admin_request(
method='PUT',
path='/v2.0/users/%s/OS-KSADM/password' % user_id,
body={
'user': {
'password': password,
},
},
token=token,
expected_status=200)

# successfully authenticate
self.public_request(
method='POST',
path='/v2.0/tokens',
body={
'auth': {
'passwordCredentials': {
'username': self.user_foo['name'],
'password': password,
},
},
},
expected_status=200)


class XmlTestCase(RestfulTestCase, CoreApiTests):
xmlns = 'http://docs.openstack.org/identity/api/v2.0'
Expand Down Expand Up @@ -1395,3 +1469,50 @@ def test_update_project_invalid_enabled_type_string(self):
token=token,
expected_status=400)
self.assertValidErrorResponse(r)

def test_authenticating_a_user_with_an_OSKSADM_password(self):
token = self.get_scoped_token()

xmlns = "http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0"

username = uuid.uuid4().hex
password = uuid.uuid4().hex

# create the user
self.admin_request(
method='POST',
path='/v2.0/users',
headers={
'Content-Type': 'application/xml'
},
body="""
<?xml version="1.0" encoding="UTF-8"?>
<user xmlns="http://docs.openstack.org/identity/api/v2.0"
xmlns:OS-KSADM="%(xmlns)s"
name="%(username)s"
OS-KSADM:password="%(password)s"
enabled="true"/>
""" % dict(username=username, password=password, xmlns=xmlns),
token=token,
expected_status=200,
convert=False)

# successfully authenticate
self.public_request(
method='POST',
path='/v2.0/tokens',
headers={
'Content-Type': 'application/xml'
},
body="""
<?xml version="1.0" encoding="UTF-8"?>
<auth xmlns="http://docs.openstack.org/identity/api/v2.0"
xmlns:OS-KSADM="%(xmlns)s">
<passwordCredentials
username="%(username)s"
password="%(password)s"/>
</auth>
""" % dict(username=username, password=password, xmlns=xmlns),
token=token,
expected_status=200,
convert=False)
20 changes: 20 additions & 0 deletions keystone/tests/test_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

import copy

from testtools import matchers

from keystone.common import serializer
from keystone import tests

Expand Down Expand Up @@ -295,3 +297,21 @@ def test_v2_links_special_case(self):
</object>
"""
self.assertEqualXML(serializer.to_xml(d), xml)

def test_xml_with_namespaced_attribute_to_dict(self):
expected = {
"user": {
"username": "test_user",
"OS-KSADM:password": "mypass",
},
}

xmlns = 'http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0'
xml = """
<?xml version="1.0" encoding="UTF-8"?>
<user xmlns="http://docs.openstack.org/identity/api/v2.0"
xmlns:OS-KSADM="%(xmlns)s"
username="test_user"
OS-KSADM:password="mypass"/>
""" % dict(xmlns=xmlns)
self.assertThat(serializer.from_xml(xml), matchers.Equals(expected))

0 comments on commit efd46cc

Please sign in to comment.