131 lines
4.0 KiB
Python
131 lines
4.0 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright 2014 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.
|
|
|
|
import Cookie
|
|
import re
|
|
|
|
import six
|
|
|
|
from nailgun.api.v1 import urls as api_urls
|
|
from nailgun.fake_keystone import validate_token
|
|
from nailgun.settings import settings
|
|
|
|
from keystonemiddleware import auth_token
|
|
|
|
|
|
def public_urls():
|
|
urls = {}
|
|
for url, methods in six.iteritems(api_urls.public_urls()):
|
|
urls['{0}{1}'.format('/api/v1', url)] = methods
|
|
urls['{0}{1}'.format('/api', url)] = methods
|
|
urls["/$"] = ['GET']
|
|
urls["/static"] = ['GET']
|
|
urls["/keystone"] = ['GET', 'POST']
|
|
return urls
|
|
|
|
|
|
class CookieTokenMixin(object):
|
|
"""Mixin for getting the auth token out of request
|
|
|
|
Token is taken from X-Auth-Token header or,
|
|
for a particular set of urls, from a cookie.
|
|
"""
|
|
|
|
cookie_routes_regexs = [re.compile(r) for r in api_urls.cookie_urls()]
|
|
|
|
def get_auth_token(self, env):
|
|
token = env.get('HTTP_X_AUTH_TOKEN', '')
|
|
if token:
|
|
return token
|
|
|
|
path = env.get('PATH_INFO', '/')
|
|
if any(r.match(path) for r in self.cookie_routes_regexs):
|
|
c = Cookie.SimpleCookie(env.get('HTTP_COOKIE', ''))
|
|
token = c.get('token')
|
|
if token:
|
|
return token.value
|
|
|
|
return ''
|
|
|
|
|
|
class SkipAuthMixin(object):
|
|
"""Skips verification of authentication tokens for public routes in API."""
|
|
def __init__(self, app):
|
|
self.public_api_routes = {}
|
|
self.app = app
|
|
try:
|
|
for route_tpl, methods in six.iteritems(public_urls()):
|
|
self.public_api_routes[re.compile(route_tpl)] = methods
|
|
except re.error as e:
|
|
msg = 'Cannot compile public API routes: {0}'.format(e)
|
|
raise Exception(msg)
|
|
|
|
super(SkipAuthMixin, self).__init__(app, settings.AUTH)
|
|
|
|
def __call__(self, env, start_response):
|
|
path = env.get('PATH_INFO', '/')
|
|
method = env.get('REQUEST_METHOD')
|
|
|
|
# The information whether the API call is being performed against the
|
|
# public API may be useful. Saving it to the
|
|
# WSGI environment is reasonable thereby.
|
|
env['is_public_api'] = False
|
|
for pattern, methods in six.iteritems(self.public_api_routes):
|
|
if re.match(pattern, path):
|
|
if method in methods:
|
|
env['is_public_api'] = True
|
|
break
|
|
|
|
if env['is_public_api']:
|
|
return self.app(env, start_response)
|
|
return super(SkipAuthMixin, self).__call__(env, start_response)
|
|
|
|
|
|
class FakeAuthProtocol(CookieTokenMixin):
|
|
"""Auth protocol for fake mode."""
|
|
def __init__(self, app, conf):
|
|
self.app = app
|
|
|
|
def __call__(self, env, start_response):
|
|
if validate_token(self.get_auth_token(env)):
|
|
env['HTTP_X_ROLES'] = settings.FAKE_KEYSTONE_ROLE
|
|
return self.app(env, start_response)
|
|
else:
|
|
start_response('401 Unauthorized', [])
|
|
return ['']
|
|
|
|
|
|
class NailgunKeystoneAuthMiddleware(
|
|
CookieTokenMixin,
|
|
SkipAuthMixin,
|
|
auth_token.AuthProtocol):
|
|
"""Auth middleware for keystone."""
|
|
def __call__(self, env, start_response):
|
|
token = self.get_auth_token(env)
|
|
|
|
if token:
|
|
env['HTTP_X_AUTH_TOKEN'] = token
|
|
|
|
return super(
|
|
NailgunKeystoneAuthMiddleware,
|
|
self
|
|
).__call__(env, start_response)
|
|
|
|
|
|
class NailgunFakeKeystoneAuthMiddleware(SkipAuthMixin, FakeAuthProtocol):
|
|
"""Auth middleware for fake mode."""
|
|
pass
|