Implied Roles API

CRD for implied roles.
Iplied roles are expanded in token issue and validation responses.
Explicitly forbids creating a rule with admin as the implied role to
  avoid escalation of privileges.

Co-Authored-By: Henry Nash <henryn@linux.vnet.ibm.com>
implements: blueprint implied-roles
Change-Id: I812843adb9a1748fb471325797dad80e1baea565
This commit is contained in:
Adam Young 2016-01-06 11:35:46 -05:00 committed by ayoung
parent 399a66f141
commit e1b5751a4c
12 changed files with 617 additions and 2 deletions

View File

@ -73,6 +73,13 @@ identity:create_role POST /v3/roles
identity:update_role PATCH /v3/roles/{role_id}
identity:delete_role DELETE /v3/roles/{role_id}
identity:get_implied_role GET /v3/roles/{prior_role_id}/implies/{implied_role_id}
identity:list_implied_roles GET /v3/roles/{prior_role_id}/implies
identity:create_implied_role PUT /v3/roles/{prior_role_id}/implies/{implied_role_id}
identity:delete_implied_role DELETE /v3/roles/{prior_role_id}/implies/{implied_role_id}
identity:list_role_inference_rules GET /v3/role_inferences
identity:check_implied_role HEAD /v3/roles/{prior_role_id}/implies/{implied_role_id}
identity:check_grant GET `grant_resources`_
identity:list_grants GET `grant_collections`_
identity:create_grant PUT `grant_resources`_

View File

@ -76,6 +76,13 @@
"identity:update_role": "rule:admin_required",
"identity:delete_role": "rule:admin_required",
"identity:get_implied_role": "rule:admin_required ",
"identity:list_implied_roles": "rule:admin_required",
"identity:create_implied_role": "rule:admin_required",
"identity:delete_implied_role": "rule:admin_required",
"identity:list_role_inference_rules": "rule:admin_required",
"identity:check_implied_role": "rule:admin_required",
"identity:check_grant": "rule:admin_required",
"identity:list_grants": "rule:admin_required",
"identity:create_grant": "rule:admin_required",

View File

@ -82,6 +82,13 @@
"identity:update_role": "rule:cloud_admin",
"identity:delete_role": "rule:cloud_admin",
"identity:get_implied_role": "rule:cloud_admin",
"identity:list_implied_roles": "rule:cloud_admin",
"identity:create_implied_role": "rule:cloud_admin",
"identity:delete_implied_role": "rule:cloud_admin",
"identity:list_role_inference_rules": "rule:cloud_admin",
"identity:check_implied_role": "rule:cloud_admin",
"domain_admin_for_grants": "rule:admin_required and (domain_id:%(domain_id)s or domain_id:%(target.project.domain_id)s)",
"project_admin_for_grants": "rule:admin_required and project_id:%(project_id)s",
"identity:check_grant": "rule:cloud_admin or rule:domain_admin_for_grants or rule:project_admin_for_grants",

View File

@ -27,6 +27,7 @@ from keystone.common import controller
from keystone.common import dependency
from keystone.common import utils
from keystone.common import validation
from keystone.common import wsgi
from keystone import exception
from keystone.i18n import _
from keystone import notifications
@ -328,6 +329,125 @@ class RoleV3(controller.V3Controller):
self.role_api.delete_role(role_id, initiator)
@dependency.requires('role_api')
class ImpliedRolesV3(controller.V3Controller):
"""The V3 ImpliedRoles CRD APIs. There is no Update."""
def _prior_role_stanza(self, endpoint, prior_role_id, prior_role_name):
return {
"id": prior_role_id,
"links": {
"self": endpoint + "/v3/roles/" + prior_role_id
},
"name": prior_role_name
}
def _implied_role_stanza(self, endpoint, implied_role):
implied_id = implied_role['id']
implied_response = {
"id": implied_id,
"links": {
"self": endpoint + "/v3/roles/" + implied_id
},
"name": implied_role['name']
}
return implied_response
def _populate_prior_role_response(self, endpoint, prior_id):
prior_role = self.role_api.get_role(prior_id)
response = {
"role_inference": {
"prior_role": self._prior_role_stanza(
endpoint, prior_id, prior_role['name'])
}
}
return response
def _populate_implied_roles_response(self, endpoint,
prior_id, implied_ids):
response = self._populate_prior_role_response(endpoint, prior_id)
response["role_inference"]['implies'] = []
for implied_id in implied_ids:
implied_role = self.role_api.get_role(implied_id)
implied_response = self._implied_role_stanza(
endpoint, implied_role)
response["role_inference"]['implies'].append(implied_response)
return response
def _populate_implied_role_response(self, endpoint, prior_id, implied_id):
response = self._populate_prior_role_response(endpoint, prior_id)
implied_role = self.role_api.get_role(implied_id)
stanza = self._implied_role_stanza(endpoint, implied_role)
response["role_inference"]['implies'] = stanza
return response
@controller.protected()
def get_implied_role(self, context, prior_role_id, implied_role_id):
ref = self.role_api.get_implied_role(prior_role_id, implied_role_id)
prior_id = ref['prior_role_id']
implied_id = ref['implied_role_id']
endpoint = super(controller.V3Controller, ImpliedRolesV3).base_url(
context, 'public')
response = self._populate_implied_role_response(
endpoint, prior_id, implied_id)
return response
@controller.protected()
def check_implied_role(self, context, prior_role_id, implied_role_id):
self.role_api.get_implied_role(prior_role_id, implied_role_id)
@controller.protected()
def create_implied_role(self, context, prior_role_id, implied_role_id):
self.role_api.create_implied_role(prior_role_id, implied_role_id)
return wsgi.render_response(
self.get_implied_role(context, prior_role_id, implied_role_id),
status=(201, 'Created'))
@controller.protected()
def delete_implied_role(self, context, prior_role_id, implied_role_id):
self.role_api.delete_implied_role(prior_role_id, implied_role_id)
@controller.protected()
def list_implied_roles(self, context, prior_role_id):
ref = self.role_api.list_implied_roles(prior_role_id)
implied_ids = [r['implied_role_id'] for r in ref]
endpoint = super(controller.V3Controller, ImpliedRolesV3).base_url(
context, 'public')
results = self._populate_implied_roles_response(
endpoint, prior_role_id, implied_ids)
return results
@controller.protected()
def list_role_inference_rules(self, context):
refs = self.role_api.list_role_inference_rules()
role_dict = {role_ref['id']: role_ref
for role_ref in self.role_api.list_roles()}
rules = dict()
endpoint = super(controller.V3Controller, ImpliedRolesV3).base_url(
context, 'public')
for ref in refs:
implied_role_id = ref['implied_role_id']
prior_role_id = ref['prior_role_id']
implied = rules.get(prior_role_id, [])
implied.append(self._implied_role_stanza(
endpoint, role_dict[implied_role_id]))
rules[prior_role_id] = implied
inferences = []
for prior_id, implied in rules.items():
prior_response = self._prior_role_stanza(
endpoint, prior_id, role_dict[prior_id]['name'])
inferences.append({'prior_role': prior_response,
'implies': implied})
results = {'role_inferences': inferences}
return results
@dependency.requires('assignment_api', 'identity_api', 'resource_api',
'role_api')
class GrantAssignmentV3(controller.V3Controller):
@ -480,6 +600,13 @@ class RoleAssignmentV3(controller.V3Controller):
'role_id': role_id,
'indirect': {'project_id': parent_id}}
or, for a role that was implied by a prior role:
{'user_id': user_id,
'project_id': project_id,
'role_id': role_id,
'indirect': {'role_id': prior role_id}}
It is possible to deduce if a role assignment came from group
membership if it has both 'user_id' in the main body of the dict and
'group_id' in the 'indirect' subdict, as well as it is possible to
@ -577,7 +704,16 @@ class RoleAssignmentV3(controller.V3Controller):
'name': entity['role_name']}
else:
formatted_entity['role'] = {'id': entity['role_id']}
formatted_link += '/roles/%s' % entity['role_id']
prior_role_link = ''
if 'role_id' in entity.get('indirect', {}):
formatted_link += '/roles/%s' % entity['indirect']['role_id']
prior_role_link = (
'/prior_role/%(prior)s/implies/%(implied)s' % {
'prior': entity['role_id'],
'implied': entity['indirect']['role_id']
})
else:
formatted_link += '/roles/%s' % entity['role_id']
if inherited_assignment:
formatted_entity['scope']['OS-INHERIT:inherited_to'] = (
@ -587,6 +723,9 @@ class RoleAssignmentV3(controller.V3Controller):
formatted_entity['links']['assignment'] = self.base_url(context,
formatted_link)
if prior_role_link:
formatted_entity['links']['prior_role'] = (
self.base_url(context, prior_role_link))
return formatted_entity

View File

@ -1452,6 +1452,21 @@ class RoleManager(manager.Manager):
self.get_role.invalidate(self, role_id)
COMPUTED_ASSIGNMENTS_REGION.invalidate()
# TODO(ayoung): Add notification
def create_implied_role(self, prior_role_id, implied_role_id):
implied_role = self.driver.get_role(implied_role_id)
self.driver.get_role(prior_role_id)
if implied_role['name'] == CONF.assignment.root_role:
raise exception.InvalidImpliedRole(role_id=implied_role_id)
response = self.driver.create_implied_role(
prior_role_id, implied_role_id)
COMPUTED_ASSIGNMENTS_REGION.invalidate()
return response
def delete_implied_role(self, prior_role_id, implied_role_id):
self.driver.delete_implied_role(prior_role_id, implied_role_id)
COMPUTED_ASSIGNMENTS_REGION.invalidate()
# The RoleDriverBase class is the set of driver methods from earlier
# drivers that we still support, that have not been removed or modified. This

View File

@ -73,6 +73,41 @@ class Routers(wsgi.RoutersBase):
router.Router(controllers.RoleV3(), 'roles', 'role',
resource_descriptions=self.v3_resources))
implied_roles_controller = controllers.ImpliedRolesV3()
self._add_resource(
mapper, implied_roles_controller,
path='/roles/{prior_role_id}/implies',
rel=json_home.build_v3_resource_relation('implied_roles'),
get_action='list_implied_roles',
status=json_home.Status.EXPERIMENTAL,
path_vars={
'prior_role_id': json_home.Parameters.ROLE_ID,
}
)
self._add_resource(
mapper, implied_roles_controller,
path='/roles/{prior_role_id}/implies/{implied_role_id}',
put_action='create_implied_role',
delete_action='delete_implied_role',
head_action='check_implied_role',
get_action='get_implied_role',
rel=json_home.build_v3_resource_relation('implied_role'),
status=json_home.Status.EXPERIMENTAL,
path_vars={
'prior_role_id': json_home.Parameters.ROLE_ID,
'implied_role_id': json_home.Parameters.ROLE_ID
}
)
self._add_resource(
mapper, implied_roles_controller,
path='/role_inferences',
get_action='list_role_inference_rules',
rel=json_home.build_v3_resource_relation('role_inferences'),
status=json_home.Status.EXPERIMENTAL,
path_vars={}
)
grant_controller = controllers.GrantAssignmentV3()
self._add_resource(
mapper, grant_controller,

View File

@ -378,6 +378,11 @@ FILE_OPTIONS = {
'keystone.assignment namespace. Only an SQL driver is '
'supplied.',
default='sql'),
cfg.StrOpt('root_role', default='admin',
help='A role that is not allowed to be an implied '
'role, as it is the root of role inference directed '
'acyclic graph.'),
],
'resource': [
cfg.StrOpt('driver',

View File

@ -289,6 +289,10 @@ class ImpliedRoleNotFound(NotFound):
message_format = _("%(prior_role_id)s does not imply %(implied_role_id)s")
class InvalidImpliedRole(Forbidden):
message_format = _("%(role_id)s cannot be an implied roles")
class RoleAssignmentNotFound(NotFound):
message_format = _("Could not find role assignment with role: "
"%(role_id)s, user or group: %(actor_id)s, "

View File

@ -1366,7 +1366,8 @@ class AssignmentTestMixin(object):
return link
def build_role_assignment_entity(self, link=None, **attribs):
def build_role_assignment_entity(
self, link=None, prior_role_link=None, **attribs):
"""Build and return a role assignment entity with provided attributes.
Provided attributes are expected to contain: domain_id or project_id,
@ -1395,6 +1396,9 @@ class AssignmentTestMixin(object):
if attribs.get('inherited_to_projects'):
entity['scope']['OS-INHERIT:inherited_to'] = 'projects'
if prior_role_link:
entity['links']['prior_role'] = prior_role_link
return entity
def build_role_assignment_entity_include_names(self,

View File

@ -2386,3 +2386,194 @@ class AssignmentInheritanceDisabledTestCase(test_v3.RestfulTestCase):
self.head(member_url, expected_status=http_client.NOT_FOUND)
self.get(collection_url, expected_status=http_client.NOT_FOUND)
self.delete(member_url, expected_status=http_client.NOT_FOUND)
class ImpliedRolesTests(test_v3.RestfulTestCase, test_v3.AssignmentTestMixin,
unit.TestCase):
def _create_role(self):
"""Call ``POST /roles``."""
ref = unit.new_role_ref()
r = self.post('/roles', body={'role': ref})
return self.assertValidRoleResponse(r, ref)
def test_list_implied_roles_none(self):
self.prior = self._create_role()
url = '/roles/%s/implies' % (self.prior['id'])
response = self.get(url).json["role_inference"]
self.assertEqual(self.prior['id'], response['prior_role']['id'])
self.assertEqual(0, len(response['implies']))
def _create_implied_role(self, prior, implied):
self.put('/roles/%s/implies/%s' % (prior['id'], implied['id']),
expected_status=http_client.CREATED)
def _delete_implied_role(self, prior, implied):
self.delete('/roles/%s/implies/%s' % (prior['id'], implied['id']))
def _setup_prior_two_implied(self):
self.prior = self._create_role()
self.implied1 = self._create_role()
self._create_implied_role(self.prior, self.implied1)
self.implied2 = self._create_role()
self._create_implied_role(self.prior, self.implied2)
def _assert_expected_implied_role_response(
self, expected_prior_id, expected_implied_ids):
r = self.get('/roles/%s/implies' % expected_prior_id)
response = r.json["role_inference"]
self.assertEqual(expected_prior_id, response['prior_role']['id'])
actual_implied_ids = [implied['id'] for implied in response['implies']]
for expected_id in expected_implied_ids:
self.assertIn(expected_id, actual_implied_ids)
self.assertEqual(len(expected_implied_ids), len(response['implies']))
self.assertIsNotNone(response['prior_role']['links']['self'])
for implied in response['implies']:
self.assertIsNotNone(implied['links']['self'])
def _assert_two_roles_implied(self):
self._assert_expected_implied_role_response(
self.prior['id'], [self.implied1['id'], self.implied2['id']])
def _assert_one_role_implied(self):
self._assert_expected_implied_role_response(
self.prior['id'], [self.implied1['id']])
self.get('/roles/%s/implies/%s' %
(self.prior['id'], self.implied2['id']),
expected_status=http_client.NOT_FOUND)
def _assert_two_rules_defined(self):
r = self.get('/role_inferences/')
rules = r.result['role_inferences']
self.assertEqual(self.prior['id'], rules[0]['prior_role']['id'])
self.assertEqual(2, len(rules[0]['implies']))
implied_ids = [implied['id'] for implied in rules[0]['implies']]
implied_names = [implied['name'] for implied in rules[0]['implies']]
self.assertIn(self.implied1['id'], implied_ids)
self.assertIn(self.implied2['id'], implied_ids)
self.assertIn(self.implied1['name'], implied_names)
self.assertIn(self.implied2['name'], implied_names)
def _assert_one_rule_defined(self):
r = self.get('/role_inferences/')
rules = r.result['role_inferences']
self.assertEqual(self.prior['id'], rules[0]['prior_role']['id'])
self.assertEqual(self.implied1['id'], rules[0]['implies'][0]['id'])
self.assertEqual(self.implied1['name'], rules[0]['implies'][0]['name'])
self.assertEqual(1, len(rules[0]['implies']))
def test_list_all_rules(self):
self._setup_prior_two_implied()
self._assert_two_rules_defined()
self._delete_implied_role(self.prior, self.implied2)
self._assert_one_rule_defined()
def test_CRD_implied_roles(self):
self._setup_prior_two_implied()
self._assert_two_roles_implied()
self._delete_implied_role(self.prior, self.implied2)
self._assert_one_role_implied()
def _create_three_roles(self):
self.role_list = []
for _ in range(3):
role = unit.new_role_ref()
self.role_api.create_role(role['id'], role)
self.role_list.append(role)
def _create_test_domain_user_project(self):
domain = unit.new_domain_ref()
self.resource_api.create_domain(domain['id'], domain)
user = unit.create_user(self.identity_api, domain_id=domain['id'])
project = unit.new_project_ref(domain_id=domain['id'])
self.resource_api.create_project(project['id'], project)
return domain, user, project
def _assign_top_role_to_user_on_project(self, user, project):
self.assignment_api.add_role_to_user_and_project(
user['id'], project['id'], self.role_list[0]['id'])
def _build_effective_role_assignments_url(self, user):
return '/role_assignments?effective&user.id=%(user_id)s' % {
'user_id': user['id']}
def _assert_all_roles_in_assignment(self, response, user):
# Now use the list role assignments api to check that all three roles
# appear in the collection
self.assertValidRoleAssignmentListResponse(
response,
expected_length=len(self.role_list),
resource_url=self._build_effective_role_assignments_url(user))
def _assert_initial_assignment_in_effective(self, response, user, project):
# The initial assignment should be there (the link url will be
# generated and checked automatically since it matches the assignment)
entity = self.build_role_assignment_entity(
project_id=project['id'],
user_id=user['id'], role_id=self.role_list[0]['id'])
self.assertRoleAssignmentInListResponse(response, entity)
def _assert_effective_role_for_implied_has_prior_in_links(
self, response, user, project, prior_index, implied_index):
# An effective role for an implied role will have the prior role
# assignment in the links
prior_link = '/prior_roles/%(prior)s/implies/%(implied)s' % {
'prior': self.role_list[prior_index]['id'],
'implied': self.role_list[implied_index]['id']}
link = self.build_role_assignment_link(
project_id=project['id'], user_id=user['id'],
role_id=self.role_list[prior_index]['id'])
entity = self.build_role_assignment_entity(
link=link, project_id=project['id'],
user_id=user['id'], role_id=self.role_list[implied_index]['id'],
prior_link=prior_link)
self.assertRoleAssignmentInListResponse(response, entity)
def test_list_role_assignments_with_implied_roles(self):
"""Call ``GET /role_assignments`` with implied role grant.
Test Plan:
- Create a domain with a user and a project
- Create 3 roles
- Role 0 implies role 1 and role 1 implies role 2
- Assign the top role to the project
- Issue the URL to check effective roles on project - this
should return all 3 roles.
- Check the links of the 3 roles indicate the prior role where
appropriate
"""
(domain, user, project) = self._create_test_domain_user_project()
self._create_three_roles()
self._create_implied_role(self.role_list[0], self.role_list[1])
self._create_implied_role(self.role_list[1], self.role_list[2])
self._assign_top_role_to_user_on_project(user, project)
response = self.get(self._build_effective_role_assignments_url(user))
r = response
self._assert_all_roles_in_assignment(r, user)
self._assert_initial_assignment_in_effective(response, user, project)
self._assert_effective_role_for_implied_has_prior_in_links(
response, user, project, 0, 1)
self._assert_effective_role_for_implied_has_prior_in_links(
response, user, project, 1, 2)
def test_root_role_as_implied_role_forbidden(self):
self.config_fixture.config(group='assignment', root_role='root')
root_role = unit.new_role_ref()
root_role['name'] = 'root'
self.role_api.create_role(root_role['id'], root_role)
prior = self._create_role()
url = '/roles/%s/implies/%s' % (prior['id'], root_role['id'])
self.put(url, expected_status=http_client.FORBIDDEN)

View File

@ -481,6 +481,191 @@ class TokenAPITests(object):
r = self.get('/auth/tokens', headers={'X-Subject-Token': v3_token})
self.assertValidProjectScopedTokenResponse(r, is_admin_project=False)
def _create_role(self):
"""Call ``POST /roles``."""
ref = unit.new_role_ref()
r = self.post('/roles', body={'role': ref})
return self.assertValidRoleResponse(r, ref)
def _create_implied_role(self, prior_id):
implied = self._create_role()
url = '/roles/%s/implies/%s' % (prior_id, implied['id'])
self.put(url, expected_status=http_client.CREATED)
return implied
def _delete_implied_role(self, prior_role_id, implied_role_id):
url = '/roles/%s/implies/%s' % (prior_role_id, implied_role_id)
self.delete(url)
def _get_scoped_token_roles(self, is_domain=False):
if is_domain:
v3_token = self.get_domain_scoped_token()
else:
v3_token = self.get_scoped_token()
r = self.get('/auth/tokens', headers={'X-Subject-Token': v3_token})
v3_token_data = r.result
token_roles = v3_token_data['token']['roles']
return token_roles
def _create_implied_role_shows_in_v3_token(self, is_domain):
token_roles = self._get_scoped_token_roles(is_domain)
self.assertEqual(1, len(token_roles))
prior = token_roles[0]['id']
implied1 = self._create_implied_role(prior)
token_roles = self._get_scoped_token_roles(is_domain)
self.assertEqual(2, len(token_roles))
implied2 = self._create_implied_role(prior)
token_roles = self._get_scoped_token_roles(is_domain)
self.assertEqual(3, len(token_roles))
token_role_ids = [role['id'] for role in token_roles]
self.assertIn(prior, token_role_ids)
self.assertIn(implied1['id'], token_role_ids)
self.assertIn(implied2['id'], token_role_ids)
def test_create_implied_role_shows_in_v3_project_token(self):
# regardless of the default chosen, this should always
# test with the option set.
self.config_fixture.config(group='token', infer_roles=True)
self._create_implied_role_shows_in_v3_token(False)
def test_create_implied_role_shows_in_v3_domain_token(self):
self.config_fixture.config(group='token', infer_roles=True)
self.assignment_api.create_grant(self.role['id'],
user_id=self.user['id'],
domain_id=self.domain['id'])
self._create_implied_role_shows_in_v3_token(True)
def test_group_assigned_implied_role_shows_in_v3_token(self):
self.config_fixture.config(group='token', infer_roles=True)
is_domain = False
token_roles = self._get_scoped_token_roles(is_domain)
self.assertEqual(1, len(token_roles))
new_role = self._create_role()
prior = new_role['id']
new_group_ref = unit.new_group_ref(domain_id=self.domain['id'])
new_group = self.identity_api.create_group(new_group_ref)
self.assignment_api.create_grant(prior,
group_id=new_group['id'],
project_id=self.project['id'])
token_roles = self._get_scoped_token_roles(is_domain)
self.assertEqual(1, len(token_roles))
self.identity_api.add_user_to_group(self.user['id'],
new_group['id'])
token_roles = self._get_scoped_token_roles(is_domain)
self.assertEqual(2, len(token_roles))
implied1 = self._create_implied_role(prior)
token_roles = self._get_scoped_token_roles(is_domain)
self.assertEqual(3, len(token_roles))
implied2 = self._create_implied_role(prior)
token_roles = self._get_scoped_token_roles(is_domain)
self.assertEqual(4, len(token_roles))
token_role_ids = [role['id'] for role in token_roles]
self.assertIn(prior, token_role_ids)
self.assertIn(implied1['id'], token_role_ids)
self.assertIn(implied2['id'], token_role_ids)
def test_multiple_implied_roles_show_in_v3_token(self):
self.config_fixture.config(group='token', infer_roles=True)
token_roles = self._get_scoped_token_roles()
self.assertEqual(1, len(token_roles))
prior = token_roles[0]['id']
implied1 = self._create_implied_role(prior)
implied2 = self._create_implied_role(prior)
implied3 = self._create_implied_role(prior)
token_roles = self._get_scoped_token_roles()
self.assertEqual(4, len(token_roles))
token_role_ids = [role['id'] for role in token_roles]
self.assertIn(prior, token_role_ids)
self.assertIn(implied1['id'], token_role_ids)
self.assertIn(implied2['id'], token_role_ids)
self.assertIn(implied3['id'], token_role_ids)
def test_chained_implied_role_shows_in_v3_token(self):
self.config_fixture.config(group='token', infer_roles=True)
token_roles = self._get_scoped_token_roles()
self.assertEqual(1, len(token_roles))
prior = token_roles[0]['id']
implied1 = self._create_implied_role(prior)
implied2 = self._create_implied_role(implied1['id'])
implied3 = self._create_implied_role(implied2['id'])
token_roles = self._get_scoped_token_roles()
self.assertEqual(4, len(token_roles))
token_role_ids = [role['id'] for role in token_roles]
self.assertIn(prior, token_role_ids)
self.assertIn(implied1['id'], token_role_ids)
self.assertIn(implied2['id'], token_role_ids)
self.assertIn(implied3['id'], token_role_ids)
def test_implied_role_disabled_by_config(self):
self.config_fixture.config(group='token', infer_roles=False)
token_roles = self._get_scoped_token_roles()
self.assertEqual(1, len(token_roles))
prior = token_roles[0]['id']
implied1 = self._create_implied_role(prior)
implied2 = self._create_implied_role(implied1['id'])
self._create_implied_role(implied2['id'])
token_roles = self._get_scoped_token_roles()
self.assertEqual(1, len(token_roles))
token_role_ids = [role['id'] for role in token_roles]
self.assertIn(prior, token_role_ids)
def test_delete_implied_role_do_not_show_in_v3_token(self):
self.config_fixture.config(group='token', infer_roles=True)
token_roles = self._get_scoped_token_roles()
prior = token_roles[0]['id']
implied = self._create_implied_role(prior)
token_roles = self._get_scoped_token_roles()
self.assertEqual(2, len(token_roles))
self._delete_implied_role(prior, implied['id'])
token_roles = self._get_scoped_token_roles()
self.assertEqual(1, len(token_roles))
def test_unrelated_implied_roles_do_not_change_v3_token(self):
self.config_fixture.config(group='token', infer_roles=True)
token_roles = self._get_scoped_token_roles()
prior = token_roles[0]['id']
implied = self._create_implied_role(prior)
token_roles = self._get_scoped_token_roles()
self.assertEqual(2, len(token_roles))
unrelated = self._create_role()
url = '/roles/%s/implies/%s' % (unrelated['id'], implied['id'])
self.put(url, expected_status=http_client.CREATED)
token_roles = self._get_scoped_token_roles()
self.assertEqual(2, len(token_roles))
self._delete_implied_role(unrelated['id'], implied['id'])
token_roles = self._get_scoped_token_roles()
self.assertEqual(2, len(token_roles))
class TokenDataTests(object):
"""Test the data in specific token types."""

View File

@ -327,6 +327,22 @@ V3_JSON_HOME_RESOURCES = {
'href-template': '/roles/{role_id}',
'href-vars': {
'role_id': json_home.Parameters.ROLE_ID, }},
json_home.build_v3_resource_relation('implied_roles'): {
'href-template': '/roles/{prior_role_id}/implies',
'href-vars': {
'prior_role_id': json_home.Parameters.ROLE_ID},
'hints': {'status': 'experimental'}},
json_home.build_v3_resource_relation('implied_role'): {
'href-template':
'/roles/{prior_role_id}/implies/{implied_role_id}',
'href-vars': {
'prior_role_id': json_home.Parameters.ROLE_ID,
'implied_role_id': json_home.Parameters.ROLE_ID,
},
'hints': {'status': 'experimental'}},
json_home.build_v3_resource_relation('role_inferences'): {
'href': '/role_inferences',
'hints': {'status': 'experimental'}},
json_home.build_v3_resource_relation('role_assignments'): {
'href': '/role_assignments'},
json_home.build_v3_resource_relation('roles'): {'href': '/roles'},