Allow for stack users in _authorize_stack_user
This change allows stack user credentials to be used to call describe_stack_resource (heat resource-metadata) It makes the following changes: * _authorize_stack_user first attempts authorize with stack.access_allowed using the context user_id before falling back to looking for ec2 credentials * context middleware sets the user_id on the context even when the username is not specified This change also adds missing test coverage to ContextMiddleware Change-Id: Idb655e403ba11a3144dacf34eba0feb59ab8d911 Closes-Bug: #1299982
This commit is contained in:
parent
5e7344b2e3
commit
57f5089083
|
@ -132,17 +132,16 @@ class ContextMiddleware(wsgi.Middleware):
|
|||
|
||||
try:
|
||||
username = None
|
||||
user_id = None
|
||||
password = None
|
||||
aws_creds = None
|
||||
|
||||
if headers.get('X-Auth-User') is not None:
|
||||
username = headers.get('X-Auth-User')
|
||||
user_id = headers.get('X-User-Id')
|
||||
password = headers.get('X-Auth-Key')
|
||||
elif headers.get('X-Auth-EC2-Creds') is not None:
|
||||
aws_creds = headers.get('X-Auth-EC2-Creds')
|
||||
|
||||
user_id = headers.get('X-User-Id')
|
||||
token = headers.get('X-Auth-Token')
|
||||
tenant = headers.get('X-Tenant-Name')
|
||||
tenant_id = headers.get('X-Tenant-Id')
|
||||
|
|
|
@ -839,8 +839,11 @@ class EngineService(service.Service):
|
|||
- The user must map to a User resource defined in the requested stack
|
||||
- The user resource must validate OK against any Policy specified
|
||||
'''
|
||||
# We're expecting EC2 credentials because all in-instance credentials
|
||||
# are deployed as ec2 keypairs
|
||||
# first check whether access is allowd by context user_id
|
||||
if stack.access_allowed(cnxt.user_id, resource_name):
|
||||
return True
|
||||
|
||||
# fall back to looking for EC2 credentials in the context
|
||||
try:
|
||||
ec2_creds = json.loads(cnxt.aws_creds).get('ec2Credentials')
|
||||
except (TypeError, AttributeError):
|
||||
|
|
|
@ -13,8 +13,12 @@
|
|||
|
||||
import mock
|
||||
import os
|
||||
from oslo.config import cfg
|
||||
import webob
|
||||
|
||||
from heat.common import context
|
||||
from heat.common import exception
|
||||
from heat.openstack.common import policy as base_policy
|
||||
from heat.tests.common import HeatTestCase
|
||||
|
||||
policy_path = os.path.dirname(os.path.realpath(__file__)) + "/policy/"
|
||||
|
@ -94,3 +98,122 @@ class TestRequestContext(HeatTestCase):
|
|||
pc.return_value = False
|
||||
ctx = context.RequestContext(roles=['notadmin'])
|
||||
self.assertFalse(ctx.is_admin)
|
||||
|
||||
|
||||
class RequestContextMiddlewareTest(HeatTestCase):
|
||||
|
||||
scenarios = [(
|
||||
'empty_headers',
|
||||
dict(
|
||||
headers={},
|
||||
expected_exception=None,
|
||||
context_dict={
|
||||
'auth_token': None,
|
||||
'auth_url': None,
|
||||
'aws_creds': None,
|
||||
'is_admin': False,
|
||||
'password': None,
|
||||
'roles': [],
|
||||
'show_deleted': False,
|
||||
'tenant': None,
|
||||
'tenant_id': None,
|
||||
'trust_id': None,
|
||||
'trustor_user_id': None,
|
||||
'user': None,
|
||||
'user_id': None,
|
||||
'username': None
|
||||
})
|
||||
), (
|
||||
'username_password',
|
||||
dict(
|
||||
headers={
|
||||
'X-Auth-User': 'my_username',
|
||||
'X-Auth-Key': 'my_password',
|
||||
'X-Auth-EC2-Creds': '{"ec2Credentials": {}}',
|
||||
'X-User-Id': '7a87ff18-31c6-45ce-a186-ec7987f488c3',
|
||||
'X-Auth-Token': 'atoken',
|
||||
'X-Tenant-Name': 'my_tenant',
|
||||
'X-Tenant-Id': 'db6808c8-62d0-4d92-898c-d644a6af20e9',
|
||||
'X-Auth-Url': 'http://192.0.2.1:5000/v1',
|
||||
'X-Roles': 'role1,role2,role3'
|
||||
},
|
||||
expected_exception=None,
|
||||
context_dict={
|
||||
'auth_token': 'atoken',
|
||||
'auth_url': 'http://192.0.2.1:5000/v1',
|
||||
'aws_creds': None,
|
||||
'is_admin': False,
|
||||
'password': 'my_password',
|
||||
'roles': ['role1', 'role2', 'role3'],
|
||||
'show_deleted': False,
|
||||
'tenant': 'my_tenant',
|
||||
'tenant_id': 'db6808c8-62d0-4d92-898c-d644a6af20e9',
|
||||
'trust_id': None,
|
||||
'trustor_user_id': None,
|
||||
'user': 'my_username',
|
||||
'user_id': '7a87ff18-31c6-45ce-a186-ec7987f488c3',
|
||||
'username': 'my_username'
|
||||
})
|
||||
), (
|
||||
'aws_creds',
|
||||
dict(
|
||||
headers={
|
||||
'X-Auth-EC2-Creds': '{"ec2Credentials": {}}',
|
||||
'X-User-Id': '7a87ff18-31c6-45ce-a186-ec7987f488c3',
|
||||
'X-Auth-Token': 'atoken',
|
||||
'X-Tenant-Name': 'my_tenant',
|
||||
'X-Tenant-Id': 'db6808c8-62d0-4d92-898c-d644a6af20e9',
|
||||
'X-Auth-Url': 'http://192.0.2.1:5000/v1',
|
||||
'X-Roles': 'role1,role2,role3',
|
||||
},
|
||||
expected_exception=None,
|
||||
context_dict={
|
||||
'auth_token': 'atoken',
|
||||
'auth_url': 'http://192.0.2.1:5000/v1',
|
||||
'aws_creds': '{"ec2Credentials": {}}',
|
||||
'is_admin': False,
|
||||
'password': None,
|
||||
'roles': ['role1', 'role2', 'role3'],
|
||||
'show_deleted': False,
|
||||
'tenant': 'my_tenant',
|
||||
'tenant_id': 'db6808c8-62d0-4d92-898c-d644a6af20e9',
|
||||
'trust_id': None,
|
||||
'trustor_user_id': None,
|
||||
'user': None,
|
||||
'user_id': '7a87ff18-31c6-45ce-a186-ec7987f488c3',
|
||||
'username': None
|
||||
})
|
||||
), (
|
||||
'malformed_roles',
|
||||
dict(
|
||||
headers={
|
||||
'X-Roles': [],
|
||||
},
|
||||
expected_exception=exception.NotAuthenticated)
|
||||
)]
|
||||
|
||||
def setUp(self):
|
||||
super(RequestContextMiddlewareTest, self).setUp()
|
||||
opts = [
|
||||
cfg.StrOpt('config_dir', default=policy_path),
|
||||
cfg.StrOpt('config_file', default='foo'),
|
||||
cfg.StrOpt('project', default='heat'),
|
||||
]
|
||||
cfg.CONF.register_opts(opts)
|
||||
pf = policy_path + 'check_admin.json'
|
||||
self.m.StubOutWithMock(base_policy.Enforcer, '_get_policy_path')
|
||||
base_policy.Enforcer._get_policy_path().MultipleTimes().AndReturn(pf)
|
||||
self.m.ReplayAll()
|
||||
|
||||
def test_context_middleware(self):
|
||||
|
||||
middleware = context.ContextMiddleware(None, None)
|
||||
request = webob.Request.blank('/stacks', headers=self.headers)
|
||||
if self.expected_exception:
|
||||
self.assertRaises(
|
||||
self.expected_exception, middleware.process_request, request)
|
||||
else:
|
||||
self.assertIsNone(middleware.process_request(request))
|
||||
ctx = request.context.to_dict()
|
||||
for k, v in self.context_dict.items():
|
||||
self.assertEqual(v, ctx[k], 'Key %s values do not match' % k)
|
||||
|
|
|
@ -163,6 +163,13 @@ user_policy_template = '''
|
|||
}
|
||||
'''
|
||||
|
||||
server_config_template = '''
|
||||
heat_template_version: 2013-05-23
|
||||
resources:
|
||||
WebServer:
|
||||
type: OS::Nova::Server
|
||||
'''
|
||||
|
||||
|
||||
def get_wordpress_stack(stack_name, ctx):
|
||||
t = template_format.parse(wp_template)
|
||||
|
@ -1372,6 +1379,31 @@ class StackServiceAuthorizeTest(HeatTestCase):
|
|||
self.stack.delete()
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_stack_authorize_stack_user_user_id(self):
|
||||
self.ctx = utils.dummy_context(user_id=str(uuid.uuid4()))
|
||||
stack = get_stack('stack_authorize_stack_user',
|
||||
self.ctx,
|
||||
server_config_template)
|
||||
self.stack = stack
|
||||
|
||||
def handler(resource_name):
|
||||
return resource_name == 'WebServer'
|
||||
|
||||
self.stack.register_access_allowed_handler(self.ctx.user_id, handler)
|
||||
|
||||
# matching credential_id and resource_name
|
||||
self.assertTrue(self.eng._authorize_stack_user(
|
||||
self.ctx, self.stack, 'WebServer'))
|
||||
|
||||
# not matching resource_name
|
||||
self.assertFalse(self.eng._authorize_stack_user(
|
||||
self.ctx, self.stack, 'NoSuchResource'))
|
||||
|
||||
# not matching credential_id
|
||||
self.ctx.user_id = str(uuid.uuid4())
|
||||
self.assertFalse(self.eng._authorize_stack_user(
|
||||
self.ctx, self.stack, 'WebServer'))
|
||||
|
||||
|
||||
class StackServiceTest(HeatTestCase):
|
||||
|
||||
|
|
|
@ -133,11 +133,12 @@ def reset_dummy_db():
|
|||
|
||||
|
||||
def dummy_context(user='test_username', tenant_id='test_tenant_id',
|
||||
password='password', roles=[]):
|
||||
password='password', roles=[], user_id=None):
|
||||
return context.RequestContext.from_dict({
|
||||
'tenant_id': tenant_id,
|
||||
'tenant': 'test_tenant',
|
||||
'username': user,
|
||||
'user_id': user_id,
|
||||
'password': password,
|
||||
'roles': roles,
|
||||
'is_admin': False,
|
||||
|
|
Loading…
Reference in New Issue