# Copyright (c) 2011 OpenStack Foundation
#
#    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.
"""
Common Auth Middleware.

"""

from oslo_log import log as logging
from oslo_log import versionutils
from oslo_serialization import jsonutils
import webob.dec
import webob.exc

from nova.api import wsgi
import nova.conf
from nova import context
from nova.i18n import _


CONF = nova.conf.CONF
LOG = logging.getLogger(__name__)


def _load_pipeline(loader, pipeline):
    filters = [loader.get_filter(n) for n in pipeline[:-1]]
    app = loader.get_app(pipeline[-1])
    filters.reverse()
    for filter in filters:
        app = filter(app)
    return app


def pipeline_factory(loader, global_conf, **local_conf):
    """A paste pipeline replica that keys off of auth_strategy."""
    versionutils.report_deprecated_feature(
        LOG,
        "The legacy V2 API code tree has been removed in Newton. "
        "Please remove legacy v2 API entry from api-paste.ini, and use "
        "V2.1 API or V2.1 API compat mode instead"
    )


def pipeline_factory_v21(loader, global_conf, **local_conf):
    """A paste pipeline replica that keys off of auth_strategy."""
    auth_strategy = CONF.api.auth_strategy
    if auth_strategy == 'noauth2':
        versionutils.report_deprecated_feature(
            LOG,
            "'[api]auth_strategy=noauth2' is deprecated as of the 21.0.0 "
            "Ussuri release and will be removed in a future release. Please "
            "remove any 'noauth2' entries from api-paste.ini; only the "
            "'keystone' pipeline is supported."
        )
    return _load_pipeline(loader, local_conf[auth_strategy].split())


class InjectContext(wsgi.Middleware):
    """Add a 'nova.context' to WSGI environ."""

    def __init__(self, context, *args, **kwargs):
        self.context = context
        super(InjectContext, self).__init__(*args, **kwargs)

    @webob.dec.wsgify(RequestClass=wsgi.Request)
    def __call__(self, req):
        req.environ['nova.context'] = self.context
        return self.application


class NovaKeystoneContext(wsgi.Middleware):
    """Make a request context from keystone headers."""

    @staticmethod
    def _create_context(env, **kwargs):
        """Create a context from a request environ.

        This exists to make test stubbing easier.
        """
        return context.RequestContext.from_environ(env, **kwargs)

    @webob.dec.wsgify(RequestClass=wsgi.Request)
    def __call__(self, req):
        # Build a context, including the auth_token...
        remote_address = req.remote_addr
        if CONF.api.use_forwarded_for:
            remote_address = req.headers.get('X-Forwarded-For', remote_address)

        service_catalog = None
        if req.headers.get('X_SERVICE_CATALOG') is not None:
            try:
                catalog_header = req.headers.get('X_SERVICE_CATALOG')
                service_catalog = jsonutils.loads(catalog_header)
            except ValueError:
                raise webob.exc.HTTPInternalServerError(
                          _('Invalid service catalog json.'))

        # NOTE(jamielennox): This is a full auth plugin set by auth_token
        # middleware in newer versions.
        user_auth_plugin = req.environ.get('keystone.token_auth')

        ctx = self._create_context(
            req.environ,
            user_auth_plugin=user_auth_plugin,
            remote_address=remote_address,
            service_catalog=service_catalog)

        if ctx.user_id is None:
            LOG.debug("Neither X_USER_ID nor X_USER found in request")
            return webob.exc.HTTPUnauthorized()

        req.environ['nova.context'] = ctx
        return self.application
