Merge lp:~khussein/swift/authn into lp:~hudson-openstack/swift/trunk
- authn
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~khussein/swift/authn |
Merge into: | lp:~hudson-openstack/swift/trunk |
Diff against target: |
1008 lines (+442/-250) 7 files modified
.unittests (+1/-1) doc/source/development_saio.rst (+9/-3) setup.py (+4/-2) swift/common/middleware/devauthn.py (+145/-0) swift/common/middleware/devauthz.py (+48/-109) swift/common/middleware/papiauth.py (+62/-0) test/unit/common/middleware/test_auth.py (+173/-135) |
To merge this branch: | bzr merge lp:~khussein/swift/authn |
Related bugs: | |
Related blueprints: |
Authentication in OpenStack
(Undefined)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Chuck Thier (community) | Needs Fixing | ||
gholt | Pending | ||
Review via email: mp+45532@code.launchpad.net |
This proposal supersedes a proposal from 2010-12-08.
This proposal has been superseded by a proposal from 2011-01-11.
Commit message
Description of the change
Implemented the proposed protocol to handle authentication in OpenStack services.
Mike Barton (redbo) wrote : Posted in a previous version of this proposal | # |
gholt (gholt) wrote : Posted in a previous version of this proposal | # |
We meant for this to be a work-in-progress; sorry.
gholt (gholt) wrote : Posted in a previous version of this proposal | # |
Oh, and these weren't the droids you were looking for anyhow. Move along; move along.
Khaled Hussein (khussein) wrote : Posted in a previous version of this proposal | # |
The proxy-server config now would be [auth1 devauth papiauth]
Chuck Thier (cthier) wrote : | # |
Initial comments:
1. There is one unit test failure
2. There are 9 functional test failures
3. There are no unit tests for the new pieces of middleware
4. There are no docs that explain how/when to use the different pieces of middleware
5. Do the saio or multiserver install docs need to change?
6. auth1 is a poor choice for the module name and class name
7. Is anything going to need to change when we move to swauth?
8. PEP8:
cthier@
swift/common/
swift/common/
swift/common/
swift/common/
swift/common/
cthier@
swift/common/
swift/common/
cthier@
cthier@
swift/common/
gholt (gholt) wrote : | # |
As I understood things, the DevAuth that we have always used was not going to change except that it was going to set an additional header instead of just setting REMOTE_USER. All the new auth stuff was going to be new files. What happened with that plan?
Khaled Hussein (khussein) wrote : | # |
Following the 'public or anonymous access' use case that you've mentioned, we've discussed adding the delegated mode, which is documented in our blueprint. So, this added a couple of new changes to the source code. It also lead us to refactor the code base a little bit so that we separate the two concerns: authentication and authorization into two separate modules.
- 160. By Khaled Hussein
-
documentation change
- 161. By Khaled Hussein
-
merge trunk
Unmerged revisions
- 161. By Khaled Hussein
-
merge trunk
- 160. By Khaled Hussein
-
documentation change
- 159. By Khaled Hussein
-
saio documentation changes
- 158. By Khaled Hussein
-
PEP8 Stuff :)
- 157. By Khaled Hussein
-
Refactored and Added unit tests
- 156. By Khaled Hussein
-
fixing conflicts
- 155. By Khaled Hussein
-
fixed functional tests
- 154. By Khaled Hussein
-
setup.py config changes
- 153. By Khaled Hussein
-
Merged trunk
- 152. By Khaled Hussein
-
Fixed unit tests
Preview Diff
1 | === modified file '.unittests' | |||
2 | --- .unittests 2010-07-13 03:34:34 +0000 | |||
3 | +++ .unittests 2011-01-11 20:54:28 +0000 | |||
4 | @@ -1,4 +1,4 @@ | |||
5 | 1 | #!/bin/bash | 1 | #!/bin/bash |
6 | 2 | 2 | ||
8 | 3 | nosetests test/unit --exe --with-coverage --cover-package swift --cover-erase | 3 | nosetests test/unit/ --exe --with-coverage --cover-package swift --cover-erase |
9 | 4 | rm -f .coverage | 4 | rm -f .coverage |
10 | 5 | 5 | ||
11 | === modified file 'doc/source/development_saio.rst' | |||
12 | --- doc/source/development_saio.rst 2010-12-02 01:08:49 +0000 | |||
13 | +++ doc/source/development_saio.rst 2011-01-11 20:54:28 +0000 | |||
14 | @@ -240,7 +240,7 @@ | |||
15 | 240 | 240 | ||
16 | 241 | [pipeline:main] | 241 | [pipeline:main] |
17 | 242 | # For DevAuth: | 242 | # For DevAuth: |
19 | 243 | pipeline = healthcheck cache auth proxy-server | 243 | pipeline = healthcheck cache devauthn devauthz papiauth proxy-server |
20 | 244 | # For Swauth: | 244 | # For Swauth: |
21 | 245 | # pipeline = healthcheck cache swauth proxy-server | 245 | # pipeline = healthcheck cache swauth proxy-server |
22 | 246 | 246 | ||
23 | @@ -249,8 +249,14 @@ | |||
24 | 249 | allow_account_management = true | 249 | allow_account_management = true |
25 | 250 | 250 | ||
26 | 251 | # Only needed for DevAuth | 251 | # Only needed for DevAuth |
29 | 252 | [filter:auth] | 252 | [filter:devauthn] |
30 | 253 | use = egg:swift#auth | 253 | use = egg:swift#devauthn |
31 | 254 | |||
32 | 255 | [filter:devauthz] | ||
33 | 256 | use = egg:swift#devauthz | ||
34 | 257 | |||
35 | 258 | [filter:papiauth] | ||
36 | 259 | use = egg:swift#papiauth | ||
37 | 254 | 260 | ||
38 | 255 | # Only needed for Swauth | 261 | # Only needed for Swauth |
39 | 256 | [filter:swauth] | 262 | [filter:swauth] |
40 | 257 | 263 | ||
41 | === modified file 'setup.py' | |||
42 | --- setup.py 2011-01-05 15:17:36 +0000 | |||
43 | +++ setup.py 2011-01-11 20:54:28 +0000 | |||
44 | @@ -1,5 +1,5 @@ | |||
45 | 1 | #!/usr/bin/python | 1 | #!/usr/bin/python |
47 | 2 | # Copyright (c) 2010-2011 OpenStack, LLC. | 2 | # Copyright (c) 2010 OpenStack, LLC. |
48 | 3 | # | 3 | # |
49 | 4 | # Licensed under the Apache License, Version 2.0 (the "License"); | 4 | # Licensed under the Apache License, Version 2.0 (the "License"); |
50 | 5 | # you may not use this file except in compliance with the License. | 5 | # you may not use this file except in compliance with the License. |
51 | @@ -94,7 +94,9 @@ | |||
52 | 94 | 'auth=swift.auth.server:app_factory', | 94 | 'auth=swift.auth.server:app_factory', |
53 | 95 | ], | 95 | ], |
54 | 96 | 'paste.filter_factory': [ | 96 | 'paste.filter_factory': [ |
56 | 97 | 'auth=swift.common.middleware.auth:filter_factory', | 97 | 'devauthn=swift.common.middleware.devauthn:filter_factory', |
57 | 98 | 'devauthz=swift.common.middleware.devauthz:filter_factory', | ||
58 | 99 | 'papiauth=swift.common.middleware.papiauth:filter_factory', | ||
59 | 98 | 'swauth=swift.common.middleware.swauth:filter_factory', | 100 | 'swauth=swift.common.middleware.swauth:filter_factory', |
60 | 99 | 'healthcheck=swift.common.middleware.healthcheck:filter_factory', | 101 | 'healthcheck=swift.common.middleware.healthcheck:filter_factory', |
61 | 100 | 'memcache=swift.common.middleware.memcache:filter_factory', | 102 | 'memcache=swift.common.middleware.memcache:filter_factory', |
62 | 101 | 103 | ||
63 | === added file 'swift/common/middleware/devauthn.py' | |||
64 | --- swift/common/middleware/devauthn.py 1970-01-01 00:00:00 +0000 | |||
65 | +++ swift/common/middleware/devauthn.py 2011-01-11 20:54:28 +0000 | |||
66 | @@ -0,0 +1,145 @@ | |||
67 | 1 | # Copyright (c) 2010 OpenStack, LLC. | ||
68 | 2 | # | ||
69 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
70 | 4 | # you may not use this file except in compliance with the License. | ||
71 | 5 | # You may obtain a copy of the License at | ||
72 | 6 | # | ||
73 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
74 | 8 | # | ||
75 | 9 | # Unless required by applicable law or agreed to in writing, software | ||
76 | 10 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
77 | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
78 | 12 | # implied. | ||
79 | 13 | # See the License for the specific language governing permissions and | ||
80 | 14 | # limitations under the License. | ||
81 | 15 | |||
82 | 16 | from time import time | ||
83 | 17 | |||
84 | 18 | from eventlet.timeout import Timeout | ||
85 | 19 | from webob.exc import HTTPUnauthorized | ||
86 | 20 | |||
87 | 21 | from swift.common.bufferedhttp import http_connect_raw as http_connect | ||
88 | 22 | from swift.common.utils import cache_from_env, TRUE_VALUES | ||
89 | 23 | |||
90 | 24 | |||
91 | 25 | class DevAuthN(object): | ||
92 | 26 | """Auth Middleware that uses the dev auth server.""" | ||
93 | 27 | |||
94 | 28 | def __init__(self, app, conf): | ||
95 | 29 | self.app = app | ||
96 | 30 | self.conf = conf | ||
97 | 31 | self.reseller_prefix = conf.get('reseller_prefix', 'AUTH').strip() | ||
98 | 32 | if self.reseller_prefix and self.reseller_prefix[-1] != '_': | ||
99 | 33 | self.reseller_prefix += '_' | ||
100 | 34 | self.auth_host = conf.get('ip', '127.0.0.1') | ||
101 | 35 | self.auth_prefix = conf.get('prefix', '/') | ||
102 | 36 | self.auth_port = int(conf.get('port', 11000)) | ||
103 | 37 | self.ssl = conf.get('ssl', 'false').lower() in TRUE_VALUES | ||
104 | 38 | self.timeout = int(conf.get('node_timeout', 10)) | ||
105 | 39 | self.delegated = int(conf.get('delegated', 0)) | ||
106 | 40 | |||
107 | 41 | def __call__(self, env, start_response): | ||
108 | 42 | """ | ||
109 | 43 | Accepts a standard WSGI application call and authenticates the request. | ||
110 | 44 | For an authenticated request, SWIFT_GROUPS will be set to a comma | ||
111 | 45 | separated list of the user's groups. It'll also set X-Authorization | ||
112 | 46 | header to 'Proxy [Username]'. If it is running in a delegated mode, it | ||
113 | 47 | sets the X-Identity-Status header to 'Confirmed' if the token is valid, | ||
114 | 48 | or 'Indeterminate' if the token doesn't exist. | ||
115 | 49 | |||
116 | 50 | With a non-empty reseller prefix, acts as the definitive auth service | ||
117 | 51 | for just tokens and accounts that begin with that prefix, but will deny | ||
118 | 52 | requests outside this prefix if no other auth middleware overrides it. | ||
119 | 53 | |||
120 | 54 | With an empty reseller prefix, acts as the definitive auth service only | ||
121 | 55 | for tokens that validate to a non-empty set of groups. For all other | ||
122 | 56 | requests, acts as the fallback auth service when no other auth | ||
123 | 57 | middleware overrides it. | ||
124 | 58 | """ | ||
125 | 59 | |||
126 | 60 | def custom_start_response(status, headers): | ||
127 | 61 | if self.delegated: | ||
128 | 62 | headers.append(('WWW-Authenticate', "Basic realm='API Realm'")) | ||
129 | 63 | return start_response(status, headers) | ||
130 | 64 | |||
131 | 65 | token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN')) | ||
132 | 66 | if token and token.startswith(self.reseller_prefix): | ||
133 | 67 | # Note: Empty reseller_prefix will match all tokens. | ||
134 | 68 | # Attempt to auth my token with my auth server | ||
135 | 69 | groups = \ | ||
136 | 70 | self.get_groups(token, memcache_client=cache_from_env(env)) | ||
137 | 71 | if groups: | ||
138 | 72 | user = groups and groups.split(',', 1)[0] or '' | ||
139 | 73 | env['SWIFT_GROUPS'] = groups | ||
140 | 74 | env['HTTP_X_AUTHORIZATION'] = "Proxy " + user | ||
141 | 75 | if self.delegated: | ||
142 | 76 | env['HTTP_X_IDENTITY_STATUS'] = "Confirmed" | ||
143 | 77 | # We know the proxy logs the token, so we augment it just | ||
144 | 78 | # a bit to also log the authenticated user. | ||
145 | 79 | env['HTTP_X_AUTH_TOKEN'] = '%s,%s' % (user, token) | ||
146 | 80 | else: | ||
147 | 81 | if self.delegated and not self.reseller_prefix: | ||
148 | 82 | env['HTTP_X_IDENTITY_STATUS'] = "Invalid" | ||
149 | 83 | else: | ||
150 | 84 | # Unauthorized token | ||
151 | 85 | return HTTPUnauthorized()(env, custom_start_response) | ||
152 | 86 | else: | ||
153 | 87 | env['HTTP_X_AUTHORIZATION'] = "Proxy" | ||
154 | 88 | if self.delegated: | ||
155 | 89 | env['HTTP_X_IDENTITY_STATUS'] = "Indeterminate" | ||
156 | 90 | else: | ||
157 | 91 | return HTTPUnauthorized()(env, custom_start_response) | ||
158 | 92 | |||
159 | 93 | env['HTTP_AUTHORIZATION'] = "Basic dTpw" | ||
160 | 94 | return self.app(env, custom_start_response) | ||
161 | 95 | |||
162 | 96 | def get_groups(self, token, memcache_client=None): | ||
163 | 97 | """ | ||
164 | 98 | Get groups for the given token. | ||
165 | 99 | |||
166 | 100 | If memcache_client is set, token credentials will be cached | ||
167 | 101 | appropriately. | ||
168 | 102 | |||
169 | 103 | With a cache miss, or no memcache_client, the configurated external | ||
170 | 104 | authentication server will be queried for the group information. | ||
171 | 105 | |||
172 | 106 | :param token: Token to validate and return a group string for. | ||
173 | 107 | :param memcache_client: Memcached client to use for caching token | ||
174 | 108 | credentials; None if no caching is desired. | ||
175 | 109 | :returns: None if the token is invalid or a string containing a comma | ||
176 | 110 | separated list of groups the authenticated user is a member | ||
177 | 111 | of. The first group in the list is also considered a unique | ||
178 | 112 | identifier for that user. | ||
179 | 113 | """ | ||
180 | 114 | groups = None | ||
181 | 115 | key = '%s/token/%s' % (self.reseller_prefix, token) | ||
182 | 116 | cached_auth_data = memcache_client and memcache_client.get(key) | ||
183 | 117 | if cached_auth_data: | ||
184 | 118 | start, expiration, groups = cached_auth_data | ||
185 | 119 | if time() - start > expiration: | ||
186 | 120 | groups = None | ||
187 | 121 | if not groups: | ||
188 | 122 | with Timeout(self.timeout): | ||
189 | 123 | conn = http_connect(self.auth_host, self.auth_port, 'GET', | ||
190 | 124 | '%stoken/%s' % (self.auth_prefix, token), ssl=self.ssl) | ||
191 | 125 | resp = conn.getresponse() | ||
192 | 126 | resp.read() | ||
193 | 127 | conn.close() | ||
194 | 128 | if resp.status // 100 != 2: | ||
195 | 129 | return None | ||
196 | 130 | expiration = float(resp.getheader('x-auth-ttl')) | ||
197 | 131 | groups = resp.getheader('x-auth-groups') | ||
198 | 132 | if memcache_client: | ||
199 | 133 | memcache_client.set(key, (time(), expiration, groups), | ||
200 | 134 | timeout=expiration) | ||
201 | 135 | return groups | ||
202 | 136 | |||
203 | 137 | |||
204 | 138 | def filter_factory(global_conf, **local_conf): | ||
205 | 139 | """Returns a WSGI filter app for use with paste.deploy.""" | ||
206 | 140 | conf = global_conf.copy() | ||
207 | 141 | conf.update(local_conf) | ||
208 | 142 | |||
209 | 143 | def auth_filter(app): | ||
210 | 144 | return DevAuthN(app, conf) | ||
211 | 145 | return auth_filter | ||
212 | 0 | 146 | ||
213 | === renamed file 'swift/common/middleware/auth.py' => 'swift/common/middleware/devauthz.py' | |||
214 | --- swift/common/middleware/auth.py 2011-01-05 16:14:31 +0000 | |||
215 | +++ swift/common/middleware/devauthz.py 2011-01-11 20:54:28 +0000 | |||
216 | @@ -13,17 +13,13 @@ | |||
217 | 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
218 | 14 | # limitations under the License. | 14 | # limitations under the License. |
219 | 15 | 15 | ||
220 | 16 | from time import time | ||
221 | 17 | |||
222 | 18 | from eventlet.timeout import Timeout | ||
223 | 19 | from webob.exc import HTTPForbidden, HTTPUnauthorized, HTTPNotFound | 16 | from webob.exc import HTTPForbidden, HTTPUnauthorized, HTTPNotFound |
224 | 20 | 17 | ||
225 | 21 | from swift.common.bufferedhttp import http_connect_raw as http_connect | ||
226 | 22 | from swift.common.middleware.acl import clean_acl, parse_acl, referrer_allowed | 18 | from swift.common.middleware.acl import clean_acl, parse_acl, referrer_allowed |
231 | 23 | from swift.common.utils import cache_from_env, split_path, TRUE_VALUES | 19 | from swift.common.utils import split_path, TRUE_VALUES |
232 | 24 | 20 | ||
233 | 25 | 21 | ||
234 | 26 | class DevAuth(object): | 22 | class DevAuthZ(object): |
235 | 27 | """Auth Middleware that uses the dev auth server.""" | 23 | """Auth Middleware that uses the dev auth server.""" |
236 | 28 | 24 | ||
237 | 29 | def __init__(self, app, conf): | 25 | def __init__(self, app, conf): |
238 | @@ -42,110 +38,50 @@ | |||
239 | 42 | """ | 38 | """ |
240 | 43 | Accepts a standard WSGI application call, authenticating the request | 39 | Accepts a standard WSGI application call, authenticating the request |
241 | 44 | and installing callback hooks for authorization and ACL header | 40 | and installing callback hooks for authorization and ACL header |
253 | 45 | validation. For an authenticated request, REMOTE_USER will be set to a | 41 | validation. |
243 | 46 | comma separated list of the user's groups. | ||
244 | 47 | |||
245 | 48 | With a non-empty reseller prefix, acts as the definitive auth service | ||
246 | 49 | for just tokens and accounts that begin with that prefix, but will deny | ||
247 | 50 | requests outside this prefix if no other auth middleware overrides it. | ||
248 | 51 | |||
249 | 52 | With an empty reseller prefix, acts as the definitive auth service only | ||
250 | 53 | for tokens that validate to a non-empty set of groups. For all other | ||
251 | 54 | requests, acts as the fallback auth service when no other auth | ||
252 | 55 | middleware overrides it. | ||
254 | 56 | """ | 42 | """ |
267 | 57 | token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN')) | 43 | groups = None |
268 | 58 | if token and token.startswith(self.reseller_prefix): | 44 | if 'SWIFT_GROUPS' in env: |
269 | 59 | # Note: Empty reseller_prefix will match all tokens. | 45 | groups = env['SWIFT_GROUPS'] |
270 | 60 | # Attempt to auth my token with my auth server | 46 | env['REMOTE_USER'] = groups |
271 | 61 | groups = \ | 47 | self.authorize = self.dev_authorize |
272 | 62 | self.get_groups(token, memcache_client=cache_from_env(env)) | 48 | env['swift.clean_acl'] = clean_acl |
273 | 63 | if groups: | 49 | elif 'swift.authorize' not in env: |
274 | 64 | env['REMOTE_USER'] = groups | 50 | self.authorize = self.empty_authorize |
275 | 65 | user = groups and groups.split(',', 1)[0] or '' | 51 | |
276 | 66 | # We know the proxy logs the token, so we augment it just a bit | 52 | if self.reseller_prefix: |
277 | 67 | # to also log the authenticated user. | 53 | # With a non-empty reseller_prefix, I would like to be called |
278 | 68 | env['HTTP_X_AUTH_TOKEN'] = '%s,%s' % (user, token) | 54 | # back for anonymous access to accounts I know I'm the |
279 | 55 | # definitive auth for. | ||
280 | 56 | try: | ||
281 | 57 | version, rest = split_path(env.get('PATH_INFO', ''), | ||
282 | 58 | 1, 2, True) | ||
283 | 59 | except ValueError: | ||
284 | 60 | return HTTPNotFound()(env, start_response) | ||
285 | 61 | if rest and rest.startswith(self.reseller_prefix): | ||
286 | 62 | # Handle anonymous access to accounts I'm the definitive | ||
287 | 63 | # auth for. | ||
288 | 69 | env['swift.authorize'] = self.authorize | 64 | env['swift.authorize'] = self.authorize |
289 | 70 | env['swift.clean_acl'] = clean_acl | 65 | env['swift.clean_acl'] = clean_acl |
321 | 71 | else: | 66 | # Not my token, not my account, I can't authorize this request, |
322 | 72 | # Unauthorized token | 67 | # deny all is a good idea if not already set... |
292 | 73 | if self.reseller_prefix: | ||
293 | 74 | # Because I know I'm the definitive auth for this token, I | ||
294 | 75 | # can deny it outright. | ||
295 | 76 | return HTTPUnauthorized()(env, start_response) | ||
296 | 77 | # Because I'm not certain if I'm the definitive auth for empty | ||
297 | 78 | # reseller_prefixed tokens, I won't overwrite swift.authorize. | ||
298 | 79 | elif 'swift.authorize' not in env: | ||
299 | 80 | env['swift.authorize'] = self.denied_response | ||
300 | 81 | else: | ||
301 | 82 | if self.reseller_prefix: | ||
302 | 83 | # With a non-empty reseller_prefix, I would like to be called | ||
303 | 84 | # back for anonymous access to accounts I know I'm the | ||
304 | 85 | # definitive auth for. | ||
305 | 86 | try: | ||
306 | 87 | version, rest = split_path(env.get('PATH_INFO', ''), | ||
307 | 88 | 1, 2, True) | ||
308 | 89 | except ValueError: | ||
309 | 90 | return HTTPNotFound()(env, start_response) | ||
310 | 91 | if rest and rest.startswith(self.reseller_prefix): | ||
311 | 92 | # Handle anonymous access to accounts I'm the definitive | ||
312 | 93 | # auth for. | ||
313 | 94 | env['swift.authorize'] = self.authorize | ||
314 | 95 | env['swift.clean_acl'] = clean_acl | ||
315 | 96 | # Not my token, not my account, I can't authorize this request, | ||
316 | 97 | # deny all is a good idea if not already set... | ||
317 | 98 | elif 'swift.authorize' not in env: | ||
318 | 99 | env['swift.authorize'] = self.denied_response | ||
319 | 100 | # Because I'm not certain if I'm the definitive auth for empty | ||
320 | 101 | # reseller_prefixed accounts, I won't overwrite swift.authorize. | ||
323 | 102 | elif 'swift.authorize' not in env: | 68 | elif 'swift.authorize' not in env: |
326 | 103 | env['swift.authorize'] = self.authorize | 69 | env['swift.authorize'] = self.denied_response |
327 | 104 | env['swift.clean_acl'] = clean_acl | 70 | # Because I'm not certain if I'm the definitive auth for empty |
328 | 71 | # reseller_prefixed accounts, I won't overwrite swift.authorize. | ||
329 | 72 | elif 'swift.authorize' not in env: | ||
330 | 73 | env['swift.authorize'] = self.empty_authorize | ||
331 | 105 | return self.app(env, start_response) | 74 | return self.app(env, start_response) |
332 | 106 | 75 | ||
375 | 107 | def get_groups(self, token, memcache_client=None): | 76 | def empty_authorize(self, req): |
376 | 108 | """ | 77 | if 'x_identity_status' in req.headers: |
377 | 109 | Get groups for the given token. | 78 | if req.headers['x_identity_status'] == 'Invalid': |
378 | 110 | 79 | return self.denied_response(req) | |
379 | 111 | If memcache_client is set, token credentials will be cached | 80 | elif req.headers['x_identity_status'] == 'Indeterminate': |
380 | 112 | appropriately. | 81 | return self.dev_authorize(req) |
381 | 113 | 82 | return None | |
382 | 114 | With a cache miss, or no memcache_client, the configurated external | 83 | |
383 | 115 | authentication server will be queried for the group information. | 84 | def dev_authorize(self, req): |
342 | 116 | |||
343 | 117 | :param token: Token to validate and return a group string for. | ||
344 | 118 | :param memcache_client: Memcached client to use for caching token | ||
345 | 119 | credentials; None if no caching is desired. | ||
346 | 120 | :returns: None if the token is invalid or a string containing a comma | ||
347 | 121 | separated list of groups the authenticated user is a member | ||
348 | 122 | of. The first group in the list is also considered a unique | ||
349 | 123 | identifier for that user. | ||
350 | 124 | """ | ||
351 | 125 | groups = None | ||
352 | 126 | key = '%s/token/%s' % (self.reseller_prefix, token) | ||
353 | 127 | cached_auth_data = memcache_client and memcache_client.get(key) | ||
354 | 128 | if cached_auth_data: | ||
355 | 129 | start, expiration, groups = cached_auth_data | ||
356 | 130 | if time() - start > expiration: | ||
357 | 131 | groups = None | ||
358 | 132 | if not groups: | ||
359 | 133 | with Timeout(self.timeout): | ||
360 | 134 | conn = http_connect(self.auth_host, self.auth_port, 'GET', | ||
361 | 135 | '%stoken/%s' % (self.auth_prefix, token), ssl=self.ssl) | ||
362 | 136 | resp = conn.getresponse() | ||
363 | 137 | resp.read() | ||
364 | 138 | conn.close() | ||
365 | 139 | if resp.status // 100 != 2: | ||
366 | 140 | return None | ||
367 | 141 | expiration = float(resp.getheader('x-auth-ttl')) | ||
368 | 142 | groups = resp.getheader('x-auth-groups') | ||
369 | 143 | if memcache_client: | ||
370 | 144 | memcache_client.set(key, (time(), expiration, groups), | ||
371 | 145 | timeout=expiration) | ||
372 | 146 | return groups | ||
373 | 147 | |||
374 | 148 | def authorize(self, req): | ||
384 | 149 | """ | 85 | """ |
385 | 150 | Returns None if the request is authorized to continue or a standard | 86 | Returns None if the request is authorized to continue or a standard |
386 | 151 | WSGI response callable if not. | 87 | WSGI response callable if not. |
387 | @@ -179,10 +115,13 @@ | |||
388 | 179 | Returns a standard WSGI response callable with the status of 403 or 401 | 115 | Returns a standard WSGI response callable with the status of 403 or 401 |
389 | 180 | depending on whether the REMOTE_USER is set or not. | 116 | depending on whether the REMOTE_USER is set or not. |
390 | 181 | """ | 117 | """ |
391 | 118 | headers = [('www-authenticate', 'delegated')] | ||
392 | 182 | if req.remote_user: | 119 | if req.remote_user: |
394 | 183 | return HTTPForbidden(request=req) | 120 | resp = HTTPForbidden(headers=headers, request=req) |
395 | 184 | else: | 121 | else: |
397 | 185 | return HTTPUnauthorized(request=req) | 122 | resp = HTTPUnauthorized(headers=headers, request=req) |
398 | 123 | |||
399 | 124 | return resp | ||
400 | 186 | 125 | ||
401 | 187 | 126 | ||
402 | 188 | def filter_factory(global_conf, **local_conf): | 127 | def filter_factory(global_conf, **local_conf): |
403 | @@ -191,5 +130,5 @@ | |||
404 | 191 | conf.update(local_conf) | 130 | conf.update(local_conf) |
405 | 192 | 131 | ||
406 | 193 | def auth_filter(app): | 132 | def auth_filter(app): |
408 | 194 | return DevAuth(app, conf) | 133 | return DevAuthZ(app, conf) |
409 | 195 | return auth_filter | 134 | return auth_filter |
410 | 196 | 135 | ||
411 | === added file 'swift/common/middleware/papiauth.py' | |||
412 | --- swift/common/middleware/papiauth.py 1970-01-01 00:00:00 +0000 | |||
413 | +++ swift/common/middleware/papiauth.py 2011-01-11 20:54:28 +0000 | |||
414 | @@ -0,0 +1,62 @@ | |||
415 | 1 | # Copyright (c) 2010 OpenStack, LLC. | ||
416 | 2 | # | ||
417 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
418 | 4 | # you may not use this file except in compliance with the License. | ||
419 | 5 | # You may obtain a copy of the License at | ||
420 | 6 | # | ||
421 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
422 | 8 | # | ||
423 | 9 | # Unless required by applicable law or agreed to in writing, software | ||
424 | 10 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
425 | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
426 | 12 | # implied. | ||
427 | 13 | # See the License for the specific language governing permissions and | ||
428 | 14 | # limitations under the License. | ||
429 | 15 | |||
430 | 16 | from webob.exc import HTTPUseProxy, HTTPUnauthorized | ||
431 | 17 | |||
432 | 18 | from swift.common.utils import TRUE_VALUES | ||
433 | 19 | |||
434 | 20 | |||
435 | 21 | class PAPIAuth(object): | ||
436 | 22 | """Auth Middleware that uses the dev auth server.""" | ||
437 | 23 | |||
438 | 24 | def __init__(self, app, conf): | ||
439 | 25 | self.app = app | ||
440 | 26 | self.conf = conf | ||
441 | 27 | self.reseller_prefix = conf.get('reseller_prefix', 'AUTH').strip() | ||
442 | 28 | if self.reseller_prefix and self.reseller_prefix[-1] != '_': | ||
443 | 29 | self.reseller_prefix += '_' | ||
444 | 30 | self.auth_host = conf.get('ip', '127.0.0.1') | ||
445 | 31 | self.auth_port = int(conf.get('port', 11000)) | ||
446 | 32 | self.auth_pass = conf.get('pass', 'dTpw') | ||
447 | 33 | self.ssl = conf.get('ssl', 'false').lower() in TRUE_VALUES | ||
448 | 34 | self.timeout = int(conf.get('node_timeout', 10)) | ||
449 | 35 | |||
450 | 36 | def __call__(self, env, start_response): | ||
451 | 37 | # Make sure that the user has been authenticated by the Auth Service | ||
452 | 38 | if 'HTTP_X_AUTHORIZATION' not in env: | ||
453 | 39 | proxy_location = 'http://' + self.auth_host + ':' + \ | ||
454 | 40 | str(self.auth_port) + '/' | ||
455 | 41 | return HTTPUseProxy(location=proxy_location)(env, start_response) | ||
456 | 42 | |||
457 | 43 | # Authenticate the Auth component itself. | ||
458 | 44 | headers = [('www-authenticate', 'Basic realm="swift"')] | ||
459 | 45 | if 'HTTP_AUTHORIZATION' not in env: | ||
460 | 46 | return HTTPUnauthorized(headers=headers)(env, start_response) | ||
461 | 47 | else: | ||
462 | 48 | auth_type, encoded_creds = env['HTTP_AUTHORIZATION'].split(None, 1) | ||
463 | 49 | if encoded_creds != self.auth_pass: | ||
464 | 50 | return HTTPUnauthorized(headers=headers)(env, start_response) | ||
465 | 51 | |||
466 | 52 | return self.app(env, start_response) | ||
467 | 53 | |||
468 | 54 | |||
469 | 55 | def filter_factory(global_conf, **local_conf): | ||
470 | 56 | """Returns a WSGI filter app for use with paste.deploy.""" | ||
471 | 57 | conf = global_conf.copy() | ||
472 | 58 | conf.update(local_conf) | ||
473 | 59 | |||
474 | 60 | def auth_filter(app): | ||
475 | 61 | return PAPIAuth(app, conf) | ||
476 | 62 | return auth_filter | ||
477 | 0 | 63 | ||
478 | === modified file 'test/unit/common/middleware/test_auth.py' | |||
479 | --- test/unit/common/middleware/test_auth.py 2011-01-05 16:14:31 +0000 | |||
480 | +++ test/unit/common/middleware/test_auth.py 2011-01-11 20:54:28 +0000 | |||
481 | @@ -23,7 +23,7 @@ | |||
482 | 23 | import eventlet | 23 | import eventlet |
483 | 24 | from webob import Request | 24 | from webob import Request |
484 | 25 | 25 | ||
486 | 26 | from swift.common.middleware import auth | 26 | from swift.common.middleware import devauthn, devauthz, papiauth |
487 | 27 | 27 | ||
488 | 28 | # mocks | 28 | # mocks |
489 | 29 | logging.getLogger().addHandler(logging.StreamHandler(sys.stdout)) | 29 | logging.getLogger().addHandler(logging.StreamHandler(sys.stdout)) |
490 | @@ -122,243 +122,281 @@ | |||
491 | 122 | class TestAuth(unittest.TestCase): | 122 | class TestAuth(unittest.TestCase): |
492 | 123 | 123 | ||
493 | 124 | def setUp(self): | 124 | def setUp(self): |
495 | 125 | self.test_auth = auth.filter_factory({})(FakeApp()) | 125 | self.test_authn = devauthn.filter_factory({})(FakeApp()) |
496 | 126 | self.test_authz = devauthz.filter_factory({})(FakeApp()) | ||
497 | 127 | self.test_papiauth = papiauth.filter_factory({})(FakeApp()) | ||
498 | 126 | 128 | ||
499 | 127 | def test_auth_deny_non_reseller_prefix(self): | 129 | def test_auth_deny_non_reseller_prefix(self): |
512 | 128 | old_http_connect = auth.http_connect | 130 | reqenv = {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/BLAH_account', |
513 | 129 | try: | 131 | 'HTTP_X_AUTH_TOKEN': 'BLAH_t', 'swift.cache': FakeMemcache()} |
514 | 130 | auth.http_connect = mock_http_connect(204, | 132 | result = ''.join(self.test_authz(reqenv, lambda x, y: None)) |
515 | 131 | {'x-auth-ttl': '1234', 'x-auth-groups': 'act:usr,act,AUTH_cfa'}) | 133 | self.assert_(result.startswith('401'), result) |
516 | 132 | reqenv = {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/BLAH_account', | 134 | self.assertEquals(reqenv['swift.authorize'], |
517 | 133 | 'HTTP_X_AUTH_TOKEN': 'BLAH_t', 'swift.cache': FakeMemcache()} | 135 | self.test_authz.denied_response) |
506 | 134 | result = ''.join(self.test_auth(reqenv, lambda x, y: None)) | ||
507 | 135 | self.assert_(result.startswith('401'), result) | ||
508 | 136 | self.assertEquals(reqenv['swift.authorize'], | ||
509 | 137 | self.test_auth.denied_response) | ||
510 | 138 | finally: | ||
511 | 139 | auth.http_connect = old_http_connect | ||
518 | 140 | 136 | ||
519 | 141 | def test_auth_deny_non_reseller_prefix_no_override(self): | 137 | def test_auth_deny_non_reseller_prefix_no_override(self): |
521 | 142 | old_http_connect = auth.http_connect | 138 | old_http_connect = devauthn.http_connect |
522 | 143 | try: | 139 | try: |
524 | 144 | auth.http_connect = mock_http_connect(204, | 140 | local_app = FakeApp() |
525 | 141 | local_authz = \ | ||
526 | 142 | devauthz.filter_factory({'reseller_prefix': ''})(local_app) | ||
527 | 143 | local_authn = \ | ||
528 | 144 | devauthn.filter_factory({'delegated': 1})(local_authz) | ||
529 | 145 | devauthn.http_connect = mock_http_connect(204, | ||
530 | 145 | {'x-auth-ttl': '1234', 'x-auth-groups': 'act:usr,act,AUTH_cfa'}) | 146 | {'x-auth-ttl': '1234', 'x-auth-groups': 'act:usr,act,AUTH_cfa'}) |
531 | 146 | fake_authorize = lambda x: lambda x, y: ['500 Fake'] | 147 | fake_authorize = lambda x: lambda x, y: ['500 Fake'] |
532 | 147 | reqenv = {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/BLAH_account', | 148 | reqenv = {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/BLAH_account', |
533 | 148 | 'HTTP_X_AUTH_TOKEN': 'BLAH_t', 'swift.cache': FakeMemcache(), | 149 | 'HTTP_X_AUTH_TOKEN': 'BLAH_t', 'swift.cache': FakeMemcache(), |
534 | 149 | 'swift.authorize': fake_authorize} | 150 | 'swift.authorize': fake_authorize} |
536 | 150 | result = ''.join(self.test_auth(reqenv, lambda x, y: None)) | 151 | result = ''.join(local_authn(reqenv, lambda x, y: None)) |
537 | 151 | self.assert_(result.startswith('500 Fake'), result) | 152 | self.assert_(result.startswith('500 Fake'), result) |
538 | 152 | self.assertEquals(reqenv['swift.authorize'], fake_authorize) | 153 | self.assertEquals(reqenv['swift.authorize'], fake_authorize) |
539 | 153 | finally: | 154 | finally: |
541 | 154 | auth.http_connect = old_http_connect | 155 | devauthn.http_connect = old_http_connect |
542 | 155 | 156 | ||
544 | 156 | def test_auth_no_reseller_prefix_deny(self): | 157 | def test_auth_no_reseller_prefix_deny_delegated(self): |
545 | 157 | # Ensures that when we have no reseller prefix, we don't deny a request | 158 | # Ensures that when we have no reseller prefix, we don't deny a request |
546 | 158 | # outright but set up a denial swift.authorize and pass the request on | 159 | # outright but set up a denial swift.authorize and pass the request on |
547 | 159 | # down the chain. | 160 | # down the chain. |
549 | 160 | old_http_connect = auth.http_connect | 161 | old_http_connect = devauthn.http_connect |
550 | 161 | try: | 162 | try: |
551 | 162 | local_app = FakeApp() | 163 | local_app = FakeApp() |
555 | 163 | local_auth = \ | 164 | local_authz = \ |
556 | 164 | auth.filter_factory({'reseller_prefix': ''})(local_app) | 165 | devauthz.filter_factory({'reseller_prefix': ''})(local_app) |
557 | 165 | auth.http_connect = mock_http_connect(404) | 166 | local_authn = \ |
558 | 167 | devauthn.filter_factory(\ | ||
559 | 168 | {'reseller_prefix': '', 'delegated': 1})(local_authz) | ||
560 | 169 | devauthn.http_connect = mock_http_connect(404) | ||
561 | 166 | reqenv = {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/account', | 170 | reqenv = {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/account', |
562 | 167 | 'HTTP_X_AUTH_TOKEN': 't', 'swift.cache': FakeMemcache()} | 171 | 'HTTP_X_AUTH_TOKEN': 't', 'swift.cache': FakeMemcache()} |
564 | 168 | result = ''.join(local_auth(reqenv, lambda x, y: None)) | 172 | result = ''.join(local_authn(reqenv, lambda x, y: None)) |
565 | 169 | self.assert_(result.startswith('401'), result) | 173 | self.assert_(result.startswith('401'), result) |
566 | 170 | self.assert_(local_app.i_was_called) | 174 | self.assert_(local_app.i_was_called) |
567 | 171 | self.assertEquals(reqenv['swift.authorize'], | 175 | self.assertEquals(reqenv['swift.authorize'], |
571 | 172 | local_auth.denied_response) | 176 | local_authz.empty_authorize) |
572 | 173 | finally: | 177 | finally: |
573 | 174 | auth.http_connect = old_http_connect | 178 | devauthn.http_connect = old_http_connect |
574 | 179 | |||
575 | 180 | def test_auth_no_reseller_prefix_deny_non_delegated(self): | ||
576 | 181 | # Ensures that when we have no reseller prefix, we don't deny a request | ||
577 | 182 | # outright but set up a denial swift.authorize and pass the request on | ||
578 | 183 | # down the chain. | ||
579 | 184 | old_http_connect = devauthn.http_connect | ||
580 | 185 | try: | ||
581 | 186 | local_app = FakeApp() | ||
582 | 187 | local_authn = \ | ||
583 | 188 | devauthn.filter_factory(\ | ||
584 | 189 | {'reseller_prefix': ''})(local_app) | ||
585 | 190 | devauthn.http_connect = mock_http_connect(404) | ||
586 | 191 | reqenv = {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/account', | ||
587 | 192 | 'HTTP_X_AUTH_TOKEN': 't', 'swift.cache': FakeMemcache()} | ||
588 | 193 | result = ''.join(local_authn(reqenv, lambda x, y: None)) | ||
589 | 194 | self.assert_(result.startswith('401'), result) | ||
590 | 195 | self.assert_(not local_app.i_was_called) | ||
591 | 196 | finally: | ||
592 | 197 | devauthn.http_connect = old_http_connect | ||
593 | 198 | |||
594 | 175 | 199 | ||
595 | 176 | def test_auth_no_reseller_prefix_allow(self): | 200 | def test_auth_no_reseller_prefix_allow(self): |
596 | 177 | # Ensures that when we have no reseller prefix, we can still allow | 201 | # Ensures that when we have no reseller prefix, we can still allow |
597 | 178 | # access if our auth server accepts requests | 202 | # access if our auth server accepts requests |
614 | 179 | old_http_connect = auth.http_connect | 203 | local_app = FakeApp() |
615 | 180 | try: | 204 | local_auth = \ |
616 | 181 | local_app = FakeApp() | 205 | devauthz.filter_factory({'reseller_prefix': ''})(local_app) |
617 | 182 | local_auth = \ | 206 | reqenv = {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/act', |
618 | 183 | auth.filter_factory({'reseller_prefix': ''})(local_app) | 207 | 'HTTP_X_AUTH_TOKEN': 't', 'swift.cache': None, |
619 | 184 | auth.http_connect = mock_http_connect(204, | 208 | 'SWIFT_GROUPS': 'act:usr,act,AUTH_cfa'} |
620 | 185 | {'x-auth-ttl': '1234', 'x-auth-groups': 'act:usr,act,AUTH_cfa'}) | 209 | result = ''.join(local_auth(reqenv, lambda x, y: None)) |
621 | 186 | reqenv = {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/act', | 210 | self.assert_(result.startswith('204'), result) |
622 | 187 | 'HTTP_X_AUTH_TOKEN': 't', 'swift.cache': None} | 211 | self.assert_(local_app.i_was_called) |
623 | 188 | result = ''.join(local_auth(reqenv, lambda x, y: None)) | 212 | self.assertEquals(reqenv['swift.authorize'], |
624 | 189 | self.assert_(result.startswith('204'), result) | 213 | local_auth.empty_authorize) |
609 | 190 | self.assert_(local_app.i_was_called) | ||
610 | 191 | self.assertEquals(reqenv['swift.authorize'], | ||
611 | 192 | local_auth.authorize) | ||
612 | 193 | finally: | ||
613 | 194 | auth.http_connect = old_http_connect | ||
625 | 195 | 214 | ||
627 | 196 | def test_auth_no_reseller_prefix_no_token(self): | 215 | def test_auth_no_reseller_prefix_no_token_delegated(self): |
628 | 197 | # Check that normally we set up a call back to our authorize. | 216 | # Check that normally we set up a call back to our authorize. |
631 | 198 | local_auth = \ | 217 | local_authz = \ |
632 | 199 | auth.filter_factory({'reseller_prefix': ''})(FakeApp()) | 218 | devauthz.filter_factory({'reseller_prefix': ''})(FakeApp()) |
633 | 219 | local_authn = \ | ||
634 | 220 | devauthn.filter_factory(\ | ||
635 | 221 | {'reseller_prefix': '', 'delegated':1})(local_authz) | ||
636 | 200 | reqenv = {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/account', | 222 | reqenv = {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/account', |
637 | 201 | 'swift.cache': FakeMemcache()} | 223 | 'swift.cache': FakeMemcache()} |
639 | 202 | result = ''.join(local_auth(reqenv, lambda x, y: None)) | 224 | result = ''.join(local_authn(reqenv, lambda x, y: None)) |
640 | 203 | self.assert_(result.startswith('401'), result) | 225 | self.assert_(result.startswith('401'), result) |
642 | 204 | self.assertEquals(reqenv['swift.authorize'], local_auth.authorize) | 226 | self.assertEquals(\ |
643 | 227 | reqenv['swift.authorize'], local_authz.empty_authorize) | ||
644 | 205 | # Now make sure we don't override an existing swift.authorize when we | 228 | # Now make sure we don't override an existing swift.authorize when we |
645 | 206 | # have no reseller prefix. | 229 | # have no reseller prefix. |
646 | 207 | local_authorize = lambda req: None | 230 | local_authorize = lambda req: None |
647 | 208 | reqenv['swift.authorize'] = local_authorize | 231 | reqenv['swift.authorize'] = local_authorize |
649 | 209 | result = ''.join(local_auth(reqenv, lambda x, y: None)) | 232 | result = ''.join(local_authz(reqenv, lambda x, y: None)) |
650 | 210 | self.assert_(result.startswith('204'), result) | 233 | self.assert_(result.startswith('204'), result) |
651 | 211 | self.assertEquals(reqenv['swift.authorize'], local_authorize) | 234 | self.assertEquals(reqenv['swift.authorize'], local_authorize) |
652 | 212 | 235 | ||
653 | 236 | def test_auth_no_reseller_prefix_no_token_non_delegated(self): | ||
654 | 237 | # Ensure that in a non_delegated mode, no token gets denied | ||
655 | 238 | # right away. | ||
656 | 239 | local_app = FakeApp() | ||
657 | 240 | local_authn = \ | ||
658 | 241 | devauthn.filter_factory(\ | ||
659 | 242 | {'reseller_prefix': ''})(local_app) | ||
660 | 243 | reqenv = {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/account', | ||
661 | 244 | 'swift.cache': FakeMemcache()} | ||
662 | 245 | result = ''.join(local_authn(reqenv, lambda x, y: None)) | ||
663 | 246 | self.assert_(result.startswith('401'), result) | ||
664 | 247 | self.assert_(not local_app.i_was_called) | ||
665 | 248 | |||
666 | 213 | def test_auth_fail(self): | 249 | def test_auth_fail(self): |
668 | 214 | old_http_connect = auth.http_connect | 250 | old_http_connect = devauthn.http_connect |
669 | 215 | try: | 251 | try: |
672 | 216 | auth.http_connect = mock_http_connect(404) | 252 | devauthn.http_connect = mock_http_connect(404) |
673 | 217 | result = ''.join(self.test_auth({'REQUEST_METHOD': 'GET', | 253 | result = ''.join(self.test_authn({'REQUEST_METHOD': 'GET', |
674 | 218 | 'HTTP_X_AUTH_TOKEN': 'AUTH_t', 'swift.cache': FakeMemcache()}, | 254 | 'HTTP_X_AUTH_TOKEN': 'AUTH_t', 'swift.cache': FakeMemcache()}, |
675 | 219 | lambda x, y: None)) | 255 | lambda x, y: None)) |
676 | 220 | self.assert_(result.startswith('401'), result) | 256 | self.assert_(result.startswith('401'), result) |
677 | 221 | finally: | 257 | finally: |
679 | 222 | auth.http_connect = old_http_connect | 258 | devauthn.http_connect = old_http_connect |
680 | 223 | 259 | ||
681 | 224 | def test_auth_success(self): | 260 | def test_auth_success(self): |
683 | 225 | old_http_connect = auth.http_connect | 261 | old_http_connect = devauthn.http_connect |
684 | 226 | try: | 262 | try: |
686 | 227 | auth.http_connect = mock_http_connect(204, | 263 | devauthn.http_connect = mock_http_connect(204, |
687 | 228 | {'x-auth-ttl': '1234', 'x-auth-groups': 'act:usr,act,AUTH_cfa'}) | 264 | {'x-auth-ttl': '1234', 'x-auth-groups': 'act:usr,act,AUTH_cfa'}) |
689 | 229 | result = ''.join(self.test_auth({'REQUEST_METHOD': 'GET', | 265 | result = ''.join(self.test_authn({'REQUEST_METHOD': 'GET', |
690 | 230 | 'PATH_INFO': '/v/AUTH_cfa', 'HTTP_X_AUTH_TOKEN': 'AUTH_t', | 266 | 'PATH_INFO': '/v/AUTH_cfa', 'HTTP_X_AUTH_TOKEN': 'AUTH_t', |
691 | 231 | 'swift.cache': FakeMemcache()}, lambda x, y: None)) | 267 | 'swift.cache': FakeMemcache()}, lambda x, y: None)) |
692 | 232 | self.assert_(result.startswith('204'), result) | 268 | self.assert_(result.startswith('204'), result) |
693 | 233 | finally: | 269 | finally: |
695 | 234 | auth.http_connect = old_http_connect | 270 | devauthn.http_connect = old_http_connect |
696 | 235 | 271 | ||
697 | 236 | def test_auth_memcache(self): | 272 | def test_auth_memcache(self): |
699 | 237 | old_http_connect = auth.http_connect | 273 | old_http_connect = devauthn.http_connect |
700 | 238 | try: | 274 | try: |
701 | 239 | fake_memcache = FakeMemcache() | 275 | fake_memcache = FakeMemcache() |
703 | 240 | auth.http_connect = mock_http_connect(204, | 276 | devauthn.http_connect = mock_http_connect(204, |
704 | 241 | {'x-auth-ttl': '1234', 'x-auth-groups': 'act:usr,act,AUTH_cfa'}) | 277 | {'x-auth-ttl': '1234', 'x-auth-groups': 'act:usr,act,AUTH_cfa'}) |
706 | 242 | result = ''.join(self.test_auth({'REQUEST_METHOD': 'GET', | 278 | result = ''.join(self.test_authn({'REQUEST_METHOD': 'GET', |
707 | 243 | 'PATH_INFO': '/v/AUTH_cfa', 'HTTP_X_AUTH_TOKEN': 'AUTH_t', | 279 | 'PATH_INFO': '/v/AUTH_cfa', 'HTTP_X_AUTH_TOKEN': 'AUTH_t', |
708 | 244 | 'swift.cache': fake_memcache}, lambda x, y: None)) | 280 | 'swift.cache': fake_memcache}, lambda x, y: None)) |
709 | 245 | self.assert_(result.startswith('204'), result) | 281 | self.assert_(result.startswith('204'), result) |
711 | 246 | auth.http_connect = mock_http_connect(404) | 282 | devauthn.http_connect = mock_http_connect(404) |
712 | 247 | # Should still be in memcache | 283 | # Should still be in memcache |
714 | 248 | result = ''.join(self.test_auth({'REQUEST_METHOD': 'GET', | 284 | result = ''.join(self.test_authn({'REQUEST_METHOD': 'GET', |
715 | 249 | 'PATH_INFO': '/v/AUTH_cfa', 'HTTP_X_AUTH_TOKEN': 'AUTH_t', | 285 | 'PATH_INFO': '/v/AUTH_cfa', 'HTTP_X_AUTH_TOKEN': 'AUTH_t', |
716 | 250 | 'swift.cache': fake_memcache}, lambda x, y: None)) | 286 | 'swift.cache': fake_memcache}, lambda x, y: None)) |
717 | 251 | self.assert_(result.startswith('204'), result) | 287 | self.assert_(result.startswith('204'), result) |
718 | 252 | finally: | 288 | finally: |
720 | 253 | auth.http_connect = old_http_connect | 289 | devauthn.http_connect = old_http_connect |
721 | 254 | 290 | ||
722 | 255 | def test_auth_just_expired(self): | 291 | def test_auth_just_expired(self): |
724 | 256 | old_http_connect = auth.http_connect | 292 | old_http_connect = devauthn.http_connect |
725 | 257 | try: | 293 | try: |
726 | 258 | fake_memcache = FakeMemcache() | 294 | fake_memcache = FakeMemcache() |
728 | 259 | auth.http_connect = mock_http_connect(204, | 295 | devauthn.http_connect = mock_http_connect(204, |
729 | 260 | {'x-auth-ttl': '0', 'x-auth-groups': 'act:usr,act,AUTH_cfa'}) | 296 | {'x-auth-ttl': '0', 'x-auth-groups': 'act:usr,act,AUTH_cfa'}) |
731 | 261 | result = ''.join(self.test_auth({'REQUEST_METHOD': 'GET', | 297 | result = ''.join(self.test_authn({'REQUEST_METHOD': 'GET', |
732 | 262 | 'PATH_INFO': '/v/AUTH_cfa', 'HTTP_X_AUTH_TOKEN': 'AUTH_t', | 298 | 'PATH_INFO': '/v/AUTH_cfa', 'HTTP_X_AUTH_TOKEN': 'AUTH_t', |
733 | 263 | 'swift.cache': fake_memcache}, lambda x, y: None)) | 299 | 'swift.cache': fake_memcache}, lambda x, y: None)) |
734 | 264 | self.assert_(result.startswith('204'), result) | 300 | self.assert_(result.startswith('204'), result) |
736 | 265 | auth.http_connect = mock_http_connect(404) | 301 | devauthn.http_connect = mock_http_connect(404) |
737 | 266 | # Should still be in memcache, but expired | 302 | # Should still be in memcache, but expired |
739 | 267 | result = ''.join(self.test_auth({'REQUEST_METHOD': 'GET', | 303 | result = ''.join(self.test_authn({'REQUEST_METHOD': 'GET', |
740 | 268 | 'HTTP_X_AUTH_TOKEN': 'AUTH_t', 'swift.cache': fake_memcache}, | 304 | 'HTTP_X_AUTH_TOKEN': 'AUTH_t', 'swift.cache': fake_memcache}, |
741 | 269 | lambda x, y: None)) | 305 | lambda x, y: None)) |
742 | 270 | self.assert_(result.startswith('401'), result) | 306 | self.assert_(result.startswith('401'), result) |
743 | 271 | finally: | 307 | finally: |
745 | 272 | auth.http_connect = old_http_connect | 308 | devauthn.http_connect = old_http_connect |
746 | 273 | 309 | ||
747 | 274 | def test_middleware_success(self): | 310 | def test_middleware_success(self): |
749 | 275 | old_http_connect = auth.http_connect | 311 | old_http_connect = devauthn.http_connect |
750 | 276 | try: | 312 | try: |
752 | 277 | auth.http_connect = mock_http_connect(204, | 313 | devauthn.http_connect = mock_http_connect(204, |
753 | 278 | {'x-auth-ttl': '1234', 'x-auth-groups': 'act:usr,act,AUTH_cfa'}) | 314 | {'x-auth-ttl': '1234', 'x-auth-groups': 'act:usr,act,AUTH_cfa'}) |
754 | 279 | req = Request.blank('/v/AUTH_cfa/c/o', | 315 | req = Request.blank('/v/AUTH_cfa/c/o', |
755 | 280 | headers={'x-auth-token': 'AUTH_t'}) | 316 | headers={'x-auth-token': 'AUTH_t'}) |
756 | 281 | req.environ['swift.cache'] = FakeMemcache() | 317 | req.environ['swift.cache'] = FakeMemcache() |
758 | 282 | result = ''.join(self.test_auth(req.environ, start_response)) | 318 | result = ''.join(self.test_authn(req.environ, start_response)) |
759 | 283 | self.assert_(result.startswith('204'), result) | 319 | self.assert_(result.startswith('204'), result) |
761 | 284 | self.assertEquals(req.remote_user, 'act:usr,act,AUTH_cfa') | 320 | self.assertEquals(\ |
762 | 321 | req.environ['SWIFT_GROUPS'], 'act:usr,act,AUTH_cfa') | ||
763 | 285 | finally: | 322 | finally: |
765 | 286 | auth.http_connect = old_http_connect | 323 | devauthn.http_connect = old_http_connect |
766 | 287 | 324 | ||
767 | 288 | def test_middleware_no_header(self): | 325 | def test_middleware_no_header(self): |
769 | 289 | old_http_connect = auth.http_connect | 326 | old_http_connect = devauthn.http_connect |
770 | 290 | try: | 327 | try: |
772 | 291 | auth.http_connect = mock_http_connect(204, | 328 | devauthn.http_connect = mock_http_connect(204, |
773 | 292 | {'x-auth-ttl': '1234', 'x-auth-groups': 'act:usr,act,AUTH_cfa'}) | 329 | {'x-auth-ttl': '1234', 'x-auth-groups': 'act:usr,act,AUTH_cfa'}) |
774 | 293 | req = Request.blank('/v/AUTH_cfa/c/o') | 330 | req = Request.blank('/v/AUTH_cfa/c/o') |
775 | 294 | req.environ['swift.cache'] = FakeMemcache() | 331 | req.environ['swift.cache'] = FakeMemcache() |
777 | 295 | result = ''.join(self.test_auth(req.environ, start_response)) | 332 | result = ''.join(self.test_authn(req.environ, start_response)) |
778 | 296 | self.assert_(result.startswith('401'), result) | 333 | self.assert_(result.startswith('401'), result) |
779 | 297 | self.assert_(not req.remote_user, req.remote_user) | 334 | self.assert_(not req.remote_user, req.remote_user) |
780 | 298 | finally: | 335 | finally: |
782 | 299 | auth.http_connect = old_http_connect | 336 | devauthn.http_connect = old_http_connect |
783 | 300 | 337 | ||
784 | 301 | def test_middleware_storage_token(self): | 338 | def test_middleware_storage_token(self): |
786 | 302 | old_http_connect = auth.http_connect | 339 | old_http_connect = devauthn.http_connect |
787 | 303 | try: | 340 | try: |
789 | 304 | auth.http_connect = mock_http_connect(204, | 341 | devauthn.http_connect = mock_http_connect(204, |
790 | 305 | {'x-auth-ttl': '1234', 'x-auth-groups': 'act:usr,act,AUTH_cfa'}) | 342 | {'x-auth-ttl': '1234', 'x-auth-groups': 'act:usr,act,AUTH_cfa'}) |
791 | 306 | req = Request.blank('/v/AUTH_cfa/c/o', | 343 | req = Request.blank('/v/AUTH_cfa/c/o', |
792 | 307 | headers={'x-storage-token': 'AUTH_t'}) | 344 | headers={'x-storage-token': 'AUTH_t'}) |
793 | 308 | req.environ['swift.cache'] = FakeMemcache() | 345 | req.environ['swift.cache'] = FakeMemcache() |
795 | 309 | result = ''.join(self.test_auth(req.environ, start_response)) | 346 | result = ''.join(self.test_authn(req.environ, start_response)) |
796 | 310 | self.assert_(result.startswith('204'), result) | 347 | self.assert_(result.startswith('204'), result) |
798 | 311 | self.assertEquals(req.remote_user, 'act:usr,act,AUTH_cfa') | 348 | self.assertEquals(\ |
799 | 349 | req.environ['SWIFT_GROUPS'], 'act:usr,act,AUTH_cfa') | ||
800 | 312 | finally: | 350 | finally: |
802 | 313 | auth.http_connect = old_http_connect | 351 | devauthn.http_connect = old_http_connect |
803 | 314 | 352 | ||
804 | 315 | def test_authorize_bad_path(self): | 353 | def test_authorize_bad_path(self): |
805 | 316 | req = Request.blank('/badpath') | 354 | req = Request.blank('/badpath') |
807 | 317 | resp = self.test_auth.authorize(req) | 355 | resp = self.test_authz.dev_authorize(req) |
808 | 318 | self.assertEquals(resp and resp.status_int, 401) | 356 | self.assertEquals(resp and resp.status_int, 401) |
809 | 319 | req = Request.blank('/badpath') | 357 | req = Request.blank('/badpath') |
810 | 320 | req.remote_user = 'act:usr,act,AUTH_cfa' | 358 | req.remote_user = 'act:usr,act,AUTH_cfa' |
812 | 321 | resp = self.test_auth.authorize(req) | 359 | resp = self.test_authz.dev_authorize(req) |
813 | 322 | self.assertEquals(resp and resp.status_int, 403) | 360 | self.assertEquals(resp and resp.status_int, 403) |
814 | 323 | req = Request.blank('') | 361 | req = Request.blank('') |
816 | 324 | resp = self.test_auth.authorize(req) | 362 | resp = self.test_authz.dev_authorize(req) |
817 | 325 | self.assertEquals(resp and resp.status_int, 404) | 363 | self.assertEquals(resp and resp.status_int, 404) |
818 | 326 | req = Request.blank('') | 364 | req = Request.blank('') |
819 | 327 | req.environ['swift.cache'] = FakeMemcache() | 365 | req.environ['swift.cache'] = FakeMemcache() |
821 | 328 | result = ''.join(self.test_auth(req.environ, lambda x, y: None)) | 366 | result = ''.join(self.test_authz(req.environ, lambda x, y: None)) |
822 | 329 | self.assert_(result.startswith('404'), result) | 367 | self.assert_(result.startswith('404'), result) |
823 | 330 | 368 | ||
824 | 331 | def test_authorize_account_access(self): | 369 | def test_authorize_account_access(self): |
825 | 332 | req = Request.blank('/v1/AUTH_cfa') | 370 | req = Request.blank('/v1/AUTH_cfa') |
826 | 333 | req.remote_user = 'act:usr,act,AUTH_cfa' | 371 | req.remote_user = 'act:usr,act,AUTH_cfa' |
828 | 334 | self.assertEquals(self.test_auth.authorize(req), None) | 372 | self.assertEquals(self.test_authz.dev_authorize(req), None) |
829 | 335 | req = Request.blank('/v1/AUTH_cfa') | 373 | req = Request.blank('/v1/AUTH_cfa') |
830 | 336 | req.remote_user = 'act:usr,act' | 374 | req.remote_user = 'act:usr,act' |
832 | 337 | resp = self.test_auth.authorize(req) | 375 | resp = self.test_authz.dev_authorize(req) |
833 | 338 | self.assertEquals(resp and resp.status_int, 403) | 376 | self.assertEquals(resp and resp.status_int, 403) |
834 | 339 | 377 | ||
835 | 340 | def test_authorize_acl_group_access(self): | 378 | def test_authorize_acl_group_access(self): |
836 | 341 | req = Request.blank('/v1/AUTH_cfa') | 379 | req = Request.blank('/v1/AUTH_cfa') |
837 | 342 | req.remote_user = 'act:usr,act' | 380 | req.remote_user = 'act:usr,act' |
839 | 343 | resp = self.test_auth.authorize(req) | 381 | resp = self.test_authz.dev_authorize(req) |
840 | 344 | self.assertEquals(resp and resp.status_int, 403) | 382 | self.assertEquals(resp and resp.status_int, 403) |
841 | 345 | req = Request.blank('/v1/AUTH_cfa') | 383 | req = Request.blank('/v1/AUTH_cfa') |
842 | 346 | req.remote_user = 'act:usr,act' | 384 | req.remote_user = 'act:usr,act' |
843 | 347 | req.acl = 'act' | 385 | req.acl = 'act' |
845 | 348 | self.assertEquals(self.test_auth.authorize(req), None) | 386 | self.assertEquals(self.test_authz.dev_authorize(req), None) |
846 | 349 | req = Request.blank('/v1/AUTH_cfa') | 387 | req = Request.blank('/v1/AUTH_cfa') |
847 | 350 | req.remote_user = 'act:usr,act' | 388 | req.remote_user = 'act:usr,act' |
848 | 351 | req.acl = 'act:usr' | 389 | req.acl = 'act:usr' |
850 | 352 | self.assertEquals(self.test_auth.authorize(req), None) | 390 | self.assertEquals(self.test_authz.dev_authorize(req), None) |
851 | 353 | req = Request.blank('/v1/AUTH_cfa') | 391 | req = Request.blank('/v1/AUTH_cfa') |
852 | 354 | req.remote_user = 'act:usr,act' | 392 | req.remote_user = 'act:usr,act' |
853 | 355 | req.acl = 'act2' | 393 | req.acl = 'act2' |
855 | 356 | resp = self.test_auth.authorize(req) | 394 | resp = self.test_authz.dev_authorize(req) |
856 | 357 | self.assertEquals(resp and resp.status_int, 403) | 395 | self.assertEquals(resp and resp.status_int, 403) |
857 | 358 | req = Request.blank('/v1/AUTH_cfa') | 396 | req = Request.blank('/v1/AUTH_cfa') |
858 | 359 | req.remote_user = 'act:usr,act' | 397 | req.remote_user = 'act:usr,act' |
859 | 360 | req.acl = 'act:usr2' | 398 | req.acl = 'act:usr2' |
861 | 361 | resp = self.test_auth.authorize(req) | 399 | resp = self.test_authz.dev_authorize(req) |
862 | 362 | self.assertEquals(resp and resp.status_int, 403) | 400 | self.assertEquals(resp and resp.status_int, 403) |
863 | 363 | 401 | ||
864 | 364 | def test_deny_cross_reseller(self): | 402 | def test_deny_cross_reseller(self): |
865 | @@ -366,96 +404,96 @@ | |||
866 | 366 | req = Request.blank('/v1/OTHER_cfa') | 404 | req = Request.blank('/v1/OTHER_cfa') |
867 | 367 | req.remote_user = 'act:usr,act,AUTH_cfa' | 405 | req.remote_user = 'act:usr,act,AUTH_cfa' |
868 | 368 | req.acl = 'act' | 406 | req.acl = 'act' |
870 | 369 | resp = self.test_auth.authorize(req) | 407 | resp = self.test_authz.dev_authorize(req) |
871 | 370 | self.assertEquals(resp and resp.status_int, 403) | 408 | self.assertEquals(resp and resp.status_int, 403) |
872 | 371 | 409 | ||
873 | 372 | def test_authorize_acl_referrer_access(self): | 410 | def test_authorize_acl_referrer_access(self): |
874 | 373 | req = Request.blank('/v1/AUTH_cfa') | 411 | req = Request.blank('/v1/AUTH_cfa') |
875 | 374 | req.remote_user = 'act:usr,act' | 412 | req.remote_user = 'act:usr,act' |
906 | 375 | resp = self.test_auth.authorize(req) | 413 | resp = self.test_authz.dev_authorize(req) |
907 | 376 | self.assertEquals(resp and resp.status_int, 403) | 414 | self.assertEquals(resp and resp.status_int, 403) |
908 | 377 | req = Request.blank('/v1/AUTH_cfa') | 415 | req = Request.blank('/v1/AUTH_cfa') |
909 | 378 | req.remote_user = 'act:usr,act' | 416 | req.remote_user = 'act:usr,act' |
910 | 379 | req.acl = '.r:*' | 417 | req.acl = '.r:*' |
911 | 380 | self.assertEquals(self.test_auth.authorize(req), None) | 418 | self.assertEquals(self.test_authz.dev_authorize(req), None) |
912 | 381 | req = Request.blank('/v1/AUTH_cfa') | 419 | req = Request.blank('/v1/AUTH_cfa') |
913 | 382 | req.remote_user = 'act:usr,act' | 420 | req.remote_user = 'act:usr,act' |
914 | 383 | req.acl = '.r:.example.com' | 421 | req.acl = '.r:.example.com' |
915 | 384 | resp = self.test_auth.authorize(req) | 422 | resp = self.test_authz.dev_authorize(req) |
916 | 385 | self.assertEquals(resp and resp.status_int, 403) | 423 | self.assertEquals(resp and resp.status_int, 403) |
917 | 386 | req = Request.blank('/v1/AUTH_cfa') | 424 | req = Request.blank('/v1/AUTH_cfa') |
918 | 387 | req.remote_user = 'act:usr,act' | 425 | req.remote_user = 'act:usr,act' |
919 | 388 | req.referer = 'http://www.example.com/index.html' | 426 | req.referer = 'http://www.example.com/index.html' |
920 | 389 | req.acl = '.r:.example.com' | 427 | req.acl = '.r:.example.com' |
921 | 390 | self.assertEquals(self.test_auth.authorize(req), None) | 428 | self.assertEquals(self.test_authz.dev_authorize(req), None) |
922 | 391 | req = Request.blank('/v1/AUTH_cfa') | 429 | req = Request.blank('/v1/AUTH_cfa') |
923 | 392 | resp = self.test_auth.authorize(req) | 430 | resp = self.test_authz.dev_authorize(req) |
924 | 393 | self.assertEquals(resp and resp.status_int, 401) | 431 | self.assertEquals(resp and resp.status_int, 401) |
925 | 394 | req = Request.blank('/v1/AUTH_cfa') | 432 | req = Request.blank('/v1/AUTH_cfa') |
926 | 395 | req.acl = '.r:*' | 433 | req.acl = '.r:*' |
927 | 396 | self.assertEquals(self.test_auth.authorize(req), None) | 434 | self.assertEquals(self.test_authz.dev_authorize(req), None) |
928 | 397 | req = Request.blank('/v1/AUTH_cfa') | 435 | req = Request.blank('/v1/AUTH_cfa') |
929 | 398 | req.acl = '.r:.example.com' | 436 | req.acl = '.r:.example.com' |
930 | 399 | resp = self.test_auth.authorize(req) | 437 | resp = self.test_authz.dev_authorize(req) |
931 | 400 | self.assertEquals(resp and resp.status_int, 401) | 438 | self.assertEquals(resp and resp.status_int, 401) |
932 | 401 | req = Request.blank('/v1/AUTH_cfa') | 439 | req = Request.blank('/v1/AUTH_cfa') |
933 | 402 | req.referer = 'http://www.example.com/index.html' | 440 | req.referer = 'http://www.example.com/index.html' |
934 | 403 | req.acl = '.r:.example.com' | 441 | req.acl = '.r:.example.com' |
935 | 404 | self.assertEquals(self.test_auth.authorize(req), None) | 442 | self.assertEquals(self.test_authz.dev_authorize(req), None) |
936 | 405 | 443 | ||
937 | 406 | def test_account_put_permissions(self): | 444 | def test_account_put_permissions(self): |
938 | 407 | req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'}) | 445 | req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'}) |
939 | 408 | req.remote_user = 'act:usr,act' | 446 | req.remote_user = 'act:usr,act' |
941 | 409 | resp = self.test_auth.authorize(req) | 447 | resp = self.test_authz.dev_authorize(req) |
942 | 410 | self.assertEquals(resp and resp.status_int, 403) | 448 | self.assertEquals(resp and resp.status_int, 403) |
943 | 411 | 449 | ||
944 | 412 | req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'}) | 450 | req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'}) |
945 | 413 | req.remote_user = 'act:usr,act,AUTH_other' | 451 | req.remote_user = 'act:usr,act,AUTH_other' |
947 | 414 | resp = self.test_auth.authorize(req) | 452 | resp = self.test_authz.dev_authorize(req) |
948 | 415 | self.assertEquals(resp and resp.status_int, 403) | 453 | self.assertEquals(resp and resp.status_int, 403) |
949 | 416 | 454 | ||
950 | 417 | # Even PUTs to your own account as account admin should fail | 455 | # Even PUTs to your own account as account admin should fail |
951 | 418 | req = Request.blank('/v1/AUTH_old', environ={'REQUEST_METHOD': 'PUT'}) | 456 | req = Request.blank('/v1/AUTH_old', environ={'REQUEST_METHOD': 'PUT'}) |
952 | 419 | req.remote_user = 'act:usr,act,AUTH_old' | 457 | req.remote_user = 'act:usr,act,AUTH_old' |
954 | 420 | resp = self.test_auth.authorize(req) | 458 | resp = self.test_authz.dev_authorize(req) |
955 | 421 | self.assertEquals(resp and resp.status_int, 403) | 459 | self.assertEquals(resp and resp.status_int, 403) |
956 | 422 | 460 | ||
957 | 423 | req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'}) | 461 | req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'}) |
958 | 424 | req.remote_user = 'act:usr,act,.reseller_admin' | 462 | req.remote_user = 'act:usr,act,.reseller_admin' |
960 | 425 | resp = self.test_auth.authorize(req) | 463 | resp = self.test_authz.dev_authorize(req) |
961 | 426 | self.assertEquals(resp, None) | 464 | self.assertEquals(resp, None) |
962 | 427 | 465 | ||
963 | 428 | # .super_admin is not something the middleware should ever see or care | 466 | # .super_admin is not something the middleware should ever see or care |
964 | 429 | # about | 467 | # about |
965 | 430 | req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'}) | 468 | req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'}) |
966 | 431 | req.remote_user = 'act:usr,act,.super_admin' | 469 | req.remote_user = 'act:usr,act,.super_admin' |
968 | 432 | resp = self.test_auth.authorize(req) | 470 | resp = self.test_authz.dev_authorize(req) |
969 | 433 | self.assertEquals(resp and resp.status_int, 403) | 471 | self.assertEquals(resp and resp.status_int, 403) |
970 | 434 | 472 | ||
971 | 435 | def test_account_delete_permissions(self): | 473 | def test_account_delete_permissions(self): |
972 | 436 | req = Request.blank('/v1/AUTH_new', | 474 | req = Request.blank('/v1/AUTH_new', |
973 | 437 | environ={'REQUEST_METHOD': 'DELETE'}) | 475 | environ={'REQUEST_METHOD': 'DELETE'}) |
974 | 438 | req.remote_user = 'act:usr,act' | 476 | req.remote_user = 'act:usr,act' |
976 | 439 | resp = self.test_auth.authorize(req) | 477 | resp = self.test_authz.dev_authorize(req) |
977 | 440 | self.assertEquals(resp and resp.status_int, 403) | 478 | self.assertEquals(resp and resp.status_int, 403) |
978 | 441 | 479 | ||
979 | 442 | req = Request.blank('/v1/AUTH_new', | 480 | req = Request.blank('/v1/AUTH_new', |
980 | 443 | environ={'REQUEST_METHOD': 'DELETE'}) | 481 | environ={'REQUEST_METHOD': 'DELETE'}) |
981 | 444 | req.remote_user = 'act:usr,act,AUTH_other' | 482 | req.remote_user = 'act:usr,act,AUTH_other' |
983 | 445 | resp = self.test_auth.authorize(req) | 483 | resp = self.test_authz.dev_authorize(req) |
984 | 446 | self.assertEquals(resp and resp.status_int, 403) | 484 | self.assertEquals(resp and resp.status_int, 403) |
985 | 447 | 485 | ||
986 | 448 | # Even DELETEs to your own account as account admin should fail | 486 | # Even DELETEs to your own account as account admin should fail |
987 | 449 | req = Request.blank('/v1/AUTH_old', | 487 | req = Request.blank('/v1/AUTH_old', |
988 | 450 | environ={'REQUEST_METHOD': 'DELETE'}) | 488 | environ={'REQUEST_METHOD': 'DELETE'}) |
989 | 451 | req.remote_user = 'act:usr,act,AUTH_old' | 489 | req.remote_user = 'act:usr,act,AUTH_old' |
991 | 452 | resp = self.test_auth.authorize(req) | 490 | resp = self.test_authz.dev_authorize(req) |
992 | 453 | self.assertEquals(resp and resp.status_int, 403) | 491 | self.assertEquals(resp and resp.status_int, 403) |
993 | 454 | 492 | ||
994 | 455 | req = Request.blank('/v1/AUTH_new', | 493 | req = Request.blank('/v1/AUTH_new', |
995 | 456 | environ={'REQUEST_METHOD': 'DELETE'}) | 494 | environ={'REQUEST_METHOD': 'DELETE'}) |
996 | 457 | req.remote_user = 'act:usr,act,.reseller_admin' | 495 | req.remote_user = 'act:usr,act,.reseller_admin' |
998 | 458 | resp = self.test_auth.authorize(req) | 496 | resp = self.test_authz.dev_authorize(req) |
999 | 459 | self.assertEquals(resp, None) | 497 | self.assertEquals(resp, None) |
1000 | 460 | 498 | ||
1001 | 461 | # .super_admin is not something the middleware should ever see or care | 499 | # .super_admin is not something the middleware should ever see or care |
1002 | @@ -463,7 +501,7 @@ | |||
1003 | 463 | req = Request.blank('/v1/AUTH_new', | 501 | req = Request.blank('/v1/AUTH_new', |
1004 | 464 | environ={'REQUEST_METHOD': 'DELETE'}) | 502 | environ={'REQUEST_METHOD': 'DELETE'}) |
1005 | 465 | req.remote_user = 'act:usr,act,.super_admin' | 503 | req.remote_user = 'act:usr,act,.super_admin' |
1007 | 466 | resp = self.test_auth.authorize(req) | 504 | resp = self.test_authz.dev_authorize(req) |
1008 | 467 | self.assertEquals(resp and resp.status_int, 403) | 505 | self.assertEquals(resp and resp.status_int, 403) |
1009 | 468 | 506 | ||
1010 | 469 | 507 |
> self.response_ status = status
self.response_ status is storing per-request info in a shared object. That could be wrong under concurrency.
> ''.join( response_ itr) content_ length = sum(map(len, response.app_iter))
> return [resp.read()]
> response.
Storing many GB of data in RAM is a bad idea.
> usersConfig. readfp( open('/ etc/openstack/ users.ini' ))
You shouldn't re-read and parse the basic auth credentials file on every request. The local imports there are kind of ugly too.
> def validateCreds(self, username, password):
only major pep8 violation I see is those camel caps.
> - 'auth=swift. auth.server: app_factory' , swift.auth. server: app_factory' , auth.basicauth: app_factory' ,
> + #'auth=
> + 'auth=swift.
Was leaving it this way a mistake? The basic auth isn't super useful right now, and removing the devauth filter completely breaks our dev and testing environments.