Fix domain federation tokens for inherited roles.

Currently domain-scoped federation-generated tokens incorrectly
include group roles that are inherited to projects within that domain.
This error is also exposed via the /auth/domains and
/OS-FEDERATION/domains API calls. This patch fixes this.

Change-Id: I2b5c1e3d695dd7b27bf3b15361fccb5c13bdd554
Closes-bug: 1385533
Closes-bug: 1385643
This commit is contained in:
Henry Nash 2014-11-05 15:09:54 +00:00
parent 4bb8d28b40
commit 1f54bebe65
4 changed files with 115 additions and 15 deletions

View File

@ -14,6 +14,7 @@
import six
import sqlalchemy
from sqlalchemy.sql.expression import false
from keystone import assignment as keystone_assignment
from keystone import clean
@ -325,6 +326,7 @@ class Assignment(keystone_assignment.Driver):
sql_constraints = sqlalchemy.and_(
RoleAssignment.type == assignment_type,
RoleAssignment.target_id == target_id,
RoleAssignment.inherited == false(),
Role.id == RoleAssignment.role_id,
RoleAssignment.actor_id.in_(group_ids))
@ -364,6 +366,7 @@ class Assignment(keystone_assignment.Driver):
group_sql_conditions = sqlalchemy.and_(
RoleAssignment.type == assignment_type,
RoleAssignment.inherited == false(),
entity.id == RoleAssignment.target_id,
RoleAssignment.actor_id.in_(group_ids))

View File

@ -3041,6 +3041,103 @@ class IdentityTests(object):
self.assertThat(member_assignments,
matchers.Equals(orig_member_assignments))
def test_get_roles_for_groups_on_domain(self):
"""Test retrieving group domain roles.
Test Plan:
- Create a domain, three groups and three roles
- Assign one an inherited and the others a non-inherited group role
to the domain
- Ensure that only the non-inherited roles are returned on the domain
"""
domain1 = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex}
self.assignment_api.create_domain(domain1['id'], domain1)
group_list = []
group_id_list = []
role_list = []
for _ in range(3):
group = {'name': uuid.uuid4().hex, 'domain_id': domain1['id']}
group = self.identity_api.create_group(group)
group_list.append(group)
group_id_list.append(group['id'])
role = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex}
self.assignment_api.create_role(role['id'], role)
role_list.append(role)
# Assign the roles - one is inherited
self.assignment_api.create_grant(group_id=group_list[0]['id'],
domain_id=domain1['id'],
role_id=role_list[0]['id'])
self.assignment_api.create_grant(group_id=group_list[1]['id'],
domain_id=domain1['id'],
role_id=role_list[1]['id'])
self.assignment_api.create_grant(group_id=group_list[2]['id'],
domain_id=domain1['id'],
role_id=role_list[2]['id'],
inherited_to_projects=True)
# Now get the effective roles for the groups on the domain project. We
# shouldn't get back the inherited role.
role_refs = self.assignment_api.get_roles_for_groups(
group_id_list, domain_id=domain1['id'])
self.assertThat(role_refs, matchers.HasLength(2))
self.assertIn(role_list[0], role_refs)
self.assertIn(role_list[1], role_refs)
def test_list_domains_for_groups(self):
"""Test retrieving domains for a list of groups.
Test Plan:
- Create three domains, three groups and one role
- Assign a non-inherited group role to two domains, and an inherited
group role to the third
- Ensure only the domains with non-inherited roles are returned
"""
domain_list = []
group_list = []
group_id_list = []
for _ in range(3):
domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex}
self.assignment_api.create_domain(domain['id'], domain)
domain_list.append(domain)
group = {'name': uuid.uuid4().hex, 'domain_id': domain['id']}
group = self.identity_api.create_group(group)
group_list.append(group)
group_id_list.append(group['id'])
role1 = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex}
self.assignment_api.create_role(role1['id'], role1)
# Assign the roles - one is inherited
self.assignment_api.create_grant(group_id=group_list[0]['id'],
domain_id=domain_list[0]['id'],
role_id=role1['id'])
self.assignment_api.create_grant(group_id=group_list[1]['id'],
domain_id=domain_list[1]['id'],
role_id=role1['id'])
self.assignment_api.create_grant(group_id=group_list[2]['id'],
domain_id=domain_list[2]['id'],
role_id=role1['id'],
inherited_to_projects=True)
# Now list the domains that have roles for any of the 3 groups
# We shouldn't get back domain[2] since that had an inherited role.
domain_refs = (
self.assignment_api.list_domains_for_groups(group_id_list))
self.assertThat(domain_refs, matchers.HasLength(2))
self.assertIn(domain_list[0], domain_refs)
self.assertIn(domain_list[1], domain_refs)
class TokenTests(object):
def _create_token_id(self):

View File

@ -409,6 +409,12 @@ class BaseLDAPIdentity(test_backend.IdentityTests):
def test_get_roles_for_user_and_domain(self):
self.skipTest('N/A: LDAP does not support multiple domains')
def test_get_roles_for_groups_on_domain(self):
self.skipTest('Blocked by bug: 1390125')
def test_list_domains_for_groups(self):
self.skipTest('N/A: LDAP does not support multiple domains')
def test_list_role_assignments_unfiltered(self):
new_domain = self._get_domain_fixture()
new_user = {'name': uuid.uuid4().hex, 'password': uuid.uuid4().hex,

View File

@ -1078,14 +1078,10 @@ class FederatedTokenTests(FederationTests):
self._check_scoped_token_attributes(token_resp)
def test_scope_to_domain_with_only_inherited_roles_fails(self):
# TODO(henry-nash): The following *should* fail (since the only roles
# a customer has on domainD are inherited to projects within it.
# See bug #1385533
r = self.v3_authenticate_token(self.TOKEN_SCOPE_DOMAIN_D_FROM_CUSTOMER)
token_resp = r.result['token']
domain_id = token_resp['domain']['id']
self.assertEqual(domain_id, self.domainD['id'])
self._check_scoped_token_attributes(token_resp)
"""Try to scope to a domain that has no direct roles."""
self.v3_authenticate_token(
self.TOKEN_SCOPE_DOMAIN_D_FROM_CUSTOMER,
expected_status=401)
def test_list_projects(self):
urls = ('/OS-FEDERATION/projects', '/auth/projects')
@ -1116,18 +1112,16 @@ class FederatedTokenTests(FederationTests):
self.tokens['EMPLOYEE_ASSERTION'],
self.tokens['ADMIN_ASSERTION'])
# TODO(henry-nash): The following *should* not include domainD
# for customer/admin since they only have a role that is inherited
# to projects within it. See bug #1385643
# NOTE(henry-nash): domain D does not appear in the expected results
# since it only had inherited roles (which only apply to projects
# within the domain)
domain_refs = (set([self.domainA['id'],
self.domainD['id']]),
domain_refs = (set([self.domainA['id']]),
set([self.domainA['id'],
self.domainB['id']]),
set([self.domainA['id'],
self.domainB['id'],
self.domainC['id'],
self.domainD['id']]))
self.domainC['id']]))
for token, domains_ref in zip(tokens, domain_refs):
for url in urls: