Merge "Implied Roles API"
This commit is contained in:
commit
d93843b3be
@ -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`_
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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,6 +704,15 @@ class RoleAssignmentV3(controller.V3Controller):
|
||||
'name': entity['role_name']}
|
||||
else:
|
||||
formatted_entity['role'] = {'id': 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:
|
||||
@ -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
|
||||
|
||||
|
@ -1449,6 +1449,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
|
||||
|
@ -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,
|
||||
|
@ -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',
|
||||
|
@ -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, "
|
||||
|
@ -1453,7 +1453,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,
|
||||
@ -1482,6 +1483,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,
|
||||
|
@ -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)
|
||||
|
@ -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."""
|
||||
|
@ -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'},
|
||||
|
Loading…
Reference in New Issue
Block a user