escalator should use oslo.xxx instead of oslo-xxx
[escalator.git] / api / escalator / api / middleware / context.py
1 # Copyright 2016 OPNFV Foundation
2 # All Rights Reserved.
3 #
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
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
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
14 #    under the License.
15
16 from oslo.serialization import jsonutils
17 from oslo.config import cfg
18 from oslo.log import log as logging
19 import webob.exc
20
21 from escalator.api import policy
22 from escalator.common import wsgi
23 import escalator.context
24 from escalator import i18n
25
26 _ = i18n._
27
28 context_opts = [
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 '
33                        'request.')),
34     cfg.StrOpt('admin_role', default='admin',
35                help=_('Role used to identify an authenticated user as '
36                       'administrator.')),
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.')),
41 ]
42
43 CONF = cfg.CONF
44 CONF.register_opts(context_opts)
45
46 LOG = logging.getLogger(__name__)
47
48
49 class BaseContextMiddleware(wsgi.Middleware):
50     def process_response(self, resp):
51         try:
52             request_id = resp.request.context.request_id
53         except AttributeError:
54             LOG.warn(_('Unable to retrieve request id from context'))
55         else:
56             resp.headers['x-openstack-request-id'] = 'req-%s' % request_id
57         return resp
58
59
60 class ContextMiddleware(BaseContextMiddleware):
61     def __init__(self, app):
62         self.policy_enforcer = policy.Enforcer()
63         super(ContextMiddleware, self).__init__(app)
64
65     def process_request(self, req):
66         """Convert authentication information into a request context
67
68         Generate a escalator.context.RequestContext object from the available
69         authentication headers and store on the 'context' attribute
70         of the req object.
71
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
76         """
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()
81         else:
82             raise webob.exc.HTTPUnauthorized()
83
84     def _get_anonymous_context(self):
85         kwargs = {
86             'user': None,
87             'tenant': None,
88             'roles': [],
89             'is_admin': False,
90             'read_only': True,
91             'policy_enforcer': self.policy_enforcer,
92         }
93         return escalator.context.RequestContext(**kwargs)
94
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(',')]
100
101         # NOTE(bcwaldon): This header is deprecated in favor of X-Auth-Token
102         deprecated_token = req.headers.get('X-Storage-Token')
103
104         service_catalog = None
105         if req.headers.get('X-Service-Catalog') is not None:
106             try:
107                 catalog_header = req.headers.get('X-Service-Catalog')
108                 service_catalog = jsonutils.loads(catalog_header)
109             except ValueError:
110                 raise webob.exc.HTTPInternalServerError(
111                     _('Invalid service catalog json.'))
112
113         kwargs = {
114             'user': req.headers.get('X-User-Id'),
115             'tenant': req.headers.get('X-Tenant-Id'),
116             'roles': roles,
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,
122         }
123
124         return escalator.context.RequestContext(**kwargs)
125
126
127 class UnauthenticatedContextMiddleware(BaseContextMiddleware):
128     def process_request(self, req):
129         """Create a context without an authorized user."""
130         kwargs = {
131             'user': None,
132             'tenant': None,
133             'roles': [],
134             'is_admin': True,
135         }
136
137         req.context = escalator.context.RequestContext(**kwargs)