1)
http://fosshelp.blogspot.in/2014/03/how-to-openstack-api-and-wsgi-api.html
2)
vim /etc/heat/api-paste.ini
# heat-api pipeline
[pipeline:heat-api]
pipeline = faultwrap ssl versionnegotiation authurl authtoken context apiv1app
[filter:faultwrap]
paste.filter_factory = heat.common.wsgi:filter_factory
heat.filter_factory = heat.api.openstack:faultwrap_filter
[filter:ssl]
paste.filter_factory = heat.common.wsgi:filter_factory
heat.filter_factory = heat.api.openstack:sslmiddleware_filter
[filter:versionnegotiation]
paste.filter_factory = heat.common.wsgi:filter_factory
heat.filter_factory = heat.api.openstack:version_negotiation_filter
# Middleware to set auth_url header appropriately
[filter:authurl]
paste.filter_factory = heat.common.auth_url:filter_factory
# Auth middleware that validates token against keystone
[filter:authtoken]
paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory <===
[filter:context]
paste.filter_factory = heat.common.context:ContextMiddleware_filter_factory
[app:apiv1app]
paste.app_factory = heat.common.wsgi:app_factory
heat.app_factory = heat.api.openstack.v1:API
3)
authurl filter
/usr/lib/python2.7/dist-packages/heat/common/auth_url.py
from oslo.config import cfg
from webob.exc import HTTPBadRequest
from webob.exc import HTTPUnauthorized
from heat.common import wsgi
from heat.openstack.common import importutils
class AuthUrlFilter(wsgi.Middleware):
def __init__(self, app, conf):
super(AuthUrlFilter, self).__init__(app)
self.conf = conf
self.auth_url = self._get_auth_url() <===
def _get_auth_url(self):
if 'auth_uri' in self.conf:
return self.conf['auth_uri']
else:
# Import auth_token to have keystone_authtoken settings setup.
auth_token_module = 'keystoneclient.middleware.auth_token' <===
importutils.import_module(auth_token_module) <===
return cfg.CONF.keystone_authtoken.auth_uri <===
def filter_factory(global_conf, **local_conf):
conf = global_conf.copy()
conf.update(local_conf)
def auth_url_filter(app):
return AuthUrlFilter(app, conf) return auth_url_filter
4)
authtoken filter
/usr/lib/python2.7/dist-packages/keystoneclient/middleware/auth_token.py
"""
TOKEN-BASED AUTH MIDDLEWARE
This WSGI component:
* Verifies that incoming client requests have valid tokens by validating
tokens with the auth service.
* Rejects unauthenticated requests
* SM:Please read the doc string of above "auth_token.py" file.
Refer to: http://docs.openstack.org/developer/python-keystoneclient/
middlewarearchitecture.html
"""
####SM:Read conf from /etc/heat/heat.conf file
opts = [
cfg.StrOpt('auth_admin_prefix',
default='',
help='Prefix to prepend at the beginning of the path'),
cfg.StrOpt('auth_host',
default='127.0.0.1',
help='Host providing the admin Identity API endpoint'),
cfg.IntOpt('auth_port',
default=35357,
help='Port of the admin Identity API endpoint'),
cfg.StrOpt('auth_protocol',
default='https',
help='Protocol of the admin Identity API endpoint'
'(http or https)'),
cfg.StrOpt('auth_uri',
default=None,
help='Complete public Identity API endpoint'),
]
####SM:Read
CONF = cfg.CONF
CONF.register_opts(opts, group='keystone_authtoken')
def filter_factory(global_conf, **local_conf):
"""Returns a WSGI filter app for use with paste.deploy."""
conf = global_conf.copy()
conf.update(local_conf)
def auth_filter(app):
return AuthProtocol(app, conf) return auth_filter
class AuthProtocol(object):
"""Auth Middleware that handles authenticating client calls."""
def __init__(self, app, conf):
self.LOG = logging.getLogger(conf.get('log_name', __name__))
self.LOG.info('Starting keystone auth_token middleware')
self.conf = conf
self.app = app
auth_host = self._conf_get('auth_host')
auth_port = int(self._conf_get('auth_port'))
self.auth_uri = self._conf_get('auth_uri')
self.admin_token = self._conf_get('admin_token')
def _conf_get(self, name):
# try config from paste-deploy first
if name in self.conf:
return self.conf[name]
else:
return CONF.keystone_authtoken[name]
def __call__(self, env, start_response): <===IMP
"""Handle incoming request.
Authenticate send downstream on success. Reject request if
we can't authenticate.
"""
self._remove_auth_headers(env)
user_token = self._get_user_token_from_header(env) <===
token_info = self._validate_user_token(user_token, env) <===
env['keystone.token_info'] = token_info
user_headers = self._build_user_headers(token_info)
self._add_headers(env, user_headers)
return self.app(env, start_response) <===
def _get_user_token_from_header(self, env):
"""Get token id from request.
:param env: wsgi request environment
:return token id
:raises InvalidUserToken if no token is provided in request
"""
token = self._get_header(env, 'X-Auth-Token',
self._get_header(env, 'X-Storage-Token'))
if token:
return token
def _validate_user_token(self, user_token, env, retry=True):
"""Authenticate user using PKI
:param user_token: user's token id
:param retry: Ignored, as it is not longer relevant
:return uncrypted body of the token if the token is valid
:raise InvalidUserToken if token is rejected
:no longer raises ServiceError since it no longer makes RPC
"""
token_id = cms.cms_hash_token(user_token)
cached = self._cache_get(token_id)
if cached:
return cached
if cms.is_ans1_token(user_token):
verified = self.verify_signed_token(user_token) <===
data = jsonutils.loads(verified)
else:
data = self.verify_uuid_token(user_token, retry) <===
expires = confirm_token_not_expired(data) <===
self._confirm_token_bind(data, env) <===
self._cache_put(token_id, data, expires)
return data
def verify_uuid_token(self, user_token, retry=True):
"""Authenticate user token with keystone.
:param user_token: user's token id
:param retry: flag that forces the middleware to retry
user authentication when an indeterminate
response is received. Optional.
:return: token object received from keystone on success
:raise InvalidUserToken: if token is rejected
:raise ServiceError: if unable to authenticate token
"""
# Determine the highest api version we can use.
if not self.auth_version:
self.auth_version = self._choose_api_version()
if self.auth_version == 'v3.0':
headers = {'X-Auth-Token': self.get_admin_token(),
'X-Subject-Token': safe_quote(user_token)}
path = '/v3/auth/tokens'
if not self.include_service_catalog:
# NOTE(gyee): only v3 API support this option
path = path + '?nocatalog'
response, data = self._json_request(
'GET',
path,
additional_headers=headers)
else:
headers = {'X-Auth-Token': self.get_admin_token()}
response, data = self._json_request(
'GET',
'/v2.0/tokens/%s' % safe_quote(user_token),
additional_headers=headers)
if response.status_code == 200:
return data
if response.status_code == 404:
self.LOG.warn("Authorization failed for token")
raise InvalidUserToken('Token authorization failed')
5)
apiv1app filter
5a)paste.app_factory = heat.common.wsgi:app_factory
/usr/lib/python2.7/dist-packages/heat/common/wsgi.py
class AppFactory(BasePasteFactory):
"""A Generic paste.deploy app factory.
This requires heat.app_factory to be set to a callable which returns a
WSGI app when invoked. The format of the name is: e.g.
[app:apiv1app]
paste.app_factory = heat.common.wsgi:app_factory
heat.app_factory = heat.api.cfn.v1:API
The WSGI app constructor must accept a ConfigOpts object and a local config
dict as its two arguments.
"""
KEY = 'heat.app_factory'
def __call__(self, global_conf, **local_conf):
"""The actual paste.app_factory protocol method."""
factory = self._import_factory(local_conf)
return factory(self.conf, **local_conf)
5b)
heat.app_factory = heat.api.openstack.v1:API
/usr/lib/python2.7/dist-packages/heat/api/openstack/v1/__init__.py
class API(wsgi.Router):
"""
WSGI router for Heat v1 ReST API requests.
"""
def __init__(self, conf, **local_conf):
self.conf = conf
mapper = routes.Mapper()
# Stacks
stacks_resource = stacks.create_resource(conf)
with mapper.submapper(controller=stacks_resource,
path_prefix="/{tenant_id}") as stack_mapper:
# Template handling
stack_mapper.connect("template_validate",
"/validate",
action="validate_template",
conditions={'method': 'POST'})
stack_mapper.connect("resource_types",
"/resource_types",
action="list_resource_types",
conditions={'method': 'GET'})
stack_mapper.connect("resource_schema",
"/resource_types/{type_name}",
action="resource_schema",
conditions={'method': 'GET'})
stack_mapper.connect("generate_template",
"/resource_types/{type_name}/template",
action="generate_template",
conditions={'method': 'GET'})
---- blabla ----
super(API, self).__init__(mapper)
http://fosshelp.blogspot.in/2014/03/how-to-openstack-api-and-wsgi-api.html
2)
vim /etc/heat/api-paste.ini
# heat-api pipeline
[pipeline:heat-api]
pipeline = faultwrap ssl versionnegotiation authurl authtoken context apiv1app
[filter:faultwrap]
paste.filter_factory = heat.common.wsgi:filter_factory
heat.filter_factory = heat.api.openstack:faultwrap_filter
[filter:ssl]
paste.filter_factory = heat.common.wsgi:filter_factory
heat.filter_factory = heat.api.openstack:sslmiddleware_filter
[filter:versionnegotiation]
paste.filter_factory = heat.common.wsgi:filter_factory
heat.filter_factory = heat.api.openstack:version_negotiation_filter
# Middleware to set auth_url header appropriately
[filter:authurl]
paste.filter_factory = heat.common.auth_url:filter_factory
# Auth middleware that validates token against keystone
[filter:authtoken]
paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory <===
[filter:context]
paste.filter_factory = heat.common.context:ContextMiddleware_filter_factory
[app:apiv1app]
paste.app_factory = heat.common.wsgi:app_factory
heat.app_factory = heat.api.openstack.v1:API
3)
authurl filter
/usr/lib/python2.7/dist-packages/heat/common/auth_url.py
from oslo.config import cfg
from webob.exc import HTTPBadRequest
from webob.exc import HTTPUnauthorized
from heat.common import wsgi
from heat.openstack.common import importutils
class AuthUrlFilter(wsgi.Middleware):
def __init__(self, app, conf):
super(AuthUrlFilter, self).__init__(app)
self.conf = conf
self.auth_url = self._get_auth_url() <===
def _get_auth_url(self):
if 'auth_uri' in self.conf:
return self.conf['auth_uri']
else:
# Import auth_token to have keystone_authtoken settings setup.
auth_token_module = 'keystoneclient.middleware.auth_token' <===
importutils.import_module(auth_token_module) <===
return cfg.CONF.keystone_authtoken.auth_uri <===
def filter_factory(global_conf, **local_conf):
conf = global_conf.copy()
conf.update(local_conf)
def auth_url_filter(app):
return AuthUrlFilter(app, conf) return auth_url_filter
4)
authtoken filter
/usr/lib/python2.7/dist-packages/keystoneclient/middleware/auth_token.py
"""
TOKEN-BASED AUTH MIDDLEWARE
This WSGI component:
* Verifies that incoming client requests have valid tokens by validating
tokens with the auth service.
* Rejects unauthenticated requests
* SM:Please read the doc string of above "auth_token.py" file.
Refer to: http://docs.openstack.org/developer/python-keystoneclient/
middlewarearchitecture.html
"""
####SM:Read conf from /etc/heat/heat.conf file
opts = [
cfg.StrOpt('auth_admin_prefix',
default='',
help='Prefix to prepend at the beginning of the path'),
cfg.StrOpt('auth_host',
default='127.0.0.1',
help='Host providing the admin Identity API endpoint'),
cfg.IntOpt('auth_port',
default=35357,
help='Port of the admin Identity API endpoint'),
cfg.StrOpt('auth_protocol',
default='https',
help='Protocol of the admin Identity API endpoint'
'(http or https)'),
cfg.StrOpt('auth_uri',
default=None,
help='Complete public Identity API endpoint'),
]
####SM:Read
CONF = cfg.CONF
CONF.register_opts(opts, group='keystone_authtoken')
def filter_factory(global_conf, **local_conf):
"""Returns a WSGI filter app for use with paste.deploy."""
conf = global_conf.copy()
conf.update(local_conf)
def auth_filter(app):
return AuthProtocol(app, conf) return auth_filter
class AuthProtocol(object):
"""Auth Middleware that handles authenticating client calls."""
def __init__(self, app, conf):
self.LOG = logging.getLogger(conf.get('log_name', __name__))
self.LOG.info('Starting keystone auth_token middleware')
self.conf = conf
self.app = app
auth_host = self._conf_get('auth_host')
auth_port = int(self._conf_get('auth_port'))
self.auth_uri = self._conf_get('auth_uri')
self.admin_token = self._conf_get('admin_token')
def _conf_get(self, name):
# try config from paste-deploy first
if name in self.conf:
return self.conf[name]
else:
return CONF.keystone_authtoken[name]
def __call__(self, env, start_response): <===IMP
"""Handle incoming request.
Authenticate send downstream on success. Reject request if
we can't authenticate.
"""
self._remove_auth_headers(env)
user_token = self._get_user_token_from_header(env) <===
token_info = self._validate_user_token(user_token, env) <===
env['keystone.token_info'] = token_info
user_headers = self._build_user_headers(token_info)
self._add_headers(env, user_headers)
return self.app(env, start_response) <===
def _get_user_token_from_header(self, env):
"""Get token id from request.
:param env: wsgi request environment
:return token id
:raises InvalidUserToken if no token is provided in request
"""
token = self._get_header(env, 'X-Auth-Token',
self._get_header(env, 'X-Storage-Token'))
if token:
return token
def _validate_user_token(self, user_token, env, retry=True):
"""Authenticate user using PKI
:param user_token: user's token id
:param retry: Ignored, as it is not longer relevant
:return uncrypted body of the token if the token is valid
:raise InvalidUserToken if token is rejected
:no longer raises ServiceError since it no longer makes RPC
"""
token_id = cms.cms_hash_token(user_token)
cached = self._cache_get(token_id)
if cached:
return cached
if cms.is_ans1_token(user_token):
verified = self.verify_signed_token(user_token) <===
data = jsonutils.loads(verified)
else:
data = self.verify_uuid_token(user_token, retry) <===
expires = confirm_token_not_expired(data) <===
self._confirm_token_bind(data, env) <===
self._cache_put(token_id, data, expires)
return data
def verify_uuid_token(self, user_token, retry=True):
"""Authenticate user token with keystone.
:param user_token: user's token id
:param retry: flag that forces the middleware to retry
user authentication when an indeterminate
response is received. Optional.
:return: token object received from keystone on success
:raise InvalidUserToken: if token is rejected
:raise ServiceError: if unable to authenticate token
"""
# Determine the highest api version we can use.
if not self.auth_version:
self.auth_version = self._choose_api_version()
if self.auth_version == 'v3.0':
headers = {'X-Auth-Token': self.get_admin_token(),
'X-Subject-Token': safe_quote(user_token)}
path = '/v3/auth/tokens'
if not self.include_service_catalog:
# NOTE(gyee): only v3 API support this option
path = path + '?nocatalog'
response, data = self._json_request(
'GET',
path,
additional_headers=headers)
else:
headers = {'X-Auth-Token': self.get_admin_token()}
response, data = self._json_request(
'GET',
'/v2.0/tokens/%s' % safe_quote(user_token),
additional_headers=headers)
if response.status_code == 200:
return data
if response.status_code == 404:
self.LOG.warn("Authorization failed for token")
raise InvalidUserToken('Token authorization failed')
5)
apiv1app filter
5a)paste.app_factory = heat.common.wsgi:app_factory
/usr/lib/python2.7/dist-packages/heat/common/wsgi.py
class AppFactory(BasePasteFactory):
"""A Generic paste.deploy app factory.
This requires heat.app_factory to be set to a callable which returns a
WSGI app when invoked. The format of the name is
[app:apiv1app]
paste.app_factory = heat.common.wsgi:app_factory
heat.app_factory = heat.api.cfn.v1:API
The WSGI app constructor must accept a ConfigOpts object and a local config
dict as its two arguments.
"""
KEY = 'heat.app_factory'
def __call__(self, global_conf, **local_conf):
"""The actual paste.app_factory protocol method."""
factory = self._import_factory(local_conf)
return factory(self.conf, **local_conf)
5b)
heat.app_factory = heat.api.openstack.v1:API
/usr/lib/python2.7/dist-packages/heat/api/openstack/v1/__init__.py
class API(wsgi.Router):
"""
WSGI router for Heat v1 ReST API requests.
"""
def __init__(self, conf, **local_conf):
self.conf = conf
mapper = routes.Mapper()
# Stacks
stacks_resource = stacks.create_resource(conf)
with mapper.submapper(controller=stacks_resource,
path_prefix="/{tenant_id}") as stack_mapper:
# Template handling
stack_mapper.connect("template_validate",
"/validate",
action="validate_template",
conditions={'method': 'POST'})
stack_mapper.connect("resource_types",
"/resource_types",
action="list_resource_types",
conditions={'method': 'GET'})
stack_mapper.connect("resource_schema",
"/resource_types/{type_name}",
action="resource_schema",
conditions={'method': 'GET'})
stack_mapper.connect("generate_template",
"/resource_types/{type_name}/template",
action="generate_template",
conditions={'method': 'GET'})
---- blabla ----
super(API, self).__init__(mapper)
No comments:
Post a Comment