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)