aboutsummaryrefslogtreecommitdiffstats
path: root/keystone-moon/keystone/tests/unit/test_v3_protection.py
diff options
context:
space:
mode:
Diffstat (limited to 'keystone-moon/keystone/tests/unit/test_v3_protection.py')
-rw-r--r--keystone-moon/keystone/tests/unit/test_v3_protection.py739
1 files changed, 610 insertions, 129 deletions
diff --git a/keystone-moon/keystone/tests/unit/test_v3_protection.py b/keystone-moon/keystone/tests/unit/test_v3_protection.py
index 9922ae5e..f77a1528 100644
--- a/keystone-moon/keystone/tests/unit/test_v3_protection.py
+++ b/keystone-moon/keystone/tests/unit/test_v3_protection.py
@@ -20,19 +20,22 @@ from oslo_serialization import jsonutils
from six.moves import http_client
from keystone import exception
-from keystone.policy.backends import rules
from keystone.tests import unit
+from keystone.tests.unit import ksfixtures
from keystone.tests.unit.ksfixtures import temporaryfile
from keystone.tests.unit import test_v3
+from keystone.tests.unit import utils
CONF = cfg.CONF
-DEFAULT_DOMAIN_ID = CONF.identity.default_domain_id
class IdentityTestProtectedCase(test_v3.RestfulTestCase):
"""Test policy enforcement on the v3 Identity API."""
+ def _policy_fixture(self):
+ return ksfixtures.Policy(self.tmpfilename, self.config_fixture)
+
def setUp(self):
"""Setup for Identity Protection Test Cases.
@@ -49,14 +52,9 @@ class IdentityTestProtectedCase(test_v3.RestfulTestCase):
the default domain.
"""
- # Ensure that test_v3.RestfulTestCase doesn't load its own
- # sample data, which would make checking the results of our
- # tests harder
- super(IdentityTestProtectedCase, self).setUp()
self.tempfile = self.useFixture(temporaryfile.SecureTempFile())
self.tmpfilename = self.tempfile.file_name
- self.config_fixture.config(group='oslo_policy',
- policy_file=self.tmpfilename)
+ super(IdentityTestProtectedCase, self).setUp()
# A default auth request we can use - un-scoped user token
self.auth = self.build_authentication_request(
@@ -66,45 +64,33 @@ class IdentityTestProtectedCase(test_v3.RestfulTestCase):
def load_sample_data(self):
self._populate_default_domain()
# Start by creating a couple of domains
- self.domainA = self.new_domain_ref()
+ self.domainA = unit.new_domain_ref()
self.resource_api.create_domain(self.domainA['id'], self.domainA)
- self.domainB = self.new_domain_ref()
+ self.domainB = unit.new_domain_ref()
self.resource_api.create_domain(self.domainB['id'], self.domainB)
- self.domainC = self.new_domain_ref()
- self.domainC['enabled'] = False
+ self.domainC = unit.new_domain_ref(enabled=False)
self.resource_api.create_domain(self.domainC['id'], self.domainC)
# Now create some users, one in domainA and two of them in domainB
- self.user1 = self.new_user_ref(domain_id=self.domainA['id'])
- password = uuid.uuid4().hex
- self.user1['password'] = password
- self.user1 = self.identity_api.create_user(self.user1)
- self.user1['password'] = password
-
- self.user2 = self.new_user_ref(domain_id=self.domainB['id'])
- password = uuid.uuid4().hex
- self.user2['password'] = password
- self.user2 = self.identity_api.create_user(self.user2)
- self.user2['password'] = password
-
- self.user3 = self.new_user_ref(domain_id=self.domainB['id'])
- password = uuid.uuid4().hex
- self.user3['password'] = password
- self.user3 = self.identity_api.create_user(self.user3)
- self.user3['password'] = password
-
- self.group1 = self.new_group_ref(domain_id=self.domainA['id'])
+ self.user1 = unit.create_user(self.identity_api,
+ domain_id=self.domainA['id'])
+ self.user2 = unit.create_user(self.identity_api,
+ domain_id=self.domainB['id'])
+ self.user3 = unit.create_user(self.identity_api,
+ domain_id=self.domainB['id'])
+
+ self.group1 = unit.new_group_ref(domain_id=self.domainA['id'])
self.group1 = self.identity_api.create_group(self.group1)
- self.group2 = self.new_group_ref(domain_id=self.domainA['id'])
+ self.group2 = unit.new_group_ref(domain_id=self.domainA['id'])
self.group2 = self.identity_api.create_group(self.group2)
- self.group3 = self.new_group_ref(domain_id=self.domainB['id'])
+ self.group3 = unit.new_group_ref(domain_id=self.domainB['id'])
self.group3 = self.identity_api.create_group(self.group3)
- self.role = self.new_role_ref()
+ self.role = unit.new_role_ref()
self.role_api.create_role(self.role['id'], self.role)
- self.role1 = self.new_role_ref()
+ self.role1 = unit.new_role_ref()
self.role_api.create_role(self.role1['id'], self.role1)
self.assignment_api.create_grant(self.role['id'],
user_id=self.user1['id'],
@@ -348,34 +334,23 @@ class IdentityTestPolicySample(test_v3.RestfulTestCase):
def load_sample_data(self):
self._populate_default_domain()
- self.just_a_user = self.new_user_ref(
+ self.just_a_user = unit.create_user(
+ self.identity_api,
domain_id=CONF.identity.default_domain_id)
- password = uuid.uuid4().hex
- self.just_a_user['password'] = password
- self.just_a_user = self.identity_api.create_user(self.just_a_user)
- self.just_a_user['password'] = password
-
- self.another_user = self.new_user_ref(
+ self.another_user = unit.create_user(
+ self.identity_api,
domain_id=CONF.identity.default_domain_id)
- password = uuid.uuid4().hex
- self.another_user['password'] = password
- self.another_user = self.identity_api.create_user(self.another_user)
- self.another_user['password'] = password
-
- self.admin_user = self.new_user_ref(
+ self.admin_user = unit.create_user(
+ self.identity_api,
domain_id=CONF.identity.default_domain_id)
- password = uuid.uuid4().hex
- self.admin_user['password'] = password
- self.admin_user = self.identity_api.create_user(self.admin_user)
- self.admin_user['password'] = password
- self.role = self.new_role_ref()
+ self.role = unit.new_role_ref()
self.role_api.create_role(self.role['id'], self.role)
- self.admin_role = {'id': uuid.uuid4().hex, 'name': 'admin'}
+ self.admin_role = unit.new_role_ref(name='admin')
self.role_api.create_role(self.admin_role['id'], self.admin_role)
# Create and assign roles to the project
- self.project = self.new_project_ref(
+ self.project = unit.new_project_ref(
domain_id=CONF.identity.default_domain_id)
self.resource_api.create_project(self.project['id'], self.project)
self.assignment_api.create_grant(self.role['id'],
@@ -461,7 +436,8 @@ class IdentityTestPolicySample(test_v3.RestfulTestCase):
token = self.get_requested_token(auth)
self.head('/auth/tokens', token=token,
- headers={'X-Subject-Token': token}, expected_status=200)
+ headers={'X-Subject-Token': token},
+ expected_status=http_client.OK)
def test_user_check_user_token(self):
# A user can check one of their own tokens.
@@ -474,7 +450,8 @@ class IdentityTestPolicySample(test_v3.RestfulTestCase):
token2 = self.get_requested_token(auth)
self.head('/auth/tokens', token=token1,
- headers={'X-Subject-Token': token2}, expected_status=200)
+ headers={'X-Subject-Token': token2},
+ expected_status=http_client.OK)
def test_user_check_other_user_token_rejected(self):
# A user cannot check another user's token.
@@ -510,7 +487,8 @@ class IdentityTestPolicySample(test_v3.RestfulTestCase):
user_token = self.get_requested_token(user_auth)
self.head('/auth/tokens', token=admin_token,
- headers={'X-Subject-Token': user_token}, expected_status=200)
+ headers={'X-Subject-Token': user_token},
+ expected_status=http_client.OK)
def test_user_revoke_same_token(self):
# Given a non-admin user token, the token can be used to revoke
@@ -579,6 +557,10 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
test_v3.AssignmentTestMixin):
"""Test policy enforcement of the sample v3 cloud policy file."""
+ def _policy_fixture(self):
+ return ksfixtures.Policy(unit.dirs.etc('policy.v3cloudsample.json'),
+ self.config_fixture)
+
def setUp(self):
"""Setup for v3 Cloud Policy Sample Test Cases.
@@ -592,8 +574,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
- domain_admin_user has role 'admin' on domainA,
- project_admin_user has role 'admin' on the project,
- just_a_user has a non-admin role on both domainA and the project.
- - admin_domain has user cloud_admin_user, with an 'admin' role
- on admin_domain.
+ - admin_domain has admin_project, and user cloud_admin_user, with an
+ 'admin' role on admin_project.
We test various api protection rules from the cloud sample policy
file to make sure the sample is valid and that we correctly enforce it.
@@ -604,62 +586,61 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
# tests harder
super(IdentityTestv3CloudPolicySample, self).setUp()
- # Finally, switch to the v3 sample policy file
- self.addCleanup(rules.reset)
- rules.reset()
self.config_fixture.config(
- group='oslo_policy',
- policy_file=unit.dirs.etc('policy.v3cloudsample.json'))
+ group='resource',
+ admin_project_name=self.admin_project['name'])
+ self.config_fixture.config(
+ group='resource',
+ admin_project_domain_name=self.admin_domain['name'])
def load_sample_data(self):
# Start by creating a couple of domains
self._populate_default_domain()
- self.domainA = self.new_domain_ref()
+ self.domainA = unit.new_domain_ref()
self.resource_api.create_domain(self.domainA['id'], self.domainA)
- self.domainB = self.new_domain_ref()
+ self.domainB = unit.new_domain_ref()
self.resource_api.create_domain(self.domainB['id'], self.domainB)
- self.admin_domain = {'id': 'admin_domain_id', 'name': 'Admin_domain'}
+ self.admin_domain = unit.new_domain_ref()
self.resource_api.create_domain(self.admin_domain['id'],
self.admin_domain)
+ self.admin_project = unit.new_project_ref(
+ domain_id=self.admin_domain['id'])
+ self.resource_api.create_project(self.admin_project['id'],
+ self.admin_project)
+
# And our users
- self.cloud_admin_user = self.new_user_ref(
+ self.cloud_admin_user = unit.create_user(
+ self.identity_api,
domain_id=self.admin_domain['id'])
- password = uuid.uuid4().hex
- self.cloud_admin_user['password'] = password
- self.cloud_admin_user = (
- self.identity_api.create_user(self.cloud_admin_user))
- self.cloud_admin_user['password'] = password
- self.just_a_user = self.new_user_ref(domain_id=self.domainA['id'])
- password = uuid.uuid4().hex
- self.just_a_user['password'] = password
- self.just_a_user = self.identity_api.create_user(self.just_a_user)
- self.just_a_user['password'] = password
- self.domain_admin_user = self.new_user_ref(
+ self.just_a_user = unit.create_user(
+ self.identity_api,
domain_id=self.domainA['id'])
- password = uuid.uuid4().hex
- self.domain_admin_user['password'] = password
- self.domain_admin_user = (
- self.identity_api.create_user(self.domain_admin_user))
- self.domain_admin_user['password'] = password
- self.project_admin_user = self.new_user_ref(
+ self.domain_admin_user = unit.create_user(
+ self.identity_api,
+ domain_id=self.domainA['id'])
+ self.domainB_admin_user = unit.create_user(
+ self.identity_api,
+ domain_id=self.domainB['id'])
+ self.project_admin_user = unit.create_user(
+ self.identity_api,
domain_id=self.domainA['id'])
- password = uuid.uuid4().hex
- self.project_admin_user['password'] = password
- self.project_admin_user = (
- self.identity_api.create_user(self.project_admin_user))
- self.project_admin_user['password'] = password
-
- # The admin role and another plain role
- self.admin_role = {'id': uuid.uuid4().hex, 'name': 'admin'}
+ self.project_adminB_user = unit.create_user(
+ self.identity_api,
+ domain_id=self.domainB['id'])
+
+ # The admin role, a domain specific role and another plain role
+ self.admin_role = unit.new_role_ref(name='admin')
self.role_api.create_role(self.admin_role['id'], self.admin_role)
- self.role = self.new_role_ref()
+ self.roleA = unit.new_role_ref(domain_id=self.domainA['id'])
+ self.role_api.create_role(self.roleA['id'], self.roleA)
+ self.role = unit.new_role_ref()
self.role_api.create_role(self.role['id'], self.role)
- # The cloud admin just gets the admin role
+ # The cloud admin just gets the admin role on the special admin project
self.assignment_api.create_grant(self.admin_role['id'],
user_id=self.cloud_admin_user['id'],
- domain_id=self.admin_domain['id'])
+ project_id=self.admin_project['id'])
# Assign roles to the domain
self.assignment_api.create_grant(self.admin_role['id'],
@@ -668,13 +649,21 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
self.assignment_api.create_grant(self.role['id'],
user_id=self.just_a_user['id'],
domain_id=self.domainA['id'])
+ self.assignment_api.create_grant(self.admin_role['id'],
+ user_id=self.domainB_admin_user['id'],
+ domain_id=self.domainB['id'])
# Create and assign roles to the project
- self.project = self.new_project_ref(domain_id=self.domainA['id'])
+ self.project = unit.new_project_ref(domain_id=self.domainA['id'])
self.resource_api.create_project(self.project['id'], self.project)
+ self.projectB = unit.new_project_ref(domain_id=self.domainB['id'])
+ self.resource_api.create_project(self.projectB['id'], self.projectB)
self.assignment_api.create_grant(self.admin_role['id'],
user_id=self.project_admin_user['id'],
project_id=self.project['id'])
+ self.assignment_api.create_grant(
+ self.admin_role['id'], user_id=self.project_adminB_user['id'],
+ project_id=self.projectB['id'])
self.assignment_api.create_grant(self.role['id'],
user_id=self.just_a_user['id'],
project_id=self.project['id'])
@@ -683,7 +672,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
# Return the expected return codes for APIs with and without data
# with any specified status overriding the normal values
if expected_status is None:
- return (200, 201, 204)
+ return (http_client.OK, http_client.CREATED,
+ http_client.NO_CONTENT)
else:
return (expected_status, expected_status, expected_status)
@@ -702,7 +692,7 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
self.delete(entity_url, auth=self.auth,
expected_status=status_no_data)
- user_ref = self.new_user_ref(domain_id=domain_id)
+ user_ref = unit.new_user_ref(domain_id=domain_id)
self.post('/users', auth=self.auth, body={'user': user_ref},
expected_status=status_created)
@@ -721,7 +711,7 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
self.delete(entity_url, auth=self.auth,
expected_status=status_no_data)
- proj_ref = self.new_project_ref(domain_id=domain_id)
+ proj_ref = unit.new_project_ref(domain_id=domain_id)
self.post('/projects', auth=self.auth, body={'project': proj_ref},
expected_status=status_created)
@@ -740,13 +730,14 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
self.delete(entity_url, auth=self.auth,
expected_status=status_no_data)
- domain_ref = self.new_domain_ref()
+ domain_ref = unit.new_domain_ref()
self.post('/domains', auth=self.auth, body={'domain': domain_ref},
expected_status=status_created)
- def _test_grants(self, target, entity_id, expected=None):
+ def _test_grants(self, target, entity_id, role_domain_id=None,
+ list_status_OK=False, expected=None):
status_OK, status_created, status_no_data = self._stati(expected)
- a_role = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex}
+ a_role = unit.new_role_ref(domain_id=role_domain_id)
self.role_api.create_role(a_role['id'], a_role)
collection_url = (
@@ -762,11 +753,67 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
expected_status=status_no_data)
self.head(member_url, auth=self.auth,
expected_status=status_no_data)
- self.get(collection_url, auth=self.auth,
- expected_status=status_OK)
+ if list_status_OK:
+ self.get(collection_url, auth=self.auth)
+ else:
+ self.get(collection_url, auth=self.auth,
+ expected_status=status_OK)
self.delete(member_url, auth=self.auth,
expected_status=status_no_data)
+ def _role_management_cases(self, read_status_OK=False, expected=None):
+ # Set the different status values for different types of call depending
+ # on whether we expect the calls to fail or not.
+ status_OK, status_created, status_no_data = self._stati(expected)
+ entity_url = '/roles/%s' % self.role['id']
+ list_url = '/roles'
+
+ if read_status_OK:
+ self.get(entity_url, auth=self.auth)
+ self.get(list_url, auth=self.auth)
+ else:
+ self.get(entity_url, auth=self.auth,
+ expected_status=status_OK)
+ self.get(list_url, auth=self.auth,
+ expected_status=status_OK)
+
+ role = {'name': 'Updated'}
+ self.patch(entity_url, auth=self.auth, body={'role': role},
+ expected_status=status_OK)
+ self.delete(entity_url, auth=self.auth,
+ expected_status=status_no_data)
+
+ role_ref = unit.new_role_ref()
+ self.post('/roles', auth=self.auth, body={'role': role_ref},
+ expected_status=status_created)
+
+ def _domain_role_management_cases(self, domain_id, read_status_OK=False,
+ expected=None):
+ # Set the different status values for different types of call depending
+ # on whether we expect the calls to fail or not.
+ status_OK, status_created, status_no_data = self._stati(expected)
+ entity_url = '/roles/%s' % self.roleA['id']
+ list_url = '/roles?domain_id=%s' % domain_id
+
+ if read_status_OK:
+ self.get(entity_url, auth=self.auth)
+ self.get(list_url, auth=self.auth)
+ else:
+ self.get(entity_url, auth=self.auth,
+ expected_status=status_OK)
+ self.get(list_url, auth=self.auth,
+ expected_status=status_OK)
+
+ role = {'name': 'Updated'}
+ self.patch(entity_url, auth=self.auth, body={'role': role},
+ expected_status=status_OK)
+ self.delete(entity_url, auth=self.auth,
+ expected_status=status_no_data)
+
+ role_ref = unit.new_role_ref(domain_id=domain_id)
+ self.post('/roles', auth=self.auth, body={'role': role_ref},
+ expected_status=status_created)
+
def test_user_management(self):
# First, authenticate with a user that does not have the domain
# admin role - shouldn't be able to do much.
@@ -786,13 +833,90 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
self._test_user_management(self.domainA['id'])
+ def test_user_management_normalized_keys(self):
+ """Illustrate the inconsistent handling of hyphens in keys.
+
+ To quote Morgan in bug 1526244:
+
+ the reason this is converted from "domain-id" to "domain_id" is
+ because of how we process/normalize data. The way we have to handle
+ specific data types for known columns requires avoiding "-" in the
+ actual python code since "-" is not valid for attributes in python
+ w/o significant use of "getattr" etc.
+
+ In short, historically we handle some things in conversions. The
+ use of "extras" has long been a poor design choice that leads to
+ odd/strange inconsistent behaviors because of other choices made in
+ handling data from within the body. (In many cases we convert from
+ "-" to "_" throughout openstack)
+
+ Source: https://bugs.launchpad.net/keystone/+bug/1526244/comments/9
+
+ """
+ # Authenticate with a user that has the domain admin role
+ self.auth = self.build_authentication_request(
+ user_id=self.domain_admin_user['id'],
+ password=self.domain_admin_user['password'],
+ domain_id=self.domainA['id'])
+
+ # Show that we can read a normal user without any surprises.
+ r = self.get(
+ '/users/%s' % self.just_a_user['id'],
+ auth=self.auth,
+ expected_status=http_client.OK)
+ self.assertValidUserResponse(r)
+
+ # We don't normalize query string keys, so both of these result in a
+ # 403, because we didn't specify a domain_id query string in either
+ # case, and we explicitly require one (it doesn't matter what
+ # 'domain-id' value you use).
+ self.get(
+ '/users?domain-id=%s' % self.domainA['id'],
+ auth=self.auth,
+ expected_status=exception.ForbiddenAction.code)
+ self.get(
+ '/users?domain-id=%s' % self.domainB['id'],
+ auth=self.auth,
+ expected_status=exception.ForbiddenAction.code)
+
+ # If we try updating the user's 'domain_id' by specifying a
+ # 'domain-id', then it'll be stored into extras rather than normalized,
+ # and the user's actual 'domain_id' is not affected.
+ r = self.patch(
+ '/users/%s' % self.just_a_user['id'],
+ auth=self.auth,
+ body={'user': {'domain-id': self.domainB['id']}},
+ expected_status=http_client.OK)
+ self.assertEqual(self.domainB['id'], r.json['user']['domain-id'])
+ self.assertEqual(self.domainA['id'], r.json['user']['domain_id'])
+ self.assertNotEqual(self.domainB['id'], self.just_a_user['domain_id'])
+ self.assertValidUserResponse(r, self.just_a_user)
+
+ # Finally, show that we can create a new user without any surprises.
+ # But if we specify a 'domain-id' instead of a 'domain_id', we get a
+ # Forbidden response because we fail a policy check before
+ # normalization occurs.
+ user_ref = unit.new_user_ref(domain_id=self.domainA['id'])
+ r = self.post(
+ '/users',
+ auth=self.auth,
+ body={'user': user_ref},
+ expected_status=http_client.CREATED)
+ self.assertValidUserResponse(r, ref=user_ref)
+ user_ref['domain-id'] = user_ref.pop('domain_id')
+ self.post(
+ '/users',
+ auth=self.auth,
+ body={'user': user_ref},
+ expected_status=exception.ForbiddenAction.code)
+
def test_user_management_by_cloud_admin(self):
# Test users management with a cloud admin. This user should
# be able to manage users in any domain.
self.auth = self.build_authentication_request(
user_id=self.cloud_admin_user['id'],
password=self.cloud_admin_user['password'],
- domain_id=self.admin_domain['id'])
+ project_id=self.admin_project['id'])
self._test_user_management(self.domainA['id'])
@@ -824,7 +948,7 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
self.auth = self.build_authentication_request(
user_id=self.cloud_admin_user['id'],
password=self.cloud_admin_user['password'],
- domain_id=self.admin_domain['id'])
+ project_id=self.admin_project['id'])
# Check whether cloud admin can operate a domain
# other than its own domain or not
@@ -858,10 +982,56 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
self.auth = self.build_authentication_request(
user_id=self.cloud_admin_user['id'],
password=self.cloud_admin_user['password'],
- domain_id=self.admin_domain['id'])
+ project_id=self.admin_project['id'])
self._test_grants('domains', self.domainA['id'])
+ def test_domain_grants_by_cloud_admin_for_domain_specific_role(self):
+ # Test domain grants with a cloud admin. This user should be
+ # able to manage domain roles on any domain.
+ self.auth = self.build_authentication_request(
+ user_id=self.cloud_admin_user['id'],
+ password=self.cloud_admin_user['password'],
+ project_id=self.admin_project['id'])
+
+ self._test_grants('domains', self.domainA['id'],
+ role_domain_id=self.domainB['id'])
+
+ def test_domain_grants_by_non_admin_for_domain_specific_role(self):
+ # A non-admin shouldn't be able to do anything
+ self.auth = self.build_authentication_request(
+ user_id=self.just_a_user['id'],
+ password=self.just_a_user['password'],
+ domain_id=self.domainA['id'])
+
+ self._test_grants('domains', self.domainA['id'],
+ role_domain_id=self.domainA['id'],
+ expected=exception.ForbiddenAction.code)
+ self._test_grants('domains', self.domainA['id'],
+ role_domain_id=self.domainB['id'],
+ expected=exception.ForbiddenAction.code)
+
+ def test_domain_grants_by_domain_admin_for_domain_specific_role(self):
+ # Authenticate with a user that does have the domain admin role,
+ # should not be able to assign a domain_specific role from another
+ # domain
+ self.auth = self.build_authentication_request(
+ user_id=self.domain_admin_user['id'],
+ password=self.domain_admin_user['password'],
+ domain_id=self.domainA['id'])
+
+ self._test_grants('domains', self.domainA['id'],
+ role_domain_id=self.domainB['id'],
+ # List status will always be OK, since we are not
+ # granting/checking/deleting assignments
+ list_status_OK=True,
+ expected=exception.ForbiddenAction.code)
+
+ # They should be able to assign a domain specific role from the same
+ # domain
+ self._test_grants('domains', self.domainA['id'],
+ role_domain_id=self.domainA['id'])
+
def test_project_grants(self):
self.auth = self.build_authentication_request(
user_id=self.just_a_user['id'],
@@ -890,11 +1060,67 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
self._test_grants('projects', self.project['id'])
+ def test_project_grants_by_non_admin_for_domain_specific_role(self):
+ # A non-admin shouldn't be able to do anything
+ self.auth = self.build_authentication_request(
+ user_id=self.just_a_user['id'],
+ password=self.just_a_user['password'],
+ project_id=self.project['id'])
+
+ self._test_grants('projects', self.project['id'],
+ role_domain_id=self.domainA['id'],
+ expected=exception.ForbiddenAction.code)
+ self._test_grants('projects', self.project['id'],
+ role_domain_id=self.domainB['id'],
+ expected=exception.ForbiddenAction.code)
+
+ def test_project_grants_by_project_admin_for_domain_specific_role(self):
+ # Authenticate with a user that does have the project admin role,
+ # should not be able to assign a domain_specific role from another
+ # domain
+ self.auth = self.build_authentication_request(
+ user_id=self.project_admin_user['id'],
+ password=self.project_admin_user['password'],
+ project_id=self.project['id'])
+
+ self._test_grants('projects', self.project['id'],
+ role_domain_id=self.domainB['id'],
+ # List status will always be OK, since we are not
+ # granting/checking/deleting assignments
+ list_status_OK=True,
+ expected=exception.ForbiddenAction.code)
+
+ # They should be able to assign a domain specific role from the same
+ # domain
+ self._test_grants('projects', self.project['id'],
+ role_domain_id=self.domainA['id'])
+
+ def test_project_grants_by_domain_admin_for_domain_specific_role(self):
+ # Authenticate with a user that does have the domain admin role,
+ # should not be able to assign a domain_specific role from another
+ # domain
+ self.auth = self.build_authentication_request(
+ user_id=self.domain_admin_user['id'],
+ password=self.domain_admin_user['password'],
+ domain_id=self.domainA['id'])
+
+ self._test_grants('projects', self.project['id'],
+ role_domain_id=self.domainB['id'],
+ # List status will always be OK, since we are not
+ # granting/checking/deleting assignments
+ list_status_OK=True,
+ expected=exception.ForbiddenAction.code)
+
+ # They should be able to assign a domain specific role from the same
+ # domain
+ self._test_grants('projects', self.project['id'],
+ role_domain_id=self.domainA['id'])
+
def test_cloud_admin_list_assignments_of_domain(self):
self.auth = self.build_authentication_request(
user_id=self.cloud_admin_user['id'],
password=self.cloud_admin_user['password'],
- domain_id=self.admin_domain['id'])
+ project_id=self.admin_project['id'])
collection_url = self.build_role_assignment_query_url(
domain_id=self.domainA['id'])
@@ -968,7 +1194,7 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
self.auth = self.build_authentication_request(
user_id=self.cloud_admin_user['id'],
password=self.cloud_admin_user['password'],
- domain_id=self.admin_domain['id'])
+ project_id=self.admin_project['id'])
collection_url = self.build_role_assignment_query_url(
project_id=self.project['id'])
@@ -990,7 +1216,33 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
self.assertRoleAssignmentInListResponse(r, project_admin_entity)
self.assertRoleAssignmentInListResponse(r, project_user_entity)
- @unit.utils.wip('waiting on bug #1437407')
+ def test_admin_project_list_assignments_of_project(self):
+ self.auth = self.build_authentication_request(
+ user_id=self.project_admin_user['id'],
+ password=self.project_admin_user['password'],
+ project_id=self.project['id'])
+
+ collection_url = self.build_role_assignment_query_url(
+ project_id=self.project['id'])
+ r = self.get(collection_url, auth=self.auth)
+ self.assertValidRoleAssignmentListResponse(
+ r, expected_length=2, resource_url=collection_url)
+
+ project_admin_entity = self.build_role_assignment_entity(
+ project_id=self.project['id'],
+ user_id=self.project_admin_user['id'],
+ role_id=self.admin_role['id'],
+ inherited_to_projects=False)
+ project_user_entity = self.build_role_assignment_entity(
+ project_id=self.project['id'],
+ user_id=self.just_a_user['id'],
+ role_id=self.role['id'],
+ inherited_to_projects=False)
+
+ self.assertRoleAssignmentInListResponse(r, project_admin_entity)
+ self.assertRoleAssignmentInListResponse(r, project_user_entity)
+
+ @utils.wip('waiting on bug #1437407')
def test_domain_admin_list_assignments_of_project(self):
self.auth = self.build_authentication_request(
user_id=self.domain_admin_user['id'],
@@ -1017,6 +1269,53 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
self.assertRoleAssignmentInListResponse(r, project_admin_entity)
self.assertRoleAssignmentInListResponse(r, project_user_entity)
+ def test_domain_admin_list_assignment_tree(self):
+ # Add a child project to the standard test data
+ sub_project = unit.new_project_ref(domain_id=self.domainA['id'],
+ parent_id=self.project['id'])
+ self.resource_api.create_project(sub_project['id'], sub_project)
+ self.assignment_api.create_grant(self.role['id'],
+ user_id=self.just_a_user['id'],
+ project_id=sub_project['id'])
+
+ collection_url = self.build_role_assignment_query_url(
+ project_id=self.project['id'])
+ collection_url += '&include_subtree=True'
+
+ # The domain admin should be able to list the assignment tree
+ auth = self.build_authentication_request(
+ user_id=self.domain_admin_user['id'],
+ password=self.domain_admin_user['password'],
+ domain_id=self.domainA['id'])
+
+ r = self.get(collection_url, auth=auth)
+ self.assertValidRoleAssignmentListResponse(
+ r, expected_length=3, resource_url=collection_url)
+
+ # A project admin should not be able to
+ auth = self.build_authentication_request(
+ user_id=self.project_admin_user['id'],
+ password=self.project_admin_user['password'],
+ project_id=self.project['id'])
+
+ r = self.get(collection_url, auth=auth,
+ expected_status=http_client.FORBIDDEN)
+
+ # A neither should a domain admin from a different domain
+ domainB_admin_user = unit.create_user(
+ self.identity_api,
+ domain_id=self.domainB['id'])
+ self.assignment_api.create_grant(self.admin_role['id'],
+ user_id=domainB_admin_user['id'],
+ domain_id=self.domainB['id'])
+ auth = self.build_authentication_request(
+ user_id=domainB_admin_user['id'],
+ password=domainB_admin_user['password'],
+ domain_id=self.domainB['id'])
+
+ r = self.get(collection_url, auth=auth,
+ expected_status=http_client.FORBIDDEN)
+
def test_domain_user_list_assignments_of_project_failed(self):
self.auth = self.build_authentication_request(
user_id=self.just_a_user['id'],
@@ -1040,7 +1339,23 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
self.auth = self.build_authentication_request(
user_id=self.cloud_admin_user['id'],
password=self.cloud_admin_user['password'],
- domain_id=self.admin_domain['id'])
+ project_id=self.admin_project['id'])
+
+ self._test_domain_management()
+
+ def test_admin_project(self):
+ self.auth = self.build_authentication_request(
+ user_id=self.project_admin_user['id'],
+ password=self.project_admin_user['password'],
+ project_id=self.project['id'])
+
+ self._test_domain_management(
+ expected=exception.ForbiddenAction.code)
+
+ self.auth = self.build_authentication_request(
+ user_id=self.cloud_admin_user['id'],
+ password=self.cloud_admin_user['password'],
+ project_id=self.admin_project['id'])
self._test_domain_management()
@@ -1050,16 +1365,15 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
password=self.domain_admin_user['password'],
domain_id=self.domainA['id'])
entity_url = '/domains/%s' % self.domainA['id']
- self.get(entity_url, auth=self.auth, expected_status=200)
+ self.get(entity_url, auth=self.auth)
def test_list_user_credentials(self):
- self.credential_user = self.new_credential_ref(self.just_a_user['id'])
- self.credential_api.create_credential(self.credential_user['id'],
- self.credential_user)
- self.credential_admin = self.new_credential_ref(
- self.cloud_admin_user['id'])
- self.credential_api.create_credential(self.credential_admin['id'],
- self.credential_admin)
+ credential_user = unit.new_credential_ref(self.just_a_user['id'])
+ self.credential_api.create_credential(credential_user['id'],
+ credential_user)
+ credential_admin = unit.new_credential_ref(self.cloud_admin_user['id'])
+ self.credential_api.create_credential(credential_admin['id'],
+ credential_admin)
self.auth = self.build_authentication_request(
user_id=self.just_a_user['id'],
@@ -1075,9 +1389,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
def test_get_and_delete_ec2_credentials(self):
"""Tests getting and deleting ec2 credentials through the ec2 API."""
- another_user = self.new_user_ref(domain_id=self.domainA['id'])
- password = another_user['password']
- another_user = self.identity_api.create_user(another_user)
+ another_user = unit.create_user(self.identity_api,
+ domain_id=self.domainA['id'])
# create a credential for just_a_user
just_user_auth = self.build_authentication_request(
@@ -1091,7 +1404,7 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
# another normal user can't get the credential
another_user_auth = self.build_authentication_request(
user_id=another_user['id'],
- password=password)
+ password=another_user['password'])
another_user_url = '/users/%s/credentials/OS-EC2/%s' % (
another_user['id'], r.result['credential']['access'])
self.get(another_user_url, auth=another_user_auth,
@@ -1160,7 +1473,26 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
admin_auth = self.build_authentication_request(
user_id=self.cloud_admin_user['id'],
password=self.cloud_admin_user['password'],
- domain_id=self.admin_domain['id'])
+ project_id=self.admin_project['id'])
+ admin_token = self.get_requested_token(admin_auth)
+
+ user_auth = self.build_authentication_request(
+ user_id=self.just_a_user['id'],
+ password=self.just_a_user['password'])
+ user_token = self.get_requested_token(user_auth)
+
+ self.get('/auth/tokens', token=admin_token,
+ headers={'X-Subject-Token': user_token})
+
+ def test_admin_project_validate_user_token(self):
+ # An admin can validate a user's token.
+ # This is GET /v3/auth/tokens
+
+ admin_auth = self.build_authentication_request(
+ user_id=self.project_admin_user['id'],
+ password=self.project_admin_user['password'],
+ project_id=self.project['id'])
+
admin_token = self.get_requested_token(admin_auth)
user_auth = self.build_authentication_request(
@@ -1182,7 +1514,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
token = self.get_requested_token(auth)
self.head('/auth/tokens', token=token,
- headers={'X-Subject-Token': token}, expected_status=200)
+ headers={'X-Subject-Token': token},
+ expected_status=http_client.OK)
def test_user_check_user_token(self):
# A user can check one of their own tokens.
@@ -1195,7 +1528,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
token2 = self.get_requested_token(auth)
self.head('/auth/tokens', token=token1,
- headers={'X-Subject-Token': token2}, expected_status=200)
+ headers={'X-Subject-Token': token2},
+ expected_status=http_client.OK)
def test_user_check_other_user_token_rejected(self):
# A user cannot check another user's token.
@@ -1231,7 +1565,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
user_token = self.get_requested_token(user_auth)
self.head('/auth/tokens', token=admin_token,
- headers={'X-Subject-Token': user_token}, expected_status=200)
+ headers={'X-Subject-Token': user_token},
+ expected_status=http_client.OK)
def test_user_revoke_same_token(self):
# Given a non-admin user token, the token can be used to revoke
@@ -1294,3 +1629,149 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase,
self.delete('/auth/tokens', token=admin_token,
headers={'X-Subject-Token': user_token})
+
+ def test_user_with_a_role_get_project(self):
+ user_auth = self.build_authentication_request(
+ user_id=self.just_a_user['id'],
+ password=self.just_a_user['password'],
+ project_id=self.project['id'])
+
+ # Test user can get project for one they have a role in
+ self.get('/projects/%s' % self.project['id'], auth=user_auth)
+
+ # Test user can not get project for one they don't have a role in,
+ # even if they have a role on another project
+ project2 = unit.new_project_ref(domain_id=self.domainA['id'])
+ self.resource_api.create_project(project2['id'], project2)
+ self.get('/projects/%s' % project2['id'], auth=user_auth,
+ expected_status=exception.ForbiddenAction.code)
+
+ def test_project_admin_get_project(self):
+ admin_auth = self.build_authentication_request(
+ user_id=self.project_admin_user['id'],
+ password=self.project_admin_user['password'],
+ project_id=self.project['id'])
+
+ resp = self.get('/projects/%s' % self.project['id'], auth=admin_auth)
+ self.assertEqual(self.project['id'],
+ jsonutils.loads(resp.body)['project']['id'])
+
+ def test_role_management_no_admin_no_rights(self):
+ # A non-admin domain user shouldn't be able to manipulate roles
+ self.auth = self.build_authentication_request(
+ user_id=self.just_a_user['id'],
+ password=self.just_a_user['password'],
+ domain_id=self.domainA['id'])
+
+ self._role_management_cases(expected=exception.ForbiddenAction.code)
+
+ # ...and nor should non-admin project user
+ self.auth = self.build_authentication_request(
+ user_id=self.just_a_user['id'],
+ password=self.just_a_user['password'],
+ project_id=self.project['id'])
+
+ self._role_management_cases(expected=exception.ForbiddenAction.code)
+
+ def test_role_management_with_project_admin(self):
+ # A project admin user should be able to get and list, but not be able
+ # to create/update/delete global roles
+ self.auth = self.build_authentication_request(
+ user_id=self.project_admin_user['id'],
+ password=self.project_admin_user['password'],
+ project_id=self.project['id'])
+
+ self._role_management_cases(read_status_OK=True,
+ expected=exception.ForbiddenAction.code)
+
+ def test_role_management_with_domain_admin(self):
+ # A domain admin user should be able to get and list, but not be able
+ # to create/update/delete global roles
+ self.auth = self.build_authentication_request(
+ user_id=self.domain_admin_user['id'],
+ password=self.domain_admin_user['password'],
+ domain_id=self.domainA['id'])
+
+ self._role_management_cases(read_status_OK=True,
+ expected=exception.ForbiddenAction.code)
+
+ def test_role_management_with_cloud_admin(self):
+ # A cloud admin user should have rights to manipulate global roles
+ self.auth = self.build_authentication_request(
+ user_id=self.cloud_admin_user['id'],
+ password=self.cloud_admin_user['password'],
+ project_id=self.admin_project['id'])
+
+ self._role_management_cases()
+
+ def test_domain_role_management_no_admin_no_rights(self):
+ # A non-admin domain user shouldn't be able to manipulate domain roles
+ self.auth = self.build_authentication_request(
+ user_id=self.just_a_user['id'],
+ password=self.just_a_user['password'],
+ domain_id=self.domainA['id'])
+
+ self._domain_role_management_cases(
+ self.domainA['id'], expected=exception.ForbiddenAction.code)
+
+ # ...and nor should non-admin project user
+ self.auth = self.build_authentication_request(
+ user_id=self.just_a_user['id'],
+ password=self.just_a_user['password'],
+ project_id=self.project['id'])
+
+ self._domain_role_management_cases(
+ self.domainA['id'], expected=exception.ForbiddenAction.code)
+
+ def test_domain_role_management_with_cloud_admin(self):
+ # A cloud admin user should have rights to manipulate domain roles
+ self.auth = self.build_authentication_request(
+ user_id=self.cloud_admin_user['id'],
+ password=self.cloud_admin_user['password'],
+ project_id=self.admin_project['id'])
+
+ self._domain_role_management_cases(self.domainA['id'])
+
+ def test_domain_role_management_with_domain_admin(self):
+ # A domain admin user should only be able to manipulate the domain
+ # specific roles in their own domain
+ self.auth = self.build_authentication_request(
+ user_id=self.domainB_admin_user['id'],
+ password=self.domainB_admin_user['password'],
+ domain_id=self.domainB['id'])
+
+ # Try to access the domain specific roles in another domain
+ self._domain_role_management_cases(
+ self.domainA['id'], expected=exception.ForbiddenAction.code)
+
+ # ...but they should be able to work with those in their own domain
+ self.auth = self.build_authentication_request(
+ user_id=self.domain_admin_user['id'],
+ password=self.domain_admin_user['password'],
+ domain_id=self.domainA['id'])
+
+ self._domain_role_management_cases(self.domainA['id'])
+
+ def test_domain_role_management_with_project_admin(self):
+ # A project admin user should have not access to domain specific roles
+ # in another domain. They should be able to get and list domain
+ # specific roles from their own domain, but not be able to create,
+ # update or delete them,
+ self.auth = self.build_authentication_request(
+ user_id=self.project_adminB_user['id'],
+ password=self.project_adminB_user['password'],
+ project_id=self.projectB['id'])
+
+ # Try access the domain specific roless in another domain
+ self._domain_role_management_cases(
+ self.domainA['id'], expected=exception.ForbiddenAction.code)
+
+ # ...but they should be ablet to work with those in their own domain
+ self.auth = self.build_authentication_request(
+ user_id=self.project_admin_user['id'],
+ password=self.project_admin_user['password'],
+ project_id=self.project['id'])
+
+ self._domain_role_management_cases(
+ self.domainA['id'], read_status_OK=True,
+ expected=exception.ForbiddenAction.code)