1 # Copyright 2016 OPNFV Foundation
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain
6 # a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
16 from oslo_serialization import jsonutils
17 from oslo_config import cfg
18 from oslo_log import log as logging
21 from escalator.api import policy
22 from escalator.common import wsgi
23 import escalator.context
24 from escalator import i18n
29 cfg.BoolOpt('owner_is_tenant', default=True,
30 help=_('When true, this option sets the owner of an image '
31 'to be the tenant. Otherwise, the owner of the '
32 ' image will be the authenticated user issuing the '
34 cfg.StrOpt('admin_role', default='admin',
35 help=_('Role used to identify an authenticated user as '
37 cfg.BoolOpt('allow_anonymous_access', default=False,
38 help=_('Allow unauthenticated users to access the API with '
39 'read-only privileges. This only applies when using '
40 'ContextMiddleware.')),
44 CONF.register_opts(context_opts)
46 LOG = logging.getLogger(__name__)
49 class BaseContextMiddleware(wsgi.Middleware):
50 def process_response(self, resp):
52 request_id = resp.request.context.request_id
53 except AttributeError:
54 LOG.warn(_('Unable to retrieve request id from context'))
56 resp.headers['x-openstack-request-id'] = 'req-%s' % request_id
60 class ContextMiddleware(BaseContextMiddleware):
61 def __init__(self, app):
62 self.policy_enforcer = policy.Enforcer()
63 super(ContextMiddleware, self).__init__(app)
65 def process_request(self, req):
66 """Convert authentication information into a request context
68 Generate a escalator.context.RequestContext object from the available
69 authentication headers and store on the 'context' attribute
72 :param req: wsgi request object that will be given the context object
73 :raises webob.exc.HTTPUnauthorized: when value of the X-Identity-Status
74 header is not 'Confirmed' and
75 anonymous access is disallowed
77 if req.headers.get('X-Identity-Status') == 'Confirmed':
78 req.context = self._get_authenticated_context(req)
79 elif CONF.allow_anonymous_access:
80 req.context = self._get_anonymous_context()
82 raise webob.exc.HTTPUnauthorized()
84 def _get_anonymous_context(self):
91 'policy_enforcer': self.policy_enforcer,
93 return escalator.context.RequestContext(**kwargs)
95 def _get_authenticated_context(self, req):
96 # NOTE(bcwaldon): X-Roles is a csv string, but we need to parse
97 # it into a list to be useful
98 roles_header = req.headers.get('X-Roles', '')
99 roles = [r.strip().lower() for r in roles_header.split(',')]
101 # NOTE(bcwaldon): This header is deprecated in favor of X-Auth-Token
102 deprecated_token = req.headers.get('X-Storage-Token')
104 service_catalog = None
105 if req.headers.get('X-Service-Catalog') is not None:
107 catalog_header = req.headers.get('X-Service-Catalog')
108 service_catalog = jsonutils.loads(catalog_header)
110 raise webob.exc.HTTPInternalServerError(
111 _('Invalid service catalog json.'))
114 'user': req.headers.get('X-User-Id'),
115 'tenant': req.headers.get('X-Tenant-Id'),
117 'is_admin': CONF.admin_role.strip().lower() in roles,
118 'auth_token': req.headers.get('X-Auth-Token', deprecated_token),
119 'owner_is_tenant': CONF.owner_is_tenant,
120 'service_catalog': service_catalog,
121 'policy_enforcer': self.policy_enforcer,
124 return escalator.context.RequestContext(**kwargs)
127 class UnauthenticatedContextMiddleware(BaseContextMiddleware):
128 def process_request(self, req):
129 """Create a context without an authorized user."""
137 req.context = escalator.context.RequestContext(**kwargs)