Ensure ephemeral user's user_id is url-safe

As a principle, an attribute ``user_id`` should always be url safe and
we should make sure this is the case especially for the federated
authentication workflow as user name and id can be provided directly
from the user (either hardcoded in the mapping rule or passed as a
parameter from the assertion).

Change-Id: I7256a5e4d31d3c2e55fe956fb0170452bb241078
Closes-Bug: #1487115
This commit is contained in:
Marek Denis 2015-08-20 18:12:39 +02:00 committed by David Stanek
parent 59ea878c8c
commit 02f8576ddf
3 changed files with 76 additions and 24 deletions

View File

@ -245,6 +245,8 @@ def setup_username(context, mapped_properties):
user['name'] = user_id
elif not user_id:
user['id'] = parse.quote(user_name)
user_id = user_name
user['id'] = parse.quote(user_id)
return user

View File

@ -789,6 +789,7 @@ MAPPING_USER_IDS = {
{
"user": {
"name": "{0}",
"id": "abc123@example.com",
"domain": {
"id": "federated"
}
@ -831,7 +832,7 @@ MAPPING_USER_IDS = {
"local": [
{
"user": {
"id": "abc123",
"id": "abc123@example.com",
"name": "{0}",
"domain": {
"id": "federated"
@ -966,6 +967,7 @@ TESTER_ASSERTION = {
}
ANOTHER_TESTER_ASSERTION = {
'Email': 'testacct@example.com',
'UserName': 'IamTester'
}

View File

@ -334,7 +334,8 @@ class FederatedSetupMixin(object):
},
{
'user': {
'name': '{0}'
'name': '{0}',
'id': '{1}'
}
}
],
@ -342,6 +343,9 @@ class FederatedSetupMixin(object):
{
'type': 'UserName'
},
{
'type': 'Email',
},
{
'type': 'orgPersonType',
'any_one_of': [
@ -359,7 +363,8 @@ class FederatedSetupMixin(object):
},
{
'user': {
'name': '{0}'
'name': '{0}',
'id': '{1}'
}
}
],
@ -367,6 +372,9 @@ class FederatedSetupMixin(object):
{
'type': self.ASSERTION_PREFIX + 'UserName'
},
{
'type': self.ASSERTION_PREFIX + 'Email',
},
{
'type': self.ASSERTION_PREFIX + 'orgPersonType',
'any_one_of': [
@ -384,7 +392,8 @@ class FederatedSetupMixin(object):
},
{
'user': {
'name': '{0}'
'name': '{0}',
'id': '{1}'
}
}
],
@ -392,6 +401,9 @@ class FederatedSetupMixin(object):
{
'type': 'UserName'
},
{
'type': 'Email'
},
{
'type': 'orgPersonType',
'any_one_of': [
@ -420,7 +432,8 @@ class FederatedSetupMixin(object):
{
'user': {
'name': '{0}'
'name': '{0}',
'id': '{1}'
}
}
],
@ -428,6 +441,9 @@ class FederatedSetupMixin(object):
{
'type': 'UserName'
},
{
'type': 'Email'
},
{
'type': 'orgPersonType',
'any_one_of': [
@ -451,7 +467,8 @@ class FederatedSetupMixin(object):
},
{
'user': {
'name': '{0}'
'name': '{0}',
'id': '{1}'
}
}
],
@ -459,6 +476,9 @@ class FederatedSetupMixin(object):
{
'type': 'UserName',
},
{
'type': 'Email',
},
{
'type': 'FirstName',
'any_one_of': [
@ -482,7 +502,8 @@ class FederatedSetupMixin(object):
},
{
'user': {
'name': '{0}'
'name': '{0}',
'id': '{1}'
}
}
],
@ -490,6 +511,9 @@ class FederatedSetupMixin(object):
{
'type': 'UserName',
},
{
'type': 'Email',
},
{
'type': 'Email',
'any_one_of': [
@ -509,7 +533,8 @@ class FederatedSetupMixin(object):
"local": [
{
'user': {
'name': '{0}'
'name': '{0}',
'id': '{1}'
}
},
{
@ -525,6 +550,9 @@ class FederatedSetupMixin(object):
{
'type': 'UserName',
},
{
'type': 'Email',
},
{
"type": "orgPersonType",
"any_one_of": [
@ -538,7 +566,8 @@ class FederatedSetupMixin(object):
"local": [
{
'user': {
'name': '{0}'
'name': '{0}',
'id': '{1}'
}
},
{
@ -554,6 +583,9 @@ class FederatedSetupMixin(object):
{
"type": "UserName",
},
{
"type": "Email",
},
{
"type": "orgPersonType",
"any_one_of": [
@ -566,7 +598,8 @@ class FederatedSetupMixin(object):
"local": [
{
"user": {
"name": "{0}"
"name": "{0}",
"id": "{1}"
}
},
{
@ -582,6 +615,9 @@ class FederatedSetupMixin(object):
{
"type": "UserName",
},
{
"type": "Email",
},
{
"type": "UserName",
"any_one_of": [
@ -1943,7 +1979,8 @@ class MappingRuleEngineTests(FederationTests):
self.assertValidMappedUserObject(mapped_properties)
mapped.setup_username({}, mapped_properties)
self.assertEqual('tbo', mapped_properties['user']['name'])
self.assertEqual('tbo', mapped_properties['user']['id'])
self.assertEqual('abc123%40example.com',
mapped_properties['user']['id'])
def test_user_identification_id(self):
"""Test varius mapping options and how users are identified.
@ -1981,17 +2018,28 @@ class MappingRuleEngineTests(FederationTests):
- Check if user's id is properly set and and equal to value hardcoded
in the mapping
This test does two iterations with different assertions used as input
for the Mapping Engine. Different assertions will be matched with
different rules in the ruleset, effectively issuing different user_id
(hardcoded values). In the first iteration, the hardcoded user_id is
not url-safe and we expect Keystone to make it url safe. In the latter
iteration, provided user_id is already url-safe and we expect server
not to change it.
"""
mapping = mapping_fixtures.MAPPING_USER_IDS
rp = mapping_utils.RuleProcessor(mapping['rules'])
assertion = mapping_fixtures.CUSTOMER_ASSERTION
mapped_properties = rp.process(assertion)
context = {'environment': {}}
self.assertIsNotNone(mapped_properties)
self.assertValidMappedUserObject(mapped_properties)
mapped.setup_username(context, mapped_properties)
self.assertEqual('bwilliams', mapped_properties['user']['name'])
self.assertEqual('abc123', mapped_properties['user']['id'])
testcases = [(mapping_fixtures.CUSTOMER_ASSERTION, 'bwilliams'),
(mapping_fixtures.EMPLOYEE_ASSERTION, 'tbo')]
for assertion, exp_user_name in testcases:
mapping = mapping_fixtures.MAPPING_USER_IDS
rp = mapping_utils.RuleProcessor(mapping['rules'])
mapped_properties = rp.process(assertion)
context = {'environment': {}}
self.assertIsNotNone(mapped_properties)
self.assertValidMappedUserObject(mapped_properties)
mapped.setup_username(context, mapped_properties)
self.assertEqual(exp_user_name, mapped_properties['user']['name'])
self.assertEqual('abc123%40example.com',
mapped_properties['user']['id'])
class FederatedTokenTests(FederationTests, FederatedSetupMixin):
@ -2925,12 +2973,12 @@ class FernetFederatedTokenTests(FederationTests, FederatedSetupMixin):
def test_federated_unscoped_token(self):
resp = self._issue_unscoped_token()
self.assertEqual(186, len(resp.headers['X-Subject-Token']))
self.assertEqual(204, len(resp.headers['X-Subject-Token']))
def test_federated_unscoped_token_with_multiple_groups(self):
assertion = 'ANOTHER_CUSTOMER_ASSERTION'
resp = self._issue_unscoped_token(assertion=assertion)
self.assertEqual(204, len(resp.headers['X-Subject-Token']))
self.assertEqual(232, len(resp.headers['X-Subject-Token']))
def test_validate_federated_unscoped_token(self):
resp = self._issue_unscoped_token()