Split webhook middleware from webhooks module

Change-Id: I3c7ff59ea1d22fd9b5ee6e4d96b5a56f1b26e58c
This commit is contained in:
yanyanhu 2015-04-13 22:51:26 -04:00
parent 69e39f6b8b
commit f4ae261d89
4 changed files with 112 additions and 96 deletions

View File

@ -1,7 +1,7 @@
# senlin-api pipeline
[pipeline:senlin-api]
pipeline = request_id faultwrap ssl versionnegotiation authtoken context trust apiv1app
pipeline = request_id faultwrap ssl versionnegotiation webhook authtoken context trust apiv1app
[app:apiv1app]
paste.app_factory = senlin.common.wsgi:app_factory
@ -31,6 +31,10 @@ senlin.filter_factory = senlin.api.openstack:version_negotiation_filter
paste.filter_factory = senlin.common.wsgi:filter_factory
senlin.filter_factory = senlin.api.openstack:trustmiddleware_filter
[filter:webhook]
paste.filter_factory = senlin.common.wsgi:filter_factory
senlin.filter_factory = senlin.api.openstack:webhookmiddleware_filter
# Auth middleware that validates token against keystone
[filter:authtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory

View File

@ -0,0 +1,102 @@
# 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 oslo_log import log as logging
from senlin.common import context
from senlin.common import exception
from senlin.common.i18n import _
from senlin.common import utils
from senlin.common import wsgi
from senlin.drivers.openstack import sdk
from senlin.webhooks import webhooks
LOG = logging.getLogger(__name__)
class WebhookMiddleware(wsgi.Middleware):
'''Middleware to do authentication for webhook triggering
This middleware gets authentication for request to a webhook
based on information embedded inside url and then rebuild the
request header.
'''
def process_request(self, req):
self._authenticate(req)
def _authenticate(self, req):
LOG.debug("Checking credentials of webhook request")
credential = self._get_credential(req)
if not credential:
return
# Get a valid token based on credential
# and fill into the request header
token_id = self._get_token(credential)
req.headers['X-Auth-Token'] = token_id
def _get_credential(self, req):
try:
url_bottom = req.url.rsplit('webhooks')[1]
webhook_id = url_bottom.rsplit('/')[1]
trigger = url_bottom.rsplit('/')[2].startswith('trigger')
if trigger is not True or 'key' not in req.params:
raise Exception()
except Exception:
LOG.debug(_("%(url)s is not a webhook trigger url,"
" pass."), {'url': req.url})
return
if req.method != 'POST':
LOG.debug(_("Not a post request to webhook trigger url"
" %(url)s, pass."), {'url': req.url})
return
# This is a webhook triggering, we need to fill in valid
# credential info into the http headers to ensure this
# request can pass keystone auth_token validation.
#
# Get the credential stored in DB based on webhook ID.
# TODO(Anyone): Use Barbican to store these credential.
LOG.debug(_("Get credential of webhook %(id)s"), webhook_id)
senlin_context = context.RequestContext.get_service_context()
webhook_obj = webhooks.Webhook.load(senlin_context, webhook_id)
credential = webhook_obj.credential
credential['webhook_id'] = webhook_id
# Decrypt the credential password with key embedded in req params
try:
password = utils.decrypt(credential['password'],
req.params['key'])
credential['password'] = password
except Exception:
msg = 'Invalid key for webhook(%s) credential decryption' % \
webhook_id
LOG.error(msg)
raise exception.SenlinBadRequest(msg=msg)
return credential
def _get_token(self, credential):
'''Get a valid token based on credential'''
try:
access_info = sdk.authenticate(**credential)
token_id = access_info.auth_token
except Exception as ex:
msg = 'Webhook get token failed: %s' % ex.message
LOG.error(msg)
raise exception.WebhookCredentialInvalid(
webhook=credential['webhook_id'])
# Get token successfully!
return token_id

View File

@ -16,6 +16,7 @@ from senlin.api.middleware import fault
from senlin.api.middleware import ssl
from senlin.api.middleware import trust
from senlin.api.middleware import version_negotiation as vn
from senlin.api.middleware import webhook
from senlin.api.openstack import versions
@ -38,3 +39,7 @@ def contextmiddleware_filter(app, conf, **local_conf):
def trustmiddleware_filter(app, conf, **local_conf):
return trust.TrustMiddleware(app)
def webhookmiddleware_filter(app, conf, **local_conf):
return webhook.WebhookMiddleware(app)

View File

@ -15,15 +15,11 @@ import datetime
from oslo_config import cfg
from oslo_log import log as logging
from senlin.common import context
from senlin.common import exception
from senlin.common.i18n import _
from senlin.common.i18n import _LE
from senlin.common.i18n import _LI
from senlin.common import utils
from senlin.common import wsgi
from senlin.db import api as db_api
from senlin.drivers.openstack import sdk
LOG = logging.getLogger(__name__)
@ -180,94 +176,3 @@ class Webhook(object):
@classmethod
def delete(cls, context, webhook_id):
db_api.webhook_delete(context, webhook_id)
class WebhookMiddleware(wsgi.Middleware):
'''Middleware to do authentication for webhook triggering
This middleware gets authentication for request to a webhook
based on information embedded inside url and then rebuild the
request header.
'''
def process_request(self, req):
self._authenticate(req)
def _authenticate(self, req):
LOG.debug("Checking credentials of webhook request")
credential = self._get_credential(req)
if not credential:
return
# Get a valid token based on credential
# and fill into the request header
token_id = self._get_token(credential)
req.headers['X-Auth-Token'] = token_id
def _get_credential(self, req):
try:
url_bottom = req.url.rsplit('webhooks')[1]
webhook_id = url_bottom.rsplit('/')[1]
trigger = url_bottom.rsplit('/')[2].startswith('trigger')
if trigger is not True or 'key' not in req.params:
raise Exception()
except Exception:
LOG.debug(_("%(url)s is not a webhook trigger url,"
" pass."), {'url': req.url})
return
if req.method != 'POST':
LOG.debug(_("Not a post request to webhook trigger url"
" %(url)s, pass."), {'url': req.url})
return
# This is a webhook triggering, we need to fill in valid
# credential info into the http headers to ensure this
# request can pass keystone auth_token validation.
#
# Get the credential stored in DB based on webhook ID.
# TODO(Anyone): Use Barbican to store these credential.
LOG.debug(_("Get credential of webhook %(id)s"), webhook_id)
senlin_context = context.RequestContext.get_service_context()
webhook_obj = Webhook.load(senlin_context, webhook_id)
credential = webhook_obj.credential
credential['webhook_id'] = webhook_id
# Decrypt the credential password with key embedded in req params
try:
password = utils.decrypt(credential['password'],
req.params['key'])
credential['password'] = password
except Exception:
msg = 'Invalid key for webhook(%s) credential decryption' % \
webhook_id
LOG.error(msg)
raise exception.SenlinBadRequest(msg=msg)
return credential
def _get_token(self, credential):
'''Get a valid token based on credential'''
try:
access_info = sdk.authenticate(**credential)
token_id = access_info.auth_token
except Exception as ex:
msg = 'Webhook get token failed: %s' % ex.message
LOG.error(msg)
raise exception.WebhookCredentialInvalid(
webhook=credential['webhook_id'])
# Get token successfully!
return token_id
def WebhookMiddleware_filter_factory(global_conf, **local_conf):
'''Factory method for paste.deploy.'''
conf = global_conf.copy()
conf.update(local_conf)
def filter(app):
return WebhookMiddleware(app)
return filter