Merge "supprt keycloak first stage"

This commit is contained in:
Jenkins 2017-07-17 10:00:33 +00:00 committed by Gerrit Code Review
commit d3a992bafc
6 changed files with 136 additions and 9 deletions

View File

@ -10,6 +10,12 @@ use = egg:Paste#urlmap
/v1 = vitragev1_keystone_pipeline
/healthcheck = healthcheck
[composite:vitrage+keycloak]
use = egg:Paste#urlmap
/ = vitrageversions_pipeline
/v1 = vitragev1_keycloak_pipeline
/healthcheck = healthcheck
[app:healthcheck]
use = egg:oslo.middleware#healthcheck
oslo_config_project = vitrage
@ -21,20 +27,27 @@ pipeline = cors http_proxy_to_wsgi vitrageversions
paste.app_factory = vitrage.api.app:app_factory
root = vitrage.api.controllers.root.VersionsController
[pipeline:vitragev1_keystone_pipeline]
pipeline = cors http_proxy_to_wsgi request_id osprofiler authtoken vitragev1
[pipeline:vitragev1_noauth_pipeline]
pipeline = cors http_proxy_to_wsgi request_id osprofiler vitragev1
[pipeline:vitragev1_keystone_pipeline]
pipeline = cors http_proxy_to_wsgi request_id osprofiler keystoneauthtoken vitragev1
[pipeline:vitragev1_keycloak_pipeline]
pipeline = cors http_proxy_to_wsgi request_id osprofiler keycloakauthtoken vitragev1
[app:vitragev1]
paste.app_factory = vitrage.api.app:app_factory
root = vitrage.api.controllers.v1.root.V1Controller
[filter:authtoken]
[filter:keystoneauthtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory
oslo_config_project = vitrage
[filter:keycloakauthtoken]
paste.filter_factory = vitrage.middleware.keycloak:filter_factory
oslo_config_project = vitrage
[filter:request_id]
paste.filter_factory = oslo_middleware:RequestId.factory

View File

@ -14,7 +14,7 @@ python-novaclient>=9.0.0 # Apache-2.0
python-heatclient>=1.6.1 # Apache-2.0
pyzabbix>=0.7.4 # LGPL
networkx>=1.10 # BSD
oslo.config>=4.0.0 # Apache-2.0
oslo.config!=4.3.0,!=4.4.0,>=4.0.0 # Apache-2.0
oslo.messaging!=5.25.0,>=5.24.2 # Apache-2.0
oslo.log>=3.22.0 # Apache-2.0
oslo.policy>=1.23.0 # Apache-2.0
@ -28,5 +28,6 @@ stevedore>=1.20.0 # Apache-2.0
voluptuous>=0.8.9 # BSD License
sympy>=0.7.6 # BSD
pysnmp>=4.2.3 # BSD
PyJWT>=1.0.1 # MIT
osprofiler>=1.4.0 # Apache-2.0

View File

@ -17,7 +17,7 @@ pyzabbix>=0.7.4 # LGPL
oslo.log>=3.22.0 # Apache-2.0
oslotest>=1.10.0 # Apache-2.0
oslo.service>=1.10.0 # Apache-2.0
oslo.config>=4.0.0 # Apache-2.0
oslo.config!=4.3.0,!=4.4.0,>=4.0.0 # Apache-2.0
oslo.messaging!=5.25.0,>=5.24.2 # Apache-2.0
oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0
oslo.policy>=1.23.0 # Apache-2.0

View File

@ -25,8 +25,8 @@ OPTS = [
help='Configuration file for WSGI definition of API.'),
cfg.IntOpt('workers', default=1, min=1,
help='Number of workers for vitrage API server.'),
cfg.BoolOpt('pecan_debug', default=False,
help='Toggle Pecan Debug Middleware.'),
cfg.StrOpt('auth_mode', default='keystone', choices={'keystone', 'noauth'},
cfg.StrOpt('auth_mode', default='keystone', choices={'keystone',
'noauth',
'keycloak'},
help='Authentication mode to use.'),
]

View File

@ -0,0 +1,15 @@
# Copyright 2017 - Nokia
#
# 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.
__author__ = 'stack'

View File

@ -0,0 +1,98 @@
# Copyright 2017 - Nokia
#
# 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.
import jwt
import requests
from oslo_config import cfg
from oslo_middleware import base
from oslo_serialization import jsonutils
from webob import exc
OPENID_CONNECT_USERINFO = '%s/realms/%s/protocol/openid-connect/userinfo'
KEYCLOAK_OPTS = [
cfg.StrOpt('auth_url', default='127.0.0.1',
help='Keycloak authentication server ip',),
cfg.StrOpt('insecure', default=False,
help='If True, SSL/TLS certificate verification is disabled'),
]
class KeycloakAuth(base.ConfigurableMiddleware):
def __init__(self, application, conf=None):
super(KeycloakAuth, self).__init__(application, conf)
self.oslo_conf.register_opts(KEYCLOAK_OPTS, 'keycloak')
self.auth_url = self._conf_get('auth_url', 'keycloak')
self.insecure = self._conf_get('insecure', 'keycloak')
@property
def reject_auth_headers(self):
header_val = 'Keycloak uri=\'%s\'' % self.auth_url
return [('WWW-Authenticate', header_val)]
@property
def roles(self):
try:
decoded = jwt.decode(self.token, algorithms=['RS256'],
verify=False)
except jwt.DecodeError:
message = "Token can't be decoded because of wrong format."
self._unauthorized(message)
return ','.join(decoded['realm_access']['roles']) \
if 'realm_access' in decoded else ''
def process_request(self, req):
self._authenticate(req)
def _authenticate(self, req):
self.token = req.headers.get('X-Auth-Token')
if self.token:
self._decode(req)
else:
message = 'Auth token must be provided in "X-Auth-Token" header.'
self._unauthorized(message)
def _decode(self, req):
realm_name = req.headers.get('X-Project-Id')
endpoint = OPENID_CONNECT_USERINFO % (self.auth_url, realm_name)
headers = {'Authorization": "Bearer %s' % self.token}
resp = requests.get(endpoint, headers=headers,
verify=not self.insecure)
resp.raise_for_status()
self._set_req_headers(req)
def _set_req_headers(self, req):
req.headers['X-Identity-Status'] = 'Confirmed'
req.headers['X-Roles'] = self.roles
def _unauthorized(self, message):
body = {'error': {
'code': 401,
'title': 'Unauthorized',
'message': message,
}}
raise exc.HTTPUnauthorized(body=jsonutils.dumps(body),
headers=self.reject_auth_headers,
content_type='application/json')
filter_factory = KeycloakAuth.factory