Authorize super admin actions on all projects
This allows admin super user (user with admin role in admin_project) to do stack operations across all projects. Change-Id: Ifbf56fde02b89248ee788e6a212ef9d11e665dc0 Partial-Bug: #1466694
This commit is contained in:
parent
9722a78840
commit
ac86702172
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"context_is_admin": "role:admin",
|
"context_is_admin": "role:admin and auth_token_info.token.is_admin_project:True",
|
||||||
"deny_stack_user": "not role:heat_stack_user",
|
"deny_stack_user": "not role:heat_stack_user",
|
||||||
"deny_everybody": "!",
|
"deny_everybody": "!",
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ def policy_enforce(handler):
|
||||||
"""
|
"""
|
||||||
@six.wraps(handler)
|
@six.wraps(handler)
|
||||||
def handle_stack_method(controller, req, tenant_id, **kwargs):
|
def handle_stack_method(controller, req, tenant_id, **kwargs):
|
||||||
if req.context.tenant_id != tenant_id:
|
if req.context.tenant_id != tenant_id and not req.context.is_admin:
|
||||||
raise exc.HTTPForbidden()
|
raise exc.HTTPForbidden()
|
||||||
allowed = req.context.policy.enforce(context=req.context,
|
allowed = req.context.policy.enforce(context=req.context,
|
||||||
action=handler.__name__,
|
action=handler.__name__,
|
||||||
|
|
|
@ -419,9 +419,10 @@ def stack_get(context, stack_id, show_deleted=False, tenant_safe=True,
|
||||||
|
|
||||||
# One exception to normal project scoping is users created by the
|
# One exception to normal project scoping is users created by the
|
||||||
# stacks in the stack_user_project_id (in the heat stack user domain)
|
# stacks in the stack_user_project_id (in the heat stack user domain)
|
||||||
if (tenant_safe and result is not None and context is not None and
|
if (tenant_safe and result is not None
|
||||||
context.tenant_id not in (result.tenant,
|
and context is not None and not context.is_admin
|
||||||
result.stack_user_project_id)):
|
and context.tenant_id not in (result.tenant,
|
||||||
|
result.stack_user_project_id)):
|
||||||
return None
|
return None
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -492,7 +493,7 @@ def _query_stack_get_all(context, tenant_safe=True, show_deleted=False,
|
||||||
context, models.Stack, show_deleted=show_deleted
|
context, models.Stack, show_deleted=show_deleted
|
||||||
).filter_by(owner_id=None)
|
).filter_by(owner_id=None)
|
||||||
|
|
||||||
if tenant_safe:
|
if tenant_safe and not context.is_admin:
|
||||||
query = query.filter_by(tenant=context.tenant_id)
|
query = query.filter_by(tenant=context.tenant_id)
|
||||||
|
|
||||||
query = query.options(orm.subqueryload("tags"))
|
query = query.options(orm.subqueryload("tags"))
|
||||||
|
@ -970,7 +971,7 @@ def software_config_get(context, config_id):
|
||||||
def software_config_get_all(context, limit=None, marker=None,
|
def software_config_get_all(context, limit=None, marker=None,
|
||||||
tenant_safe=True):
|
tenant_safe=True):
|
||||||
query = model_query(context, models.SoftwareConfig)
|
query = model_query(context, models.SoftwareConfig)
|
||||||
if tenant_safe:
|
if tenant_safe and not context.is_admin:
|
||||||
query = query.filter_by(tenant=context.tenant_id)
|
query = query.filter_by(tenant=context.tenant_id)
|
||||||
return _paginate_query(context, query, models.SoftwareConfig,
|
return _paginate_query(context, query, models.SoftwareConfig,
|
||||||
limit=limit, marker=marker).all()
|
limit=limit, marker=marker).all()
|
||||||
|
|
|
@ -486,7 +486,8 @@ class EngineService(service.Service):
|
||||||
raise exception.EntityNotFound(entity='Stack',
|
raise exception.EntityNotFound(entity='Stack',
|
||||||
name=identity.stack_name)
|
name=identity.stack_name)
|
||||||
|
|
||||||
if cnxt.tenant_id not in (identity.tenant, s.stack_user_project_id):
|
if not cnxt.is_admin and cnxt.tenant_id not in (
|
||||||
|
identity.tenant, s.stack_user_project_id):
|
||||||
# The DB API should not allow this, but sanity-check anyway..
|
# The DB API should not allow this, but sanity-check anyway..
|
||||||
raise exception.InvalidTenant(target=identity.tenant,
|
raise exception.InvalidTenant(target=identity.tenant,
|
||||||
actual=cnxt.tenant_id)
|
actual=cnxt.tenant_id)
|
||||||
|
|
|
@ -110,6 +110,18 @@ class TestPolicyEnforce(common.HeatTestCase):
|
||||||
self.controller.an_action,
|
self.controller.an_action,
|
||||||
self.req, tenant_id='bar')
|
self.req, tenant_id='bar')
|
||||||
|
|
||||||
|
@mock.patch.object(policy.Enforcer, 'enforce')
|
||||||
|
def test_policy_enforce_tenant_mismatch_is_admin(self, mock_enforce):
|
||||||
|
self.req.context = context.RequestContext(tenant_id='foo',
|
||||||
|
is_admin=True)
|
||||||
|
mock_enforce.return_value = True
|
||||||
|
|
||||||
|
self.assertEqual('woot',
|
||||||
|
self.controller.an_action(self.req, 'foo'))
|
||||||
|
|
||||||
|
self.assertEqual('woot',
|
||||||
|
self.controller.an_action(self.req, 'bar'))
|
||||||
|
|
||||||
@mock.patch.object(policy.Enforcer, 'enforce')
|
@mock.patch.object(policy.Enforcer, 'enforce')
|
||||||
def test_policy_enforce_policy_deny(self, mock_enforce):
|
def test_policy_enforce_policy_deny(self, mock_enforce):
|
||||||
mock_enforce.return_value = False
|
mock_enforce.return_value = False
|
||||||
|
|
|
@ -1847,11 +1847,19 @@ class DBAPIStackTest(common.HeatTestCase):
|
||||||
def test_stack_get_can_return_a_stack_from_different_tenant(self):
|
def test_stack_get_can_return_a_stack_from_different_tenant(self):
|
||||||
stack = create_stack(self.ctx, self.template, self.user_creds)
|
stack = create_stack(self.ctx, self.template, self.user_creds)
|
||||||
self.ctx.tenant_id = 'abc'
|
self.ctx.tenant_id = 'abc'
|
||||||
|
# with tenant_safe = False
|
||||||
ret_stack = db_api.stack_get(self.ctx, stack.id,
|
ret_stack = db_api.stack_get(self.ctx, stack.id,
|
||||||
show_deleted=False, tenant_safe=False)
|
show_deleted=False, tenant_safe=False)
|
||||||
self.assertEqual(stack.id, ret_stack.id)
|
self.assertEqual(stack.id, ret_stack.id)
|
||||||
self.assertEqual('db_test_stack_name', ret_stack.name)
|
self.assertEqual('db_test_stack_name', ret_stack.name)
|
||||||
|
|
||||||
|
# with ctx.is_admin = True
|
||||||
|
self.ctx.is_admin = True
|
||||||
|
ret_stack = db_api.stack_get(self.ctx, stack.id,
|
||||||
|
show_deleted=False)
|
||||||
|
self.assertEqual(stack.id, ret_stack.id)
|
||||||
|
self.assertEqual('db_test_stack_name', ret_stack.name)
|
||||||
|
|
||||||
def test_stack_get_by_name(self):
|
def test_stack_get_by_name(self):
|
||||||
stack = create_stack(self.ctx, self.template, self.user_creds)
|
stack = create_stack(self.ctx, self.template, self.user_creds)
|
||||||
ret_stack = db_api.stack_get_by_name(self.ctx, stack.name)
|
ret_stack = db_api.stack_get_by_name(self.ctx, stack.name)
|
||||||
|
@ -1934,6 +1942,21 @@ class DBAPIStackTest(common.HeatTestCase):
|
||||||
stacks = db_api.stack_get_all(self.ctx, tenant_safe=False)
|
stacks = db_api.stack_get_all(self.ctx, tenant_safe=False)
|
||||||
self.assertEqual(5, len(stacks))
|
self.assertEqual(5, len(stacks))
|
||||||
|
|
||||||
|
def test_stack_get_all_with_admin_context(self):
|
||||||
|
values = [
|
||||||
|
{'tenant': UUID1},
|
||||||
|
{'tenant': UUID1},
|
||||||
|
{'tenant': UUID2},
|
||||||
|
{'tenant': UUID2},
|
||||||
|
{'tenant': UUID2},
|
||||||
|
]
|
||||||
|
[create_stack(self.ctx, self.template, self.user_creds,
|
||||||
|
**val) for val in values]
|
||||||
|
|
||||||
|
self.ctx.is_admin = True
|
||||||
|
stacks = db_api.stack_get_all(self.ctx)
|
||||||
|
self.assertEqual(5, len(stacks))
|
||||||
|
|
||||||
def test_stack_count_all_with_regular_tenant(self):
|
def test_stack_count_all_with_regular_tenant(self):
|
||||||
values = [
|
values = [
|
||||||
{'tenant': UUID1},
|
{'tenant': UUID1},
|
||||||
|
|
|
@ -50,6 +50,7 @@ class EncryptionVolTypeTest(functional_base.FunctionalTestsBase):
|
||||||
# Temporarily switch to admin
|
# Temporarily switch to admin
|
||||||
self.conf.username = self.conf.admin_username
|
self.conf.username = self.conf.admin_username
|
||||||
self.conf.password = self.conf.admin_password
|
self.conf.password = self.conf.admin_password
|
||||||
|
self.conf.tenant_name = 'admin'
|
||||||
self.manager = clients.ClientManager(self.conf)
|
self.manager = clients.ClientManager(self.conf)
|
||||||
self.client = self.manager.orchestration_client
|
self.client = self.manager.orchestration_client
|
||||||
self.volume_client = self.manager.volume_client
|
self.volume_client = self.manager.volume_client
|
||||||
|
|
Loading…
Reference in New Issue