Rework service broker authorization process

Now we can use keystonemiddleware and murano context middleware for requests
from outside OpenStack so we don't need to recreate keystoneclient for each
request and can easily get token from the request headers. That makes
authorization proccess a lot easier in terms of source code.

Change-Id: If937501074b85d43921fdb108f8af3babeded828
Closes-Bug: #1512254
This commit is contained in:
Nikolay Starodubtsev 2015-11-11 14:12:16 +03:00
parent b985627eaa
commit c508be37a2
4 changed files with 17 additions and 90 deletions

View File

@ -1,5 +1,5 @@
[pipeline:cloudfoundry]
pipeline = cloudfoundryapi
pipeline = request_id ssl ext_context authtoken context cloudfoundryapi
[pipeline:murano]
pipeline = request_id ssl versionnegotiation faultwrap authtoken context rootapp

View File

@ -1,28 +0,0 @@
# Copyright (c) 2015 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from keystoneclient.v3 import client
from oslo_config import cfg
CONF = cfg.CONF
def authenticate(user, password, tenant=None):
project_name = tenant or CONF.cfapi.tenant
keystone = client.Client(username=user,
password=password,
project_name=project_name,
auth_url=CONF.cfapi.auth_url.replace(
'v2.0', 'v3'))
return keystone

View File

@ -12,7 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import base64
import json
import uuid
@ -20,13 +19,10 @@ import muranoclient.client as client
from oslo_config import cfg
from oslo_log import log as logging
import six
from webob import exc
from webob import response
from murano.api.v1.cloudfoundry import auth as keystone_auth
from murano.common.i18n import _LI
from murano.common import wsgi
from murano import context
from murano.db.catalog import api as db_api
from murano.db.services import cf_connections as db_cf
@ -64,21 +60,6 @@ class Controller(object):
srv['plans'] = [plan]
return srv
def _check_auth(self, req, tenant=None):
auth = req.headers.get('Authorization', None)
if auth is None:
raise exc.HTTPUnauthorized(explanation='Bad credentials')
auth_info = auth.split(' ')[1]
auth_decoded = base64.b64decode(auth_info)
user = auth_decoded.split(':')[0]
password = auth_decoded.split(':')[1]
if tenant:
keystone = keystone_auth.authenticate(user, password, tenant)
else:
keystone = keystone_auth.authenticate(user, password)
return (user, password, keystone)
def _make_service(self, name, package, plan_id):
id = uuid.uuid4().hex
@ -94,13 +75,9 @@ class Controller(object):
return None
def list(self, req):
user, _, keystone = self._check_auth(req)
# Once we get here we were authorized by keystone
token = keystone.auth_token
ctx = context.RequestContext(user=user, tenant='', auth_token=token)
packages = db_api.package_search({'type': 'application'}, ctx,
packages = db_api.package_search({'type': 'application'},
req.context,
catalog=True)
services = []
for package in packages:
@ -135,19 +112,13 @@ class Controller(object):
try:
tenant = db_cf.get_tenant_for_org(org_guid)
except AttributeError:
# FIXME(Kezar): need to find better way to get tenant
tenant = CONF.cfapi.tenant
tenant = req.headers['X-Project-Id']
db_cf.set_tenant_for_org(org_guid, tenant)
LOG.info(_LI("Cloud Foundry {org_id} mapped to tenant "
"{tenant_name}").format(org_id=org_guid,
tenant_name=tenant))
# Now as we have all parameters we can try to auth user in actual
# tenant
user, _, keystone = self._check_auth(req, tenant)
# Once we get here we were authorized by keystone
token = keystone.auth_token
token = req.headers['X-Auth-Token']
m_cli = muranoclient(token)
try:
environment_id = db_cf.get_environment_for_space(space_guid)
@ -160,11 +131,7 @@ class Controller(object):
.format(space_id=space_guid,
environment_id=environment_id))
LOG.debug('Keystone endpoint: {0}'.format(keystone.auth_ref))
tenant_id = keystone.project_id
ctx = context.RequestContext(user=user, tenant=tenant_id)
package = db_api.package_get(service_id, ctx)
package = db_api.package_get(service_id, req.context)
LOG.debug('Adding service {name}'.format(name=package.name))
service = self._make_service(space_guid, package, plan_id)
@ -198,10 +165,7 @@ class Controller(object):
service_id = service.service_id
environment_id = service.environment_id
tenant = service.tenant
_, _, keystone = self._check_auth(req, tenant)
# Once we get here we were authorized by keystone
token = keystone.auth_token
token = req.headers['X-Auth-Token']
m_cli = muranoclient(token)
session_id = create_session(m_cli, environment_id)
@ -217,10 +181,7 @@ class Controller(object):
service_id = db_service.service_id
environment_id = db_service.environment_id
tenant = db_service.tenant
_, _, keystone = self._check_auth(req, tenant)
# Once we get here we were authorized by keystone
token = keystone.auth_token
token = req.headers['X-Auth-Token']
m_cli = muranoclient(token)
session_id = create_session(m_cli, environment_id)
@ -250,10 +211,7 @@ class Controller(object):
def get_last_operation(self, req, instance_id):
service = db_cf.get_service_for_instance(instance_id)
env_id = service.environment_id
tenant = service.tenant
_, _, keystone = self._check_auth(req, tenant)
# Once we get here we were authorized by keystone
token = keystone.auth_token
token = req.headers["X-Auth-Token"]
m_cli = muranoclient(token)
# NOTE(starodubcevna): we can track only environment status. it's
@ -276,6 +234,7 @@ class Controller(object):
def muranoclient(token_id):
# TODO(starodubcevna): I guess it can be done better.
endpoint = "http://{murano_host}:{murano_port}".format(
murano_host=CONF.bind_host, murano_port=CONF.bind_port)
insecure = False

View File

@ -29,12 +29,12 @@ class TestController(base.MuranoTestCase):
self.request = mock.MagicMock()
self.request.headers = {'Authorization': 'Basic {encoded}'.format(
encoded=base64.b64encode('test:test'))}
encoded=base64.b64encode('test:test')), 'X-Auth-Token': 'foo-bar',
'X-Project-Id': 'bar-baz'}
@mock.patch('murano.common.policy.check_is_admin')
@mock.patch('murano.db.catalog.api.package_search')
@mock.patch('murano.api.v1.cloudfoundry.auth.authenticate')
def test_list(self, mock_auth, mock_db_search, mock_policy):
def test_list(self, mock_db_search, mock_policy):
pkg0 = mock.MagicMock()
pkg0.id = 'xxx'
@ -64,8 +64,7 @@ class TestController(base.MuranoTestCase):
@mock.patch('murano.db.services.cf_connections.set_instance_for_service')
@mock.patch('murano.db.services.cf_connections.get_environment_for_space')
@mock.patch('murano.db.services.cf_connections.get_tenant_for_org')
@mock.patch('murano.api.v1.cloudfoundry.auth.authenticate')
def test_provision_from_scratch(self, mock_auth, mock_get_tenant,
def test_provision_from_scratch(self, mock_get_tenant,
mock_get_environment, mock_is, mock_client,
mock_package, mock_policy):
@ -93,8 +92,7 @@ class TestController(base.MuranoTestCase):
@mock.patch('murano.db.services.cf_connections.set_tenant_for_org')
@mock.patch('murano.db.services.cf_connections.get_environment_for_space')
@mock.patch('murano.db.services.cf_connections.get_tenant_for_org')
@mock.patch('murano.api.v1.cloudfoundry.auth.authenticate')
def test_provision_existent(self, mock_auth, mock_get_tenant,
def test_provision_existent(self, mock_get_tenant,
mock_get_environment, mock_set_tenant,
mock_set_environment, mock_is, mock_client,
mock_package, mock_policy):
@ -116,9 +114,8 @@ class TestController(base.MuranoTestCase):
self.assertIsInstance(resp, response.Response)
@mock.patch('murano.api.v1.cloudfoundry.cfapi.muranoclient')
@mock.patch('murano.api.v1.cloudfoundry.auth.authenticate')
@mock.patch('murano.db.services.cf_connections.get_service_for_instance')
def test_deprovision(self, mock_get_si, mock_auth, mock_client):
def test_deprovision(self, mock_get_si, mock_client):
service = mock.MagicMock()
service.service_id = '111-111'
service.tenant_id = '222-222'
@ -131,8 +128,7 @@ class TestController(base.MuranoTestCase):
@mock.patch('murano.api.v1.cloudfoundry.cfapi.muranoclient')
@mock.patch('murano.db.services.cf_connections.get_service_for_instance')
@mock.patch('murano.api.v1.cloudfoundry.auth.authenticate')
def test_bind(self, mock_auth, mock_get_si, mock_client):
def test_bind(self, mock_get_si, mock_client):
service = mock.MagicMock()
service.service_id = '111-111'
service.tenant_id = '222-222'