#!/usr/bin/env python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (c) 2010 OpenStack, LLC.
#
# 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.


"""
Auth Middleware that handles auth for a service

This module can be installed as a filter in front of your service to validate
that requests are coming from a trusted component that has handled
authenticating the call. If a call comes from an untrusted source, it will
redirect it back to be properly authenticated. This is done by sending our a
305 proxy redirect response with the URL for the auth service.

The auth service settings are specified in the INI file (keystone.ini). The ini
file is passed in as the WSGI config file when starting the service. For this
proof of concept, the ini file is in echo/echo/echo.ini.

In the current implementation use a basic auth password to verify that the
request is coming from a valid auth component or service

Refer to: http://wiki.openstack.org/openstack-authn


HEADERS
-------

* HTTP\_ is a standard http header
* HTTP_X is an extended http header

Coming in from initial call
^^^^^^^^^^^^^^^^^^^^^^^^^^^

HTTP_X_AUTH_TOKEN
    the client token being passed in

HTTP_X_STORAGE_TOKEN
    the client token being passed in (legacy Rackspace use) to support
    cloud files

Used for communication between components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

www-authenticate
    only used if this component is being used remotely

HTTP_AUTHORIZATION
    basic auth password used to validate the connection

What we add to the request for use by the OpenStack service
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

HTTP_X_AUTHORIZATION
    the client identity being passed in


"""
import logging
from webob.exc import HTTPUseProxy, HTTPUnauthorized

PROTOCOL_NAME = "Remote Proxy Authentication"
logger = logging.getLogger(__name__)  # pylint: disable=C0103


class RemoteAuth(object):

    # app is the downstream WSGI component, usually the OpenStack service
    #
    # if app is not provided, the assumption is this filter is being run
    # from a separate server.

    def __init__(self, app, conf):
        logger.info("Starting the %s component", PROTOCOL_NAME)
        # app is the next app in WSGI chain - eventually the OpenStack service
        self.app = app
        self.conf = conf
        # where to redirect untrusted requests to
        self.proxy_location = conf.get('proxy_location')
        # secret that will tell us a request is coming from a trusted auth
        # component
        self.remote_auth_pass = conf.get('remote_auth_pass')

    def __call__(self, env, start_response):
        # Validate the request is trusted
        # Authenticate the Auth component itself.
        headers = [('www-authenticate', 'Basic realm="API Auth"')]
        if 'HTTP_AUTHORIZATION' not in env:
            logger.debug("Unauthenticated: redirecting to %s" %
                         self.proxy_location)
            # Redirect to proxy (auth component) and show that basic auth is
            # required
            return HTTPUseProxy(location=self.proxy_location,
                        headers=headers)(env, start_response)
        else:
            auth_type, encoded_creds = env['HTTP_AUTHORIZATION'].split(None, 1)
            if encoded_creds != self.remote_auth_pass:
                logger.warn('Bad proxy credentials received')
                return HTTPUnauthorized(headers=headers)(env, start_response)

        # Make sure that the user has been authenticated by the Auth Service
        if 'HTTP_X_AUTHORIZATION' not in env:
            return HTTPUnauthorized()(env, start_response)

        return self.app(env, start_response)


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 RemoteAuth(app, conf)
    return auth_filter
