Coverage for manila/api/middleware/auth.py: 76%
92 statements
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
1# Copyright 2010 OpenStack LLC.
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"""
16Common Auth Middleware.
18"""
19import os
21from oslo_config import cfg
22from oslo_log import log
23from oslo_serialization import jsonutils
24import webob.dec
25import webob.exc
27from manila.api.openstack import wsgi
28from manila import context
29from manila.i18n import _
30from manila.wsgi import common as base_wsgi
32use_forwarded_for_opt = cfg.BoolOpt(
33 'use_forwarded_for',
34 default=False,
35 deprecated_for_removal=True,
36 deprecated_reason='This feature is duplicate of the HTTPProxyToWSGI '
37 'middleware of oslo.middleware.',
38 deprecated_since='Zed',
39 help='Treat X-Forwarded-For as the canonical remote address. '
40 'Only enable this if you have a sanitizing proxy.')
42CONF = cfg.CONF
43CONF.register_opt(use_forwarded_for_opt)
44LOG = log.getLogger(__name__)
47def pipeline_factory(loader, global_conf, **local_conf):
48 """A paste pipeline replica that keys off of auth_strategy."""
49 pipeline = local_conf[CONF.auth_strategy]
50 if not CONF.api_rate_limit: 50 ↛ 51line 50 didn't jump to line 51 because the condition on line 50 was never true
51 limit_name = CONF.auth_strategy + '_nolimit'
52 pipeline = local_conf.get(limit_name, pipeline)
53 pipeline = pipeline.split()
54 filters = [loader.get_filter(n) for n in pipeline[:-1]]
55 app = loader.get_app(pipeline[-1])
56 filters.reverse()
57 for filter in filters:
58 app = filter(app)
59 return app
62class InjectContext(base_wsgi.Middleware):
63 """Add a 'manila.context' to WSGI environ."""
65 def __init__(self, context, *args, **kwargs):
66 self.context = context
67 super(InjectContext, self).__init__(*args, **kwargs)
69 @webob.dec.wsgify(RequestClass=base_wsgi.Request)
70 def __call__(self, req):
71 req.environ['manila.context'] = self.context
72 return self.application
75class ManilaKeystoneContext(base_wsgi.Middleware):
76 """Make a request context from keystone headers."""
78 @webob.dec.wsgify(RequestClass=base_wsgi.Request)
79 def __call__(self, req):
80 # Build a context, including the auth_token...
81 remote_address = req.remote_addr
82 if CONF.use_forwarded_for: 82 ↛ 83line 82 didn't jump to line 83 because the condition on line 82 was never true
83 remote_address = req.headers.get('X-Forwarded-For', remote_address)
85 service_catalog = None
86 if req.headers.get('X_SERVICE_CATALOG') is not None: 86 ↛ 87line 86 didn't jump to line 87 because the condition on line 86 was never true
87 try:
88 catalog_header = req.headers.get('X_SERVICE_CATALOG')
89 service_catalog = jsonutils.loads(catalog_header)
90 except ValueError:
91 raise webob.exc.HTTPInternalServerError(
92 _('Invalid service catalog json.'))
94 ctx = context.RequestContext.from_environ(
95 req.environ,
96 remote_address=remote_address,
97 service_catalog=service_catalog)
99 if ctx.user_id is None:
100 LOG.debug("Neither X_USER_ID nor X_USER found in request")
101 return webob.exc.HTTPUnauthorized()
103 if req.environ.get('X_PROJECT_DOMAIN_ID'): 103 ↛ 104line 103 didn't jump to line 104 because the condition on line 103 was never true
104 ctx.project_domain_id = req.environ['X_PROJECT_DOMAIN_ID']
106 if req.environ.get('X_PROJECT_DOMAIN_NAME'): 106 ↛ 107line 106 didn't jump to line 107 because the condition on line 106 was never true
107 ctx.project_domain_name = req.environ['X_PROJECT_DOMAIN_NAME']
109 if req.environ.get('X_USER_DOMAIN_ID'): 109 ↛ 110line 109 didn't jump to line 110 because the condition on line 109 was never true
110 ctx.user_domain_id = req.environ['X_USER_DOMAIN_ID']
112 if req.environ.get('X_USER_DOMAIN_NAME'): 112 ↛ 113line 112 didn't jump to line 113 because the condition on line 112 was never true
113 ctx.user_domain_name = req.environ['X_USER_DOMAIN_NAME']
115 req.environ['manila.context'] = ctx
116 return self.application
119class NoAuthMiddlewareBase(base_wsgi.Middleware):
120 """Return a fake token if one isn't specified."""
122 def base_call(self, req, project_id_in_path=False):
123 if 'X-Auth-Token' not in req.headers:
124 user_id = req.headers.get('X-Auth-User', 'admin')
125 project_id = req.headers.get('X-Auth-Project-Id', 'admin')
126 if project_id_in_path: 126 ↛ 129line 126 didn't jump to line 129 because the condition on line 126 was always true
127 os_url = os.path.join(req.url.rstrip('/'), project_id)
128 else:
129 os_url = req.url.rstrip('/')
130 res = webob.Response()
131 # NOTE(vish): This is expecting and returning Auth(1.1), whereas
132 # keystone uses 2.0 auth. We should probably allow
133 # 2.0 auth here as well.
134 res.headers['X-Auth-Token'] = '%s:%s' % (user_id, project_id)
135 res.headers['X-Server-Management-Url'] = os_url
136 res.content_type = 'text/plain'
137 res.status = '204'
138 return res
140 token = req.headers['X-Auth-Token']
141 user_id, _sep, project_id = token.partition(':')
142 project_id = project_id or user_id
143 remote_address = getattr(req, 'remote_addr', '127.0.0.1')
144 if CONF.use_forwarded_for: 144 ↛ 145line 144 didn't jump to line 145 because the condition on line 144 was never true
145 remote_address = req.headers.get('X-Forwarded-For', remote_address)
146 ctx = context.RequestContext(user_id,
147 project_id,
148 is_admin=True,
149 remote_address=remote_address)
151 req.environ['manila.context'] = ctx
152 return self.application
155class NoAuthMiddleware(NoAuthMiddlewareBase):
156 """Return a fake token if one isn't specified.
158 Sets project_id in URLs.
159 """
161 @webob.dec.wsgify(RequestClass=wsgi.Request)
162 def __call__(self, req):
163 return self.base_call(req, project_id_in_path=True)
166class NoAuthMiddlewarev2_60(NoAuthMiddlewareBase):
167 """Return a fake token if one isn't specified.
169 Does not set project_id in URLs.
170 """
171 @webob.dec.wsgify(RequestClass=wsgi.Request)
172 def __call__(self, req):
173 return self.base_call(req)