Merge lp:~gholt/swift/authlock into lp:~hudson-openstack/swift/trunk
- authlock
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Chuck Thier | ||||
Approved revision: | 73 | ||||
Merged at revision: | 72 | ||||
Proposed branch: | lp:~gholt/swift/authlock | ||||
Merge into: | lp:~hudson-openstack/swift/trunk | ||||
Diff against target: |
1818 lines (+810/-240) 16 files modified
bin/swift-auth-add-user (+15/-1) bin/swift-auth-recreate-accounts (+20/-8) doc/source/development_auth.rst (+7/-4) doc/source/development_saio.rst (+8/-4) doc/source/howto_cyberduck.rst (+27/-10) etc/auth-server.conf-sample (+2/-0) swift/auth/server.py (+127/-81) swift/common/constraints.py (+2/-0) swift/common/middleware/auth.py (+8/-3) swift/proxy/server.py (+56/-4) test/functional/swift.py (+1/-1) test/functional/tests.py (+1/-1) test/probe/common.py (+14/-2) test/unit/auth/test_server.py (+403/-79) test/unit/common/middleware/test_auth.py (+31/-0) test/unit/proxy/test_server.py (+88/-42) |
||||
To merge this branch: | bzr merge lp:~gholt/swift/authlock | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Chuck Thier (community) | Approve | ||
Review via email: mp+35165@code.launchpad.net |
Commit message
Locking down the DevAuth by adding support for a super admin and reseller admins.
Description of the change
Locking down the DevAuth:
There are two new classes of users, super admin and reseller admin, for a total of four now:
user
Standard user, only has access to containers if they are specifically listed in the containers' ACLs.
account admin
Has full access within a single account. Can also create additional standard users and account admins.
reseller admin
Is treated as an account admin for all accounts with the reseller's prefix. Can also create additional account admins for any account with the reseller's prefix. Cannot create additional reseller admins.
super admin
Has the same access as the reseller admin and can create additional reseller admins. Also can issue a "recreate accounts" call.
Tool changes:
swift-auth-
Accepts new options: -r to make the user a reseller admin, -U and -K to define the admin issuing the request. Simplest change to existing usage: "swift-
swift-auth-
Refactored to use optparse. Accepts new options: -c for the auth-server.conf location, -U -K to define the admin issuing the request (must be super admin at the moment). Simplest change to existing usage: "swift-
Code changes:
swift/auth/
No longer needs the account ring or access to backend account servers; instead it creates accounts through a normal PUT request to the proxy server. So now it can be running anywhere and actually work with multiple clusters if needed.
Requires a super_admin_key be set in the conf file, allowing the permission set indicated above.
Supports creating reseller admins.
Everything is locked down as specified above.
Can return a special group named .reseller_admin to the auth middleware, indicating that account PUTs are allowed by the user.
swift/common/
Just changed to lockdown the new account PUT facility to just .reseller_admin and to treat .reseller_admin as also an account admin.
swift/proxy/
Supports account PUTs. Moved MAX_CONTAINER_
And, of course, doc and test updates.
gholt (gholt) wrote : | # |
Preview Diff
1 | === modified file 'bin/swift-auth-add-user' | |||
2 | --- bin/swift-auth-add-user 2010-09-06 02:53:08 +0000 | |||
3 | +++ bin/swift-auth-add-user 2010-09-12 00:25:58 +0000 | |||
4 | @@ -33,6 +33,16 @@ | |||
5 | 33 | default=False, help='Give the user administrator access; otherwise ' | 33 | default=False, help='Give the user administrator access; otherwise ' |
6 | 34 | 'the user will only have access to containers specifically allowed ' | 34 | 'the user will only have access to containers specifically allowed ' |
7 | 35 | 'with ACLs.') | 35 | 'with ACLs.') |
8 | 36 | parser.add_option('-r', '--reseller-admin', dest='reseller_admin', | ||
9 | 37 | action='store_true', default=False, help='Give the user full reseller ' | ||
10 | 38 | 'administrator access, giving them full access to all accounts within ' | ||
11 | 39 | 'the reseller, including the ability to create new accounts. Creating ' | ||
12 | 40 | 'a new reseller admin requires super_admin rights.') | ||
13 | 41 | parser.add_option('-U', '--admin-user', dest='admin_user', | ||
14 | 42 | default='.super_admin', help='The user with admin rights to add users ' | ||
15 | 43 | '(default: .super_admin).') | ||
16 | 44 | parser.add_option('-K', '--admin-key', dest='admin_key', | ||
17 | 45 | help='The key for the user with admin rights to add users.') | ||
18 | 36 | args = argv[1:] | 46 | args = argv[1:] |
19 | 37 | if not args: | 47 | if not args: |
20 | 38 | args.append('-h') | 48 | args.append('-h') |
21 | @@ -48,9 +58,13 @@ | |||
22 | 48 | port = int(conf.get('bind_port', 11000)) | 58 | port = int(conf.get('bind_port', 11000)) |
23 | 49 | ssl = conf.get('cert_file') is not None | 59 | ssl = conf.get('cert_file') is not None |
24 | 50 | path = '/account/%s/%s' % (account, user) | 60 | path = '/account/%s/%s' % (account, user) |
26 | 51 | headers = {'X-Auth-User-Key': password} | 61 | headers = {'X-Auth-Admin-User': options.admin_user, |
27 | 62 | 'X-Auth-Admin-Key': options.admin_key, | ||
28 | 63 | 'X-Auth-User-Key': password} | ||
29 | 52 | if options.admin: | 64 | if options.admin: |
30 | 53 | headers['X-Auth-User-Admin'] = 'true' | 65 | headers['X-Auth-User-Admin'] = 'true' |
31 | 66 | if options.reseller_admin: | ||
32 | 67 | headers['X-Auth-User-Reseller-Admin'] = 'true' | ||
33 | 54 | conn = http_connect(host, port, 'PUT', path, headers, ssl=ssl) | 68 | conn = http_connect(host, port, 'PUT', path, headers, ssl=ssl) |
34 | 55 | resp = conn.getresponse() | 69 | resp = conn.getresponse() |
35 | 56 | if resp.status == 204: | 70 | if resp.status == 204: |
36 | 57 | 71 | ||
37 | === modified file 'bin/swift-auth-recreate-accounts' | |||
38 | --- bin/swift-auth-recreate-accounts 2010-08-20 00:50:12 +0000 | |||
39 | +++ bin/swift-auth-recreate-accounts 2010-09-12 00:25:58 +0000 | |||
40 | @@ -15,25 +15,37 @@ | |||
41 | 15 | # limitations under the License. | 15 | # limitations under the License. |
42 | 16 | 16 | ||
43 | 17 | from ConfigParser import ConfigParser | 17 | from ConfigParser import ConfigParser |
44 | 18 | from optparse import OptionParser | ||
45 | 18 | from sys import argv, exit | 19 | from sys import argv, exit |
46 | 19 | 20 | ||
47 | 20 | from swift.common.bufferedhttp import http_connect_raw as http_connect | 21 | from swift.common.bufferedhttp import http_connect_raw as http_connect |
48 | 21 | 22 | ||
49 | 22 | if __name__ == '__main__': | 23 | if __name__ == '__main__': |
55 | 23 | f = '/etc/swift/auth-server.conf' | 24 | default_conf = '/etc/swift/auth-server.conf' |
56 | 24 | if len(argv) == 2: | 25 | parser = OptionParser(usage='Usage: %prog [options]') |
57 | 25 | f = argv[1] | 26 | parser.add_option('-c', '--conf', dest='conf', default=default_conf, |
58 | 26 | elif len(argv) != 1: | 27 | help='Configuration file to determine how to connect to the local ' |
59 | 27 | exit('Syntax: %s [conf_file]' % argv[0]) | 28 | 'auth server (default: %s).' % default_conf) |
60 | 29 | parser.add_option('-U', '--admin-user', dest='admin_user', | ||
61 | 30 | default='.super_admin', help='The user with admin rights to recreate ' | ||
62 | 31 | 'accounts (default: .super_admin).') | ||
63 | 32 | parser.add_option('-K', '--admin-key', dest='admin_key', | ||
64 | 33 | help='The key for the user with admin rights to recreate accounts.') | ||
65 | 34 | args = argv[1:] | ||
66 | 35 | if not args: | ||
67 | 36 | args.append('-h') | ||
68 | 37 | (options, args) = parser.parse_args(args) | ||
69 | 28 | c = ConfigParser() | 38 | c = ConfigParser() |
72 | 29 | if not c.read(f): | 39 | if not c.read(options.conf): |
73 | 30 | exit('Unable to read conf file: %s' % f) | 40 | exit('Unable to read conf file: %s' % options.conf) |
74 | 31 | conf = dict(c.items('app:auth-server')) | 41 | conf = dict(c.items('app:auth-server')) |
75 | 32 | host = conf.get('bind_ip', '127.0.0.1') | 42 | host = conf.get('bind_ip', '127.0.0.1') |
76 | 33 | port = int(conf.get('bind_port', 11000)) | 43 | port = int(conf.get('bind_port', 11000)) |
77 | 34 | ssl = conf.get('cert_file') is not None | 44 | ssl = conf.get('cert_file') is not None |
78 | 35 | path = '/recreate_accounts' | 45 | path = '/recreate_accounts' |
80 | 36 | conn = http_connect(host, port, 'POST', path, ssl=ssl) | 46 | conn = http_connect(host, port, 'POST', path, ssl=ssl, |
81 | 47 | headers={'X-Auth-Admin-User': options.admin_user, | ||
82 | 48 | 'X-Auth-Admin-Key': options.admin_key}) | ||
83 | 37 | resp = conn.getresponse() | 49 | resp = conn.getresponse() |
84 | 38 | if resp.status == 200: | 50 | if resp.status == 200: |
85 | 39 | print resp.read() | 51 | print resp.read() |
86 | 40 | 52 | ||
87 | === modified file 'doc/source/development_auth.rst' | |||
88 | --- doc/source/development_auth.rst 2010-09-09 17:24:25 +0000 | |||
89 | +++ doc/source/development_auth.rst 2010-09-12 00:25:58 +0000 | |||
90 | @@ -6,10 +6,13 @@ | |||
91 | 6 | Creating Your Own Auth Server and Middleware | 6 | Creating Your Own Auth Server and Middleware |
92 | 7 | -------------------------------------------- | 7 | -------------------------------------------- |
93 | 8 | 8 | ||
98 | 9 | The included swift/common/middleware/auth.py is a good minimal example of how | 9 | The included swift/auth/server.py and swift/common/middleware/auth.py are good |
99 | 10 | to create auth middleware. The main points are that the auth middleware can | 10 | minimal examples of how to create an external auth server and proxy server auth |
100 | 11 | reject requests up front, before they ever get to the Swift Proxy application, | 11 | middleware. Also, see the `Swauth <https://launchpad.net/swauth>`_ project for |
101 | 12 | and afterwards when the proxy issues callbacks to verify authorization. | 12 | a more complete implementation. The main points are that the auth middleware |
102 | 13 | can reject requests up front, before they ever get to the Swift Proxy | ||
103 | 14 | application, and afterwards when the proxy issues callbacks to verify | ||
104 | 15 | authorization. | ||
105 | 13 | 16 | ||
106 | 14 | It's generally good to separate the authentication and authorization | 17 | It's generally good to separate the authentication and authorization |
107 | 15 | procedures. Authentication verifies that a request actually comes from who it | 18 | procedures. Authentication verifies that a request actually comes from who it |
108 | 16 | 19 | ||
109 | === modified file 'doc/source/development_saio.rst' | |||
110 | --- doc/source/development_saio.rst 2010-09-06 02:53:08 +0000 | |||
111 | +++ doc/source/development_saio.rst 2010-09-12 00:25:58 +0000 | |||
112 | @@ -177,6 +177,8 @@ | |||
113 | 177 | [app:auth-server] | 177 | [app:auth-server] |
114 | 178 | use = egg:swift#auth | 178 | use = egg:swift#auth |
115 | 179 | default_cluster_url = http://127.0.0.1:8080/v1 | 179 | default_cluster_url = http://127.0.0.1:8080/v1 |
116 | 180 | # Highly recommended to change this. | ||
117 | 181 | super_admin_key = devauth | ||
118 | 180 | 182 | ||
119 | 181 | #. Create `/etc/swift/proxy-server.conf`:: | 183 | #. Create `/etc/swift/proxy-server.conf`:: |
120 | 182 | 184 | ||
121 | @@ -511,7 +513,9 @@ | |||
122 | 511 | 513 | ||
123 | 512 | #!/bin/bash | 514 | #!/bin/bash |
124 | 513 | 515 | ||
126 | 514 | swift-auth-recreate-accounts | 516 | # Replace devauth with whatever your super_admin key is (recorded in |
127 | 517 | # /etc/swift/auth-server.conf). | ||
128 | 518 | swift-auth-recreate-accounts -K devauth | ||
129 | 515 | swift-init object-updater start | 519 | swift-init object-updater start |
130 | 516 | swift-init container-updater start | 520 | swift-init container-updater start |
131 | 517 | swift-init object-replicator start | 521 | swift-init object-replicator start |
132 | @@ -526,12 +530,12 @@ | |||
133 | 526 | #. `remakerings` | 530 | #. `remakerings` |
134 | 527 | #. `cd ~/swift/trunk; ./.unittests` | 531 | #. `cd ~/swift/trunk; ./.unittests` |
135 | 528 | #. `startmain` (The ``Unable to increase file descriptor limit. Running as non-root?`` warnings are expected and ok.) | 532 | #. `startmain` (The ``Unable to increase file descriptor limit. Running as non-root?`` warnings are expected and ok.) |
137 | 529 | #. `swift-auth-add-user --admin test tester testing` | 533 | #. `swift-auth-add-user -K devauth -a test tester testing` # Replace ``devauth`` with whatever your super_admin key is (recorded in /etc/swift/auth-server.conf). |
138 | 530 | #. Get an `X-Storage-Url` and `X-Auth-Token`: ``curl -v -H 'X-Storage-User: test:tester' -H 'X-Storage-Pass: testing' http://127.0.0.1:11000/v1.0`` | 534 | #. Get an `X-Storage-Url` and `X-Auth-Token`: ``curl -v -H 'X-Storage-User: test:tester' -H 'X-Storage-Pass: testing' http://127.0.0.1:11000/v1.0`` |
139 | 531 | #. Check that you can GET account: ``curl -v -H 'X-Auth-Token: <token-from-x-auth-token-above>' <url-from-x-storage-url-above>`` | 535 | #. Check that you can GET account: ``curl -v -H 'X-Auth-Token: <token-from-x-auth-token-above>' <url-from-x-storage-url-above>`` |
140 | 532 | #. Check that `st` works: `st -A http://127.0.0.1:11000/v1.0 -U test:tester -K testing stat` | 536 | #. Check that `st` works: `st -A http://127.0.0.1:11000/v1.0 -U test:tester -K testing stat` |
143 | 533 | #. `swift-auth-add-user --admin test2 tester2 testing2` | 537 | #. `swift-auth-add-user -K devauth -a test2 tester2 testing2` # Replace ``devauth`` with whatever your super_admin key is (recorded in /etc/swift/auth-server.conf). |
144 | 534 | #. `swift-auth-add-user test tester3 testing3` | 538 | #. `swift-auth-add-user -K devauth test tester3 testing3` # Replace ``devauth`` with whatever your super_admin key is (recorded in /etc/swift/auth-server.conf). |
145 | 535 | #. `cp ~/swift/trunk/test/functional/sample.conf /etc/swift/func_test.conf` | 539 | #. `cp ~/swift/trunk/test/functional/sample.conf /etc/swift/func_test.conf` |
146 | 536 | #. `cd ~/swift/trunk; ./.functests` (Note: functional tests will first delete | 540 | #. `cd ~/swift/trunk; ./.functests` (Note: functional tests will first delete |
147 | 537 | everything in the configured accounts.) | 541 | everything in the configured accounts.) |
148 | 538 | 542 | ||
149 | === modified file 'doc/source/howto_cyberduck.rst' | |||
150 | --- doc/source/howto_cyberduck.rst 2010-09-06 02:21:08 +0000 | |||
151 | +++ doc/source/howto_cyberduck.rst 2010-09-12 00:25:58 +0000 | |||
152 | @@ -90,26 +90,43 @@ | |||
153 | 90 | 90 | ||
154 | 91 | #. Example proxy-server config:: | 91 | #. Example proxy-server config:: |
155 | 92 | 92 | ||
159 | 93 | [proxy-server] | 93 | [DEFAULT] |
157 | 94 | bind_port = 8080 | ||
158 | 95 | user = swift | ||
160 | 96 | cert_file = /etc/swift/cert.crt | 94 | cert_file = /etc/swift/cert.crt |
161 | 97 | key_file = /etc/swift/cert.key | 95 | key_file = /etc/swift/cert.key |
164 | 98 | 96 | ||
165 | 99 | [auth-server] | 97 | [pipeline:main] |
166 | 98 | pipeline = healthcheck cache auth proxy-server | ||
167 | 99 | |||
168 | 100 | [app:proxy-server] | ||
169 | 101 | use = egg:swift#proxy | ||
170 | 102 | |||
171 | 103 | [filter:auth] | ||
172 | 104 | use = egg:swift#auth | ||
173 | 100 | ssl = true | 105 | ssl = true |
174 | 106 | |||
175 | 107 | [filter:healthcheck] | ||
176 | 108 | use = egg:swift#healthcheck | ||
177 | 109 | |||
178 | 110 | [filter:cache] | ||
179 | 111 | use = egg:swift#memcache | ||
180 | 101 | 112 | ||
181 | 102 | #. Example auth-server config:: | 113 | #. Example auth-server config:: |
182 | 103 | 114 | ||
184 | 104 | [auth-server] | 115 | [DEFAULT] |
185 | 116 | cert_file = /etc/swift/cert.crt | ||
186 | 117 | key_file = /etc/swift/cert.key | ||
187 | 118 | |||
188 | 119 | [pipeline:main] | ||
189 | 120 | pipeline = auth-server | ||
190 | 121 | |||
191 | 122 | [app:auth-server] | ||
192 | 123 | use = egg:swift#auth | ||
193 | 124 | super_admin_key = devauth | ||
194 | 105 | default_cluster_url = https://ec2-184-72-156-130.compute-1.amazonaws.com:8080/v1 | 125 | default_cluster_url = https://ec2-184-72-156-130.compute-1.amazonaws.com:8080/v1 |
195 | 106 | user = swift | ||
196 | 107 | cert_file = /etc/swift/cert.crt | ||
197 | 108 | key_file = /etc/swift/cert.key | ||
198 | 109 | 126 | ||
199 | 110 | #. Use swift-auth-add-user to create a new account and admin user:: | 127 | #. Use swift-auth-add-user to create a new account and admin user:: |
200 | 111 | 128 | ||
202 | 112 | ubuntu@domU-12-31-39-03-CD-06:/home/swift/swift/bin$ swift-auth-add-user --admin a3 b3 c3 | 129 | ubuntu@domU-12-31-39-03-CD-06:/home/swift/swift/bin$ swift-auth-add-user -K devauth -a a3 b3 c3 |
203 | 113 | https://ec2-184-72-156-130.compute-1.amazonaws.com:8080/v1/06228ccf-6d0a-4395-889e-e971e8de8781 | 130 | https://ec2-184-72-156-130.compute-1.amazonaws.com:8080/v1/06228ccf-6d0a-4395-889e-e971e8de8781 |
204 | 114 | 131 | ||
205 | 115 | .. note:: | 132 | .. note:: |
206 | 116 | 133 | ||
207 | === modified file 'etc/auth-server.conf-sample' | |||
208 | --- etc/auth-server.conf-sample 2010-08-24 14:08:16 +0000 | |||
209 | +++ etc/auth-server.conf-sample 2010-09-12 00:25:58 +0000 | |||
210 | @@ -12,6 +12,8 @@ | |||
211 | 12 | 12 | ||
212 | 13 | [app:auth-server] | 13 | [app:auth-server] |
213 | 14 | use = egg:swift#auth | 14 | use = egg:swift#auth |
214 | 15 | # Highly recommended to change this. | ||
215 | 16 | super_admin_key = devauth | ||
216 | 15 | # log_name = auth-server | 17 | # log_name = auth-server |
217 | 16 | # log_facility = LOG_LOCAL0 | 18 | # log_facility = LOG_LOCAL0 |
218 | 17 | # log_level = INFO | 19 | # log_level = INFO |
219 | 18 | 20 | ||
220 | === modified file 'swift/auth/server.py' | |||
221 | --- swift/auth/server.py 2010-09-09 17:24:25 +0000 | |||
222 | +++ swift/auth/server.py 2010-09-12 00:25:58 +0000 | |||
223 | @@ -14,23 +14,21 @@ | |||
224 | 14 | # limitations under the License. | 14 | # limitations under the License. |
225 | 15 | 15 | ||
226 | 16 | from __future__ import with_statement | 16 | from __future__ import with_statement |
227 | 17 | import errno | ||
228 | 18 | import os | 17 | import os |
229 | 19 | import socket | ||
230 | 20 | from contextlib import contextmanager | 18 | from contextlib import contextmanager |
231 | 21 | from time import gmtime, strftime, time | 19 | from time import gmtime, strftime, time |
232 | 22 | from urllib import unquote, quote | 20 | from urllib import unquote, quote |
233 | 23 | from uuid import uuid4 | 21 | from uuid import uuid4 |
234 | 22 | from urlparse import urlparse | ||
235 | 24 | 23 | ||
236 | 25 | import sqlite3 | 24 | import sqlite3 |
237 | 26 | from webob import Request, Response | 25 | from webob import Request, Response |
240 | 27 | from webob.exc import HTTPBadRequest, HTTPNoContent, HTTPUnauthorized, \ | 26 | from webob.exc import HTTPBadRequest, HTTPForbidden, HTTPNoContent, \ |
241 | 28 | HTTPServiceUnavailable, HTTPNotFound | 27 | HTTPUnauthorized, HTTPServiceUnavailable, HTTPNotFound |
242 | 29 | 28 | ||
244 | 30 | from swift.common.bufferedhttp import http_connect | 29 | from swift.common.bufferedhttp import http_connect_raw as http_connect |
245 | 31 | from swift.common.db import get_db_connection | 30 | from swift.common.db import get_db_connection |
248 | 32 | from swift.common.ring import Ring | 31 | from swift.common.utils import get_logger, split_path |
247 | 33 | from swift.common.utils import get_logger, normalize_timestamp, split_path | ||
249 | 34 | 32 | ||
250 | 35 | 33 | ||
251 | 36 | class AuthController(object): | 34 | class AuthController(object): |
252 | @@ -69,8 +67,7 @@ | |||
253 | 69 | 67 | ||
254 | 70 | * The developer makes a ReST call to create a new user. | 68 | * The developer makes a ReST call to create a new user. |
255 | 71 | * If the account for the user does not yet exist, the auth server makes | 69 | * If the account for the user does not yet exist, the auth server makes |
258 | 72 | ReST calls to the Swift cluster's account servers to create a new account | 70 | a ReST call to the Swift cluster to create a new account on its end. |
257 | 73 | on its end. | ||
259 | 74 | * The auth server records the information in its database. | 71 | * The auth server records the information in its database. |
260 | 75 | 72 | ||
261 | 76 | A last use case is recreating existing accounts; this is really only useful | 73 | A last use case is recreating existing accounts; this is really only useful |
262 | @@ -78,34 +75,34 @@ | |||
263 | 78 | the auth server's database is retained: | 75 | the auth server's database is retained: |
264 | 79 | 76 | ||
265 | 80 | * A developer makes an ReST call to have the existing accounts recreated. | 77 | * A developer makes an ReST call to have the existing accounts recreated. |
269 | 81 | * For each account in its database, the auth server makes ReST calls to | 78 | * For each account in its database, the auth server makes a ReST call to |
270 | 82 | the Swift cluster's account servers to create a specific account on its | 79 | the Swift cluster to create the specific account on its end. |
268 | 83 | end. | ||
271 | 84 | 80 | ||
272 | 85 | :param conf: The [auth-server] dictionary of the auth server configuration | 81 | :param conf: The [auth-server] dictionary of the auth server configuration |
273 | 86 | file | 82 | file |
274 | 87 | :param ring: Overrides loading the account ring from a file; useful for | ||
275 | 88 | testing. | ||
276 | 89 | 83 | ||
277 | 90 | See the etc/auth-server.conf-sample for information on the possible | 84 | See the etc/auth-server.conf-sample for information on the possible |
278 | 91 | configuration parameters. | 85 | configuration parameters. |
279 | 92 | """ | 86 | """ |
280 | 93 | 87 | ||
282 | 94 | def __init__(self, conf, ring=None): | 88 | def __init__(self, conf): |
283 | 95 | self.logger = get_logger(conf) | 89 | self.logger = get_logger(conf) |
284 | 90 | self.super_admin_key = conf.get('super_admin_key') | ||
285 | 91 | if not self.super_admin_key: | ||
286 | 92 | msg = 'No super_admin_key set in conf file! Exiting.' | ||
287 | 93 | try: | ||
288 | 94 | self.logger.critical(msg) | ||
289 | 95 | except: | ||
290 | 96 | pass | ||
291 | 97 | raise ValueError(msg) | ||
292 | 96 | self.swift_dir = conf.get('swift_dir', '/etc/swift') | 98 | self.swift_dir = conf.get('swift_dir', '/etc/swift') |
293 | 97 | self.reseller_prefix = conf.get('reseller_prefix', 'AUTH').strip() | 99 | self.reseller_prefix = conf.get('reseller_prefix', 'AUTH').strip() |
294 | 98 | if self.reseller_prefix and self.reseller_prefix[-1] != '_': | 100 | if self.reseller_prefix and self.reseller_prefix[-1] != '_': |
295 | 99 | self.reseller_prefix += '_' | 101 | self.reseller_prefix += '_' |
298 | 100 | self.default_cluster_url = \ | 102 | self.default_cluster_url = conf.get('default_cluster_url', |
299 | 101 | conf.get('default_cluster_url', 'http://127.0.0.1:8080/v1') | 103 | 'http://127.0.0.1:8080/v1').rstrip('/') |
300 | 102 | self.token_life = int(conf.get('token_life', 86400)) | 104 | self.token_life = int(conf.get('token_life', 86400)) |
301 | 103 | self.log_headers = conf.get('log_headers') == 'True' | 105 | self.log_headers = conf.get('log_headers') == 'True' |
302 | 104 | if ring: | ||
303 | 105 | self.account_ring = ring | ||
304 | 106 | else: | ||
305 | 107 | self.account_ring = \ | ||
306 | 108 | Ring(os.path.join(self.swift_dir, 'account.ring.gz')) | ||
307 | 109 | self.db_file = os.path.join(self.swift_dir, 'auth.db') | 106 | self.db_file = os.path.join(self.swift_dir, 'auth.db') |
308 | 110 | self.conn = get_db_connection(self.db_file, okay_to_create=True) | 107 | self.conn = get_db_connection(self.db_file, okay_to_create=True) |
309 | 111 | try: | 108 | try: |
310 | @@ -114,9 +111,16 @@ | |||
311 | 114 | if str(err) == 'no such column: admin': | 111 | if str(err) == 'no such column: admin': |
312 | 115 | self.conn.execute("ALTER TABLE account ADD COLUMN admin TEXT") | 112 | self.conn.execute("ALTER TABLE account ADD COLUMN admin TEXT") |
313 | 116 | self.conn.execute("UPDATE account SET admin = 't'") | 113 | self.conn.execute("UPDATE account SET admin = 't'") |
314 | 114 | try: | ||
315 | 115 | self.conn.execute('SELECT reseller_admin FROM account LIMIT 1') | ||
316 | 116 | except sqlite3.OperationalError, err: | ||
317 | 117 | if str(err) == 'no such column: reseller_admin': | ||
318 | 118 | self.conn.execute( | ||
319 | 119 | "ALTER TABLE account ADD COLUMN reseller_admin TEXT") | ||
320 | 117 | self.conn.execute('''CREATE TABLE IF NOT EXISTS account ( | 120 | self.conn.execute('''CREATE TABLE IF NOT EXISTS account ( |
321 | 118 | account TEXT, url TEXT, cfaccount TEXT, | 121 | account TEXT, url TEXT, cfaccount TEXT, |
323 | 119 | user TEXT, password TEXT, admin TEXT)''') | 122 | user TEXT, password TEXT, admin TEXT, |
324 | 123 | reseller_admin TEXT)''') | ||
325 | 120 | self.conn.execute('''CREATE INDEX IF NOT EXISTS ix_account_account | 124 | self.conn.execute('''CREATE INDEX IF NOT EXISTS ix_account_account |
326 | 121 | ON account (account)''') | 125 | ON account (account)''') |
327 | 122 | try: | 126 | try: |
328 | @@ -139,51 +143,36 @@ | |||
329 | 139 | 143 | ||
330 | 140 | def add_storage_account(self, account_name=''): | 144 | def add_storage_account(self, account_name=''): |
331 | 141 | """ | 145 | """ |
334 | 142 | Creates an account within the Swift cluster by making a ReST call to | 146 | Creates an account within the Swift cluster by making a ReST call. |
333 | 143 | each of the responsible account servers. | ||
335 | 144 | 147 | ||
336 | 145 | :param account_name: The desired name for the account; if omitted a | 148 | :param account_name: The desired name for the account; if omitted a |
337 | 146 | UUID4 will be used. | 149 | UUID4 will be used. |
338 | 147 | :returns: False upon failure, otherwise the name of the account | 150 | :returns: False upon failure, otherwise the name of the account |
339 | 148 | within the Swift cluster. | 151 | within the Swift cluster. |
340 | 149 | """ | 152 | """ |
341 | 150 | begin = time() | ||
342 | 151 | orig_account_name = account_name | 153 | orig_account_name = account_name |
343 | 152 | if not account_name: | 154 | if not account_name: |
344 | 153 | account_name = '%s%s' % (self.reseller_prefix, uuid4().hex) | 155 | account_name = '%s%s' % (self.reseller_prefix, uuid4().hex) |
378 | 154 | partition, nodes = self.account_ring.get_nodes(account_name) | 156 | url = '%s/%s' % (self.default_cluster_url, account_name) |
379 | 155 | headers = {'X-Timestamp': normalize_timestamp(time()), | 157 | parsed = urlparse(url) |
380 | 156 | 'x-cf-trans-id': 'tx' + str(uuid4())} | 158 | # Create a single use token. |
381 | 157 | statuses = [] | 159 | token = '%stk%s' % (self.reseller_prefix, uuid4().hex) |
382 | 158 | for node in nodes: | 160 | with self.get_conn() as conn: |
383 | 159 | try: | 161 | conn.execute(''' |
384 | 160 | conn = None | 162 | INSERT INTO token |
385 | 161 | conn = http_connect(node['ip'], node['port'], node['device'], | 163 | (token, created, account, user, cfaccount) VALUES |
386 | 162 | partition, 'PUT', '/' + account_name, headers) | 164 | (?, ?, '.super_admin', '.single_use', '.reseller_admin')''', |
387 | 163 | source = conn.getresponse() | 165 | (token, time())) |
388 | 164 | statuses.append(source.status) | 166 | conn.commit() |
389 | 165 | if source.status >= 500: | 167 | conn = http_connect(parsed.hostname, parsed.port, 'PUT', parsed.path, |
390 | 166 | self.logger.error('ERROR With account server %s:%s/%s: ' | 168 | {'X-Auth-Token': token}, ssl=(parsed.scheme == 'https')) |
391 | 167 | 'Response %s %s: %s' % | 169 | resp = conn.getresponse() |
392 | 168 | (node['ip'], node['port'], node['device'], | 170 | resp.read() |
393 | 169 | source.status, source.reason, source.read(1024))) | 171 | if resp.status // 100 != 2: |
394 | 170 | conn = None | 172 | self.logger.error('ERROR attempting to create account %s: %s %s' % |
395 | 171 | except BaseException, err: | 173 | (url, resp.status, resp.reason)) |
396 | 172 | log_call = self.logger.exception | 174 | return False |
397 | 173 | msg = 'ERROR With account server ' \ | 175 | return account_name |
365 | 174 | '%(ip)s:%(port)s/%(device)s (will retry later): ' % node | ||
366 | 175 | if isinstance(err, socket.error): | ||
367 | 176 | if err[0] == errno.ECONNREFUSED: | ||
368 | 177 | log_call = self.logger.error | ||
369 | 178 | msg += 'Connection refused' | ||
370 | 179 | elif err[0] == errno.EHOSTUNREACH: | ||
371 | 180 | log_call = self.logger.error | ||
372 | 181 | msg += 'Host unreachable' | ||
373 | 182 | log_call(msg) | ||
374 | 183 | rv = False | ||
375 | 184 | if len([s for s in statuses if (200 <= s < 300)]) > len(nodes) / 2: | ||
376 | 185 | rv = account_name | ||
377 | 186 | return rv | ||
398 | 187 | 176 | ||
399 | 188 | @contextmanager | 177 | @contextmanager |
400 | 189 | def get_conn(self): | 178 | def get_conn(self): |
401 | @@ -229,7 +218,9 @@ | |||
402 | 229 | 218 | ||
403 | 230 | :param token: The token to validate | 219 | :param token: The token to validate |
404 | 231 | :returns: (TTL, account, user, cfaccount) if valid, False otherwise. | 220 | :returns: (TTL, account, user, cfaccount) if valid, False otherwise. |
406 | 232 | cfaccount will be None for users without admin access. | 221 | cfaccount will be None for users without admin access for the |
407 | 222 | account. cfaccount will be .reseller_admin for users with | ||
408 | 223 | full reseller admin rights. | ||
409 | 233 | """ | 224 | """ |
410 | 234 | begin = time() | 225 | begin = time() |
411 | 235 | self.purge_old_tokens() | 226 | self.purge_old_tokens() |
412 | @@ -241,18 +232,20 @@ | |||
413 | 241 | (token,)).fetchone() | 232 | (token,)).fetchone() |
414 | 242 | if row is not None: | 233 | if row is not None: |
415 | 243 | created = row[0] | 234 | created = row[0] |
417 | 244 | if time() - created >= self.token_life: | 235 | if time() - created < self.token_life: |
418 | 236 | rv = (self.token_life - (time() - created), row[1], row[2], | ||
419 | 237 | row[3]) | ||
420 | 238 | # Remove the token if it was expired or single use. | ||
421 | 239 | if not rv or rv[2] == '.single_use': | ||
422 | 245 | conn.execute(''' | 240 | conn.execute(''' |
423 | 246 | DELETE FROM token WHERE token = ?''', (token,)) | 241 | DELETE FROM token WHERE token = ?''', (token,)) |
424 | 247 | conn.commit() | 242 | conn.commit() |
425 | 248 | else: | ||
426 | 249 | rv = (self.token_life - (time() - created), row[1], row[2], | ||
427 | 250 | row[3]) | ||
428 | 251 | self.logger.info('validate_token(%s, _, _) = %s [%.02f]' % | 243 | self.logger.info('validate_token(%s, _, _) = %s [%.02f]' % |
429 | 252 | (repr(token), repr(rv), time() - begin)) | 244 | (repr(token), repr(rv), time() - begin)) |
430 | 253 | return rv | 245 | return rv |
431 | 254 | 246 | ||
433 | 255 | def create_user(self, account, user, password, admin=False): | 247 | def create_user(self, account, user, password, admin=False, |
434 | 248 | reseller_admin=False): | ||
435 | 256 | """ | 249 | """ |
436 | 257 | Handles the create_user call for developers, used to request a user be | 250 | Handles the create_user call for developers, used to request a user be |
437 | 258 | added in the auth server database. If the account does not yet exist, | 251 | added in the auth server database. If the account does not yet exist, |
438 | @@ -274,6 +267,9 @@ | |||
439 | 274 | :param admin: If true, the user will be granted full access to the | 267 | :param admin: If true, the user will be granted full access to the |
440 | 275 | account; otherwise, another user will have to add the | 268 | account; otherwise, another user will have to add the |
441 | 276 | user to the ACLs for containers to grant access. | 269 | user to the ACLs for containers to grant access. |
442 | 270 | :param reseller_admin: If true, the user will be granted full access to | ||
443 | 271 | all accounts within this reseller, including the | ||
444 | 272 | ability to create additional accounts. | ||
445 | 277 | 273 | ||
446 | 278 | :returns: False if the create fails, 'already exists' if the user | 274 | :returns: False if the create fails, 'already exists' if the user |
447 | 279 | already exists, or storage url if successful | 275 | already exists, or storage url if successful |
448 | @@ -287,9 +283,9 @@ | |||
449 | 287 | (account, user)).fetchone() | 283 | (account, user)).fetchone() |
450 | 288 | if row: | 284 | if row: |
451 | 289 | self.logger.info( | 285 | self.logger.info( |
453 | 290 | 'ALREADY EXISTS create_user(%s, %s, _, %s) [%.02f]' % | 286 | 'ALREADY EXISTS create_user(%s, %s, _, %s, %s) [%.02f]' % |
454 | 291 | (repr(account), repr(user), repr(admin), | 287 | (repr(account), repr(user), repr(admin), |
456 | 292 | time() - begin)) | 288 | repr(reseller_admin), time() - begin)) |
457 | 293 | return 'already exists' | 289 | return 'already exists' |
458 | 294 | row = conn.execute( | 290 | row = conn.execute( |
459 | 295 | 'SELECT url, cfaccount FROM account WHERE account = ?', | 291 | 'SELECT url, cfaccount FROM account WHERE account = ?', |
460 | @@ -301,21 +297,22 @@ | |||
461 | 301 | account_hash = self.add_storage_account() | 297 | account_hash = self.add_storage_account() |
462 | 302 | if not account_hash: | 298 | if not account_hash: |
463 | 303 | self.logger.info( | 299 | self.logger.info( |
465 | 304 | 'FAILED create_user(%s, %s, _, %s) [%.02f]' % | 300 | 'FAILED create_user(%s, %s, _, %s, %s) [%.02f]' % |
466 | 305 | (repr(account), repr(user), repr(admin), | 301 | (repr(account), repr(user), repr(admin), |
468 | 306 | time() - begin)) | 302 | repr(reseller_admin), time() - begin)) |
469 | 307 | return False | 303 | return False |
470 | 308 | url = self.default_cluster_url.rstrip('/') + '/' + account_hash | 304 | url = self.default_cluster_url.rstrip('/') + '/' + account_hash |
471 | 309 | conn.execute('''INSERT INTO account | 305 | conn.execute('''INSERT INTO account |
474 | 310 | (account, url, cfaccount, user, password, admin) | 306 | (account, url, cfaccount, user, password, admin, |
475 | 311 | VALUES (?, ?, ?, ?, ?, ?)''', | 307 | reseller_admin) |
476 | 308 | VALUES (?, ?, ?, ?, ?, ?, ?)''', | ||
477 | 312 | (account, url, account_hash, user, password, | 309 | (account, url, account_hash, user, password, |
479 | 313 | admin and 't' or '')) | 310 | admin and 't' or '', reseller_admin and 't' or '')) |
480 | 314 | conn.commit() | 311 | conn.commit() |
481 | 315 | self.logger.info( | 312 | self.logger.info( |
485 | 316 | 'SUCCESS create_user(%s, %s, _, %s) = %s [%.02f]' % | 313 | 'SUCCESS create_user(%s, %s, _, %s, %s) = %s [%.02f]' % |
486 | 317 | (repr(account), repr(user), repr(admin), repr(url), | 314 | (repr(account), repr(user), repr(admin), repr(reseller_admin), |
487 | 318 | time() - begin)) | 315 | repr(url), time() - begin)) |
488 | 319 | return url | 316 | return url |
489 | 320 | 317 | ||
490 | 321 | def recreate_accounts(self): | 318 | def recreate_accounts(self): |
491 | @@ -339,6 +336,32 @@ | |||
492 | 339 | (rv, time() - begin)) | 336 | (rv, time() - begin)) |
493 | 340 | return rv | 337 | return rv |
494 | 341 | 338 | ||
495 | 339 | def is_account_admin(self, request, for_account): | ||
496 | 340 | """ | ||
497 | 341 | Returns True if the request represents coming from .super_admin, a | ||
498 | 342 | .reseller_admin, or an admin for the account specified. | ||
499 | 343 | """ | ||
500 | 344 | if request.headers.get('X-Auth-Admin-User') == '.super_admin' and \ | ||
501 | 345 | request.headers.get('X-Auth-Admin-Key') == self.super_admin_key: | ||
502 | 346 | return True | ||
503 | 347 | try: | ||
504 | 348 | account, user = \ | ||
505 | 349 | request.headers.get('X-Auth-Admin-User').split(':', 1) | ||
506 | 350 | except (AttributeError, ValueError): | ||
507 | 351 | return False | ||
508 | 352 | with self.get_conn() as conn: | ||
509 | 353 | row = conn.execute(''' | ||
510 | 354 | SELECT reseller_admin, admin FROM account | ||
511 | 355 | WHERE account = ? AND user = ? AND password = ?''', | ||
512 | 356 | (account, user, | ||
513 | 357 | request.headers.get('X-Auth-Admin-Key'))).fetchone() | ||
514 | 358 | if row: | ||
515 | 359 | if row[0] == 't': | ||
516 | 360 | return True | ||
517 | 361 | if row[1] == 't' and account == for_account: | ||
518 | 362 | return True | ||
519 | 363 | return False | ||
520 | 364 | |||
521 | 342 | def handle_token(self, request): | 365 | def handle_token(self, request): |
522 | 343 | """ | 366 | """ |
523 | 344 | Handles ReST requests from Swift to validate tokens | 367 | Handles ReST requests from Swift to validate tokens |
524 | @@ -362,7 +385,9 @@ | |||
525 | 362 | if not validation: | 385 | if not validation: |
526 | 363 | return HTTPNotFound() | 386 | return HTTPNotFound() |
527 | 364 | groups = ['%s:%s' % (validation[1], validation[2]), validation[1]] | 387 | groups = ['%s:%s' % (validation[1], validation[2]), validation[1]] |
529 | 365 | if validation[3]: # admin access to a cfaccount | 388 | if validation[3]: |
530 | 389 | # admin access to a cfaccount or ".reseller_admin" to access to all | ||
531 | 390 | # accounts, including creating new ones. | ||
532 | 366 | groups.append(validation[3]) | 391 | groups.append(validation[3]) |
533 | 367 | return HTTPNoContent(headers={'X-Auth-TTL': validation[0], | 392 | return HTTPNoContent(headers={'X-Auth-TTL': validation[0], |
534 | 368 | 'X-Auth-Groups': ','.join(groups)}) | 393 | 'X-Auth-Groups': ','.join(groups)}) |
535 | @@ -380,6 +405,7 @@ | |||
536 | 380 | Valid headers: | 405 | Valid headers: |
537 | 381 | * X-Auth-User-Key: <password> | 406 | * X-Auth-User-Key: <password> |
538 | 382 | * X-Auth-User-Admin: <true|false> | 407 | * X-Auth-User-Admin: <true|false> |
539 | 408 | * X-Auth-User-Reseller-Admin: <true|false> | ||
540 | 383 | 409 | ||
541 | 384 | If the HTTP request returns with a 204, then the user was added, | 410 | If the HTTP request returns with a 204, then the user was added, |
542 | 385 | and the storage url will be available in the X-Storage-Url header. | 411 | and the storage url will be available in the X-Storage-Url header. |
543 | @@ -390,13 +416,24 @@ | |||
544 | 390 | _, account_name, user_name = split_path(request.path, minsegs=3) | 416 | _, account_name, user_name = split_path(request.path, minsegs=3) |
545 | 391 | except ValueError: | 417 | except ValueError: |
546 | 392 | return HTTPBadRequest() | 418 | return HTTPBadRequest() |
547 | 419 | create_reseller_admin = \ | ||
548 | 420 | request.headers.get('x-auth-user-reseller-admin') == 'true' | ||
549 | 421 | if create_reseller_admin and ( | ||
550 | 422 | request.headers.get('X-Auth-Admin-User') != '.super_admin' or | ||
551 | 423 | request.headers.get('X-Auth-Admin-Key') != self.super_admin_key): | ||
552 | 424 | return HTTPForbidden(request=request) | ||
553 | 425 | create_account_admin = \ | ||
554 | 426 | request.headers.get('x-auth-user-admin') == 'true' | ||
555 | 427 | if create_account_admin and \ | ||
556 | 428 | not self.is_account_admin(request, account_name): | ||
557 | 429 | return HTTPForbidden(request=request) | ||
558 | 393 | if 'X-Auth-User-Key' not in request.headers: | 430 | if 'X-Auth-User-Key' not in request.headers: |
560 | 394 | return HTTPBadRequest('X-Auth-User-Key is required') | 431 | return HTTPBadRequest(body='X-Auth-User-Key is required') |
561 | 395 | password = request.headers['x-auth-user-key'] | 432 | password = request.headers['x-auth-user-key'] |
562 | 396 | storage_url = self.create_user(account_name, user_name, password, | 433 | storage_url = self.create_user(account_name, user_name, password, |
564 | 397 | request.headers.get('x-auth-user-admin') == 'true') | 434 | create_account_admin, create_reseller_admin) |
565 | 398 | if storage_url == 'already exists': | 435 | if storage_url == 'already exists': |
567 | 399 | return HTTPBadRequest(storage_url) | 436 | return HTTPBadRequest(body=storage_url) |
568 | 400 | if not storage_url: | 437 | if not storage_url: |
569 | 401 | return HTTPServiceUnavailable() | 438 | return HTTPServiceUnavailable() |
570 | 402 | return HTTPNoContent(headers={'x-storage-url': storage_url}) | 439 | return HTTPNoContent(headers={'x-storage-url': storage_url}) |
571 | @@ -412,6 +449,9 @@ | |||
572 | 412 | 449 | ||
573 | 413 | :param request: webob.Request object | 450 | :param request: webob.Request object |
574 | 414 | """ | 451 | """ |
575 | 452 | if request.headers.get('X-Auth-Admin-User') != '.super_admin' or \ | ||
576 | 453 | request.headers.get('X-Auth-Admin-Key') != self.super_admin_key: | ||
577 | 454 | return HTTPForbidden(request=request) | ||
578 | 415 | result = self.recreate_accounts() | 455 | result = self.recreate_accounts() |
579 | 416 | return Response(result, 200, request=request) | 456 | return Response(result, 200, request=request) |
580 | 417 | 457 | ||
581 | @@ -471,7 +511,7 @@ | |||
582 | 471 | self.purge_old_tokens() | 511 | self.purge_old_tokens() |
583 | 472 | with self.get_conn() as conn: | 512 | with self.get_conn() as conn: |
584 | 473 | row = conn.execute(''' | 513 | row = conn.execute(''' |
586 | 474 | SELECT cfaccount, url, admin FROM account | 514 | SELECT cfaccount, url, admin, reseller_admin FROM account |
587 | 475 | WHERE account = ? AND user = ? AND password = ?''', | 515 | WHERE account = ? AND user = ? AND password = ?''', |
588 | 476 | (account, user, password)).fetchone() | 516 | (account, user, password)).fetchone() |
589 | 477 | if row is None: | 517 | if row is None: |
590 | @@ -479,6 +519,7 @@ | |||
591 | 479 | cfaccount = row[0] | 519 | cfaccount = row[0] |
592 | 480 | url = row[1] | 520 | url = row[1] |
593 | 481 | admin = row[2] == 't' | 521 | admin = row[2] == 't' |
594 | 522 | reseller_admin = row[3] == 't' | ||
595 | 482 | row = conn.execute(''' | 523 | row = conn.execute(''' |
596 | 483 | SELECT token FROM token WHERE account = ? AND user = ?''', | 524 | SELECT token FROM token WHERE account = ? AND user = ?''', |
597 | 484 | (account, user)).fetchone() | 525 | (account, user)).fetchone() |
598 | @@ -486,11 +527,16 @@ | |||
599 | 486 | token = row[0] | 527 | token = row[0] |
600 | 487 | else: | 528 | else: |
601 | 488 | token = '%stk%s' % (self.reseller_prefix, uuid4().hex) | 529 | token = '%stk%s' % (self.reseller_prefix, uuid4().hex) |
602 | 530 | token_cfaccount = '' | ||
603 | 531 | if admin: | ||
604 | 532 | token_cfaccount = cfaccount | ||
605 | 533 | if reseller_admin: | ||
606 | 534 | token_cfaccount = '.reseller_admin' | ||
607 | 489 | conn.execute(''' | 535 | conn.execute(''' |
608 | 490 | INSERT INTO token | 536 | INSERT INTO token |
609 | 491 | (token, created, account, user, cfaccount) | 537 | (token, created, account, user, cfaccount) |
610 | 492 | VALUES (?, ?, ?, ?, ?)''', | 538 | VALUES (?, ?, ?, ?, ?)''', |
612 | 493 | (token, time(), account, user, admin and cfaccount or '')) | 539 | (token, time(), account, user, token_cfaccount)) |
613 | 494 | conn.commit() | 540 | conn.commit() |
614 | 495 | return HTTPNoContent(headers={'x-auth-token': token, | 541 | return HTTPNoContent(headers={'x-auth-token': token, |
615 | 496 | 'x-storage-token': token, | 542 | 'x-storage-token': token, |
616 | 497 | 543 | ||
617 | === modified file 'swift/common/constraints.py' | |||
618 | --- swift/common/constraints.py 2010-08-16 22:30:27 +0000 | |||
619 | +++ swift/common/constraints.py 2010-09-12 00:25:58 +0000 | |||
620 | @@ -36,6 +36,8 @@ | |||
621 | 36 | CONTAINER_LISTING_LIMIT = 10000 | 36 | CONTAINER_LISTING_LIMIT = 10000 |
622 | 37 | #: Max container list length of a get request for an account | 37 | #: Max container list length of a get request for an account |
623 | 38 | ACCOUNT_LISTING_LIMIT = 10000 | 38 | ACCOUNT_LISTING_LIMIT = 10000 |
624 | 39 | MAX_ACCOUNT_NAME_LENGTH = 256 | ||
625 | 40 | MAX_CONTAINER_NAME_LENGTH = 256 | ||
626 | 39 | 41 | ||
627 | 40 | 42 | ||
628 | 41 | def check_metadata(req, target_type): | 43 | def check_metadata(req, target_type): |
629 | 42 | 44 | ||
630 | === modified file 'swift/common/middleware/auth.py' | |||
631 | --- swift/common/middleware/auth.py 2010-09-09 17:24:25 +0000 | |||
632 | +++ swift/common/middleware/auth.py 2010-09-12 00:25:58 +0000 | |||
633 | @@ -49,7 +49,7 @@ | |||
634 | 49 | token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN')) | 49 | token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN')) |
635 | 50 | if token and token.startswith(self.reseller_prefix): | 50 | if token and token.startswith(self.reseller_prefix): |
636 | 51 | memcache_client = cache_from_env(env) | 51 | memcache_client = cache_from_env(env) |
638 | 52 | key = 'devauth/%s' % token | 52 | key = '%s/token/%s' % (self.reseller_prefix, token) |
639 | 53 | cached_auth_data = memcache_client.get(key) | 53 | cached_auth_data = memcache_client.get(key) |
640 | 54 | if cached_auth_data: | 54 | if cached_auth_data: |
641 | 55 | start, expiration, groups = cached_auth_data | 55 | start, expiration, groups = cached_auth_data |
642 | @@ -85,14 +85,19 @@ | |||
643 | 85 | version, account, container, obj = split_path(req.path, 1, 4, True) | 85 | version, account, container, obj = split_path(req.path, 1, 4, True) |
644 | 86 | if not account or not account.startswith(self.reseller_prefix): | 86 | if not account or not account.startswith(self.reseller_prefix): |
645 | 87 | return self.denied_response(req) | 87 | return self.denied_response(req) |
647 | 88 | if req.remote_user and account in req.remote_user.split(','): | 88 | user_groups = (req.remote_user or '').split(',') |
648 | 89 | if '.reseller_admin' in user_groups: | ||
649 | 90 | return None | ||
650 | 91 | if account in user_groups and (req.method != 'PUT' or container): | ||
651 | 92 | # If the user is admin for the account and is not trying to do an | ||
652 | 93 | # account PUT... | ||
653 | 89 | return None | 94 | return None |
654 | 90 | referrers, groups = parse_acl(getattr(req, 'acl', None)) | 95 | referrers, groups = parse_acl(getattr(req, 'acl', None)) |
655 | 91 | if referrer_allowed(req.referer, referrers): | 96 | if referrer_allowed(req.referer, referrers): |
656 | 92 | return None | 97 | return None |
657 | 93 | if not req.remote_user: | 98 | if not req.remote_user: |
658 | 94 | return self.denied_response(req) | 99 | return self.denied_response(req) |
660 | 95 | for user_group in req.remote_user.split(','): | 100 | for user_group in user_groups: |
661 | 96 | if user_group in groups: | 101 | if user_group in groups: |
662 | 97 | return None | 102 | return None |
663 | 98 | return self.denied_response(req) | 103 | return self.denied_response(req) |
664 | 99 | 104 | ||
665 | === modified file 'swift/proxy/server.py' | |||
666 | --- swift/proxy/server.py 2010-09-10 14:52:10 +0000 | |||
667 | +++ swift/proxy/server.py 2010-09-12 00:25:58 +0000 | |||
668 | @@ -35,13 +35,12 @@ | |||
669 | 35 | from swift.common.utils import get_logger, normalize_timestamp, split_path, \ | 35 | from swift.common.utils import get_logger, normalize_timestamp, split_path, \ |
670 | 36 | cache_from_env | 36 | cache_from_env |
671 | 37 | from swift.common.bufferedhttp import http_connect | 37 | from swift.common.bufferedhttp import http_connect |
674 | 38 | from swift.common.constraints import check_object_creation, check_metadata, \ | 38 | from swift.common.constraints import check_metadata, check_object_creation, \ |
675 | 39 | MAX_FILE_SIZE, check_xml_encodable | 39 | check_xml_encodable, MAX_ACCOUNT_NAME_LENGTH, MAX_CONTAINER_NAME_LENGTH, \ |
676 | 40 | MAX_FILE_SIZE | ||
677 | 40 | from swift.common.exceptions import ChunkReadTimeout, \ | 41 | from swift.common.exceptions import ChunkReadTimeout, \ |
678 | 41 | ChunkWriteTimeout, ConnectionTimeout | 42 | ChunkWriteTimeout, ConnectionTimeout |
679 | 42 | 43 | ||
680 | 43 | MAX_CONTAINER_NAME_LENGTH = 256 | ||
681 | 44 | |||
682 | 45 | 44 | ||
683 | 46 | def update_headers(response, headers): | 45 | def update_headers(response, headers): |
684 | 47 | """ | 46 | """ |
685 | @@ -1080,6 +1079,59 @@ | |||
686 | 1080 | req.path_info.rstrip('/'), self.app.account_ring.replica_count) | 1079 | req.path_info.rstrip('/'), self.app.account_ring.replica_count) |
687 | 1081 | 1080 | ||
688 | 1082 | @public | 1081 | @public |
689 | 1082 | def PUT(self, req): | ||
690 | 1083 | """HTTP PUT request handler.""" | ||
691 | 1084 | error_response = check_metadata(req, 'account') | ||
692 | 1085 | if error_response: | ||
693 | 1086 | return error_response | ||
694 | 1087 | if len(self.account_name) > MAX_ACCOUNT_NAME_LENGTH: | ||
695 | 1088 | resp = HTTPBadRequest(request=req) | ||
696 | 1089 | resp.body = 'Account name length of %d longer than %d' % \ | ||
697 | 1090 | (len(self.account_name), MAX_ACCOUNT_NAME_LENGTH) | ||
698 | 1091 | return resp | ||
699 | 1092 | account_partition, accounts = \ | ||
700 | 1093 | self.app.account_ring.get_nodes(self.account_name) | ||
701 | 1094 | headers = {'X-Timestamp': normalize_timestamp(time.time()), | ||
702 | 1095 | 'x-cf-trans-id': self.trans_id} | ||
703 | 1096 | headers.update(value for value in req.headers.iteritems() | ||
704 | 1097 | if value[0].lower().startswith('x-account-meta-')) | ||
705 | 1098 | statuses = [] | ||
706 | 1099 | reasons = [] | ||
707 | 1100 | bodies = [] | ||
708 | 1101 | for node in self.iter_nodes(account_partition, accounts, | ||
709 | 1102 | self.app.account_ring): | ||
710 | 1103 | if self.error_limited(node): | ||
711 | 1104 | continue | ||
712 | 1105 | try: | ||
713 | 1106 | with ConnectionTimeout(self.app.conn_timeout): | ||
714 | 1107 | conn = http_connect(node['ip'], node['port'], | ||
715 | 1108 | node['device'], account_partition, 'PUT', | ||
716 | 1109 | req.path_info, headers) | ||
717 | 1110 | with Timeout(self.app.node_timeout): | ||
718 | 1111 | source = conn.getresponse() | ||
719 | 1112 | body = source.read() | ||
720 | 1113 | if 200 <= source.status < 300 \ | ||
721 | 1114 | or 400 <= source.status < 500: | ||
722 | 1115 | statuses.append(source.status) | ||
723 | 1116 | reasons.append(source.reason) | ||
724 | 1117 | bodies.append(body) | ||
725 | 1118 | else: | ||
726 | 1119 | if source.status == 507: | ||
727 | 1120 | self.error_limit(node) | ||
728 | 1121 | except: | ||
729 | 1122 | self.exception_occurred(node, 'Account', | ||
730 | 1123 | 'Trying to PUT to %s' % req.path) | ||
731 | 1124 | if len(statuses) >= len(accounts): | ||
732 | 1125 | break | ||
733 | 1126 | while len(statuses) < len(accounts): | ||
734 | 1127 | statuses.append(503) | ||
735 | 1128 | reasons.append('') | ||
736 | 1129 | bodies.append('') | ||
737 | 1130 | self.app.memcache.delete('account%s' % req.path_info.rstrip('/')) | ||
738 | 1131 | return self.best_response(req, statuses, reasons, bodies, | ||
739 | 1132 | 'Account PUT') | ||
740 | 1133 | |||
741 | 1134 | @public | ||
742 | 1083 | def POST(self, req): | 1135 | def POST(self, req): |
743 | 1084 | """HTTP POST request handler.""" | 1136 | """HTTP POST request handler.""" |
744 | 1085 | error_response = check_metadata(req, 'account') | 1137 | error_response = check_metadata(req, 'account') |
745 | 1086 | 1138 | ||
746 | === modified file 'test/functional/swift.py' | |||
747 | --- test/functional/swift.py 2010-07-12 22:03:45 +0000 | |||
748 | +++ test/functional/swift.py 2010-09-12 00:25:58 +0000 | |||
749 | @@ -124,7 +124,7 @@ | |||
750 | 124 | if response.status == 401: | 124 | if response.status == 401: |
751 | 125 | raise AuthenticationFailed() | 125 | raise AuthenticationFailed() |
752 | 126 | 126 | ||
754 | 127 | if response.status != 204: | 127 | if response.status not in (200, 204): |
755 | 128 | raise ResponseError(response) | 128 | raise ResponseError(response) |
756 | 129 | 129 | ||
757 | 130 | for hdr in response.getheaders(): | 130 | for hdr in response.getheaders(): |
758 | 131 | 131 | ||
759 | === modified file 'test/functional/tests.py' | |||
760 | --- test/functional/tests.py 2010-09-03 04:50:16 +0000 | |||
761 | +++ test/functional/tests.py 2010-09-12 00:25:58 +0000 | |||
762 | @@ -172,7 +172,7 @@ | |||
763 | 172 | 172 | ||
764 | 173 | def testPUT(self): | 173 | def testPUT(self): |
765 | 174 | self.env.account.conn.make_request('PUT') | 174 | self.env.account.conn.make_request('PUT') |
767 | 175 | self.assert_status(405) | 175 | self.assert_status([403, 405]) |
768 | 176 | 176 | ||
769 | 177 | def testAccountHead(self): | 177 | def testAccountHead(self): |
770 | 178 | try_count = 0 | 178 | try_count = 0 |
771 | 179 | 179 | ||
772 | === modified file 'test/probe/common.py' | |||
773 | --- test/probe/common.py 2010-07-19 03:00:28 +0000 | |||
774 | +++ test/probe/common.py 2010-09-12 00:25:58 +0000 | |||
775 | @@ -13,16 +13,26 @@ | |||
776 | 13 | # See the License for the specific language governing permissions and | 13 | # See the License for the specific language governing permissions and |
777 | 14 | # limitations under the License. | 14 | # limitations under the License. |
778 | 15 | 15 | ||
780 | 16 | from os import kill | 16 | from os import environ, kill |
781 | 17 | from signal import SIGTERM | 17 | from signal import SIGTERM |
782 | 18 | from subprocess import call, Popen | 18 | from subprocess import call, Popen |
783 | 19 | from time import sleep | 19 | from time import sleep |
784 | 20 | from ConfigParser import ConfigParser | ||
785 | 20 | 21 | ||
786 | 21 | from swift.common.bufferedhttp import http_connect_raw as http_connect | 22 | from swift.common.bufferedhttp import http_connect_raw as http_connect |
787 | 22 | from swift.common.client import get_auth | 23 | from swift.common.client import get_auth |
788 | 23 | from swift.common.ring import Ring | 24 | from swift.common.ring import Ring |
789 | 24 | 25 | ||
790 | 25 | 26 | ||
791 | 27 | AUTH_SERVER_CONF_FILE = environ.get('SWIFT_AUTH_SERVER_CONF_FILE', | ||
792 | 28 | '/etc/swift/auth-server.conf') | ||
793 | 29 | c = ConfigParser() | ||
794 | 30 | if not c.read(AUTH_SERVER_CONF_FILE): | ||
795 | 31 | exit('Unable to read config file: %s' % AUTH_SERVER_CONF_FILE) | ||
796 | 32 | conf = dict(c.items('app:auth-server')) | ||
797 | 33 | SUPER_ADMIN_KEY = conf.get('super_admin_key', 'devauth') | ||
798 | 34 | |||
799 | 35 | |||
800 | 26 | def kill_pids(pids): | 36 | def kill_pids(pids): |
801 | 27 | for pid in pids.values(): | 37 | for pid in pids.values(): |
802 | 28 | try: | 38 | try: |
803 | @@ -50,7 +60,9 @@ | |||
804 | 50 | container_ring = Ring('/etc/swift/container.ring.gz') | 60 | container_ring = Ring('/etc/swift/container.ring.gz') |
805 | 51 | object_ring = Ring('/etc/swift/object.ring.gz') | 61 | object_ring = Ring('/etc/swift/object.ring.gz') |
806 | 52 | sleep(5) | 62 | sleep(5) |
808 | 53 | conn = http_connect('127.0.0.1', '11000', 'POST', '/recreate_accounts') | 63 | conn = http_connect('127.0.0.1', '11000', 'POST', '/recreate_accounts', |
809 | 64 | headers={'X-Auth-Admin-User': '.super_admin', | ||
810 | 65 | 'X-Auth-Admin-Key': SUPER_ADMIN_KEY}) | ||
811 | 54 | resp = conn.getresponse() | 66 | resp = conn.getresponse() |
812 | 55 | if resp.status != 200: | 67 | if resp.status != 200: |
813 | 56 | raise Exception('Recreating accounts failed. (%d)' % resp.status) | 68 | raise Exception('Recreating accounts failed. (%d)' % resp.status) |
814 | 57 | 69 | ||
815 | === modified file 'test/unit/auth/test_server.py' | |||
816 | --- test/unit/auth/test_server.py 2010-09-06 20:26:31 +0000 | |||
817 | +++ test/unit/auth/test_server.py 2010-09-12 00:25:58 +0000 | |||
818 | @@ -53,33 +53,27 @@ | |||
819 | 53 | def getheader(self, name): | 53 | def getheader(self, name): |
820 | 54 | return self.getheaders().get(name.lower()) | 54 | return self.getheaders().get(name.lower()) |
821 | 55 | code_iter = iter(code_iter) | 55 | code_iter = iter(code_iter) |
828 | 56 | def connect(*args, **ckwargs): | 56 | def connect(*args, **kwargs): |
829 | 57 | if 'give_content_type' in kwargs: | 57 | connect.last_args = args |
830 | 58 | if len(args) >= 7 and 'content_type' in args[6]: | 58 | connect.last_kwargs = kwargs |
825 | 59 | kwargs['give_content_type'](args[6]['content-type']) | ||
826 | 60 | else: | ||
827 | 61 | kwargs['give_content_type']('') | ||
831 | 62 | return FakeConn(code_iter.next()) | 59 | return FakeConn(code_iter.next()) |
832 | 63 | return connect | 60 | return connect |
833 | 64 | 61 | ||
834 | 65 | 62 | ||
835 | 66 | class FakeRing(object): | ||
836 | 67 | def get_nodes(self, path): | ||
837 | 68 | return 1, [{'ip': '10.0.0.%s' % x, 'port': 1000+x, 'device': 'sda'} | ||
838 | 69 | for x in xrange(3)] | ||
839 | 70 | |||
840 | 71 | |||
841 | 72 | class TestAuthServer(unittest.TestCase): | 63 | class TestAuthServer(unittest.TestCase): |
842 | 73 | 64 | ||
843 | 74 | def setUp(self): | 65 | def setUp(self): |
844 | 66 | self.ohttp_connect = auth_server.http_connect | ||
845 | 75 | self.testdir = os.path.join(os.path.dirname(__file__), | 67 | self.testdir = os.path.join(os.path.dirname(__file__), |
846 | 76 | 'auth_server') | 68 | 'auth_server') |
847 | 77 | rmtree(self.testdir, ignore_errors=1) | 69 | rmtree(self.testdir, ignore_errors=1) |
848 | 78 | os.mkdir(self.testdir) | 70 | os.mkdir(self.testdir) |
851 | 79 | self.conf = {'swift_dir': self.testdir, 'log_name': 'auth'} | 71 | self.conf = {'swift_dir': self.testdir, 'log_name': 'auth', |
852 | 80 | self.controller = auth_server.AuthController(self.conf, FakeRing()) | 72 | 'super_admin_key': 'testkey'} |
853 | 73 | self.controller = auth_server.AuthController(self.conf) | ||
854 | 81 | 74 | ||
855 | 82 | def tearDown(self): | 75 | def tearDown(self): |
856 | 76 | auth_server.http_connect = self.ohttp_connect | ||
857 | 83 | rmtree(self.testdir, ignore_errors=1) | 77 | rmtree(self.testdir, ignore_errors=1) |
858 | 84 | 78 | ||
859 | 85 | def test_get_conn(self): | 79 | def test_get_conn(self): |
860 | @@ -106,7 +100,7 @@ | |||
861 | 106 | self.assert_(conn is not None) | 100 | self.assert_(conn is not None) |
862 | 107 | 101 | ||
863 | 108 | def test_validate_token_non_existant_token(self): | 102 | def test_validate_token_non_existant_token(self): |
865 | 109 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 103 | auth_server.http_connect = fake_http_connect(201) |
866 | 110 | cfaccount = self.controller.create_user( | 104 | cfaccount = self.controller.create_user( |
867 | 111 | 'test', 'tester', 'testing',).split('/')[-1] | 105 | 'test', 'tester', 'testing',).split('/')[-1] |
868 | 112 | res = self.controller.handle_auth(Request.blank('/v1/test/auth', | 106 | res = self.controller.handle_auth(Request.blank('/v1/test/auth', |
869 | @@ -117,7 +111,7 @@ | |||
870 | 117 | self.assertEquals(self.controller.validate_token(token + 'bad'), False) | 111 | self.assertEquals(self.controller.validate_token(token + 'bad'), False) |
871 | 118 | 112 | ||
872 | 119 | def test_validate_token_good(self): | 113 | def test_validate_token_good(self): |
874 | 120 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 114 | auth_server.http_connect = fake_http_connect(201) |
875 | 121 | cfaccount = self.controller.create_user( | 115 | cfaccount = self.controller.create_user( |
876 | 122 | 'test', 'tester', 'testing',).split('/')[-1] | 116 | 'test', 'tester', 'testing',).split('/')[-1] |
877 | 123 | res = self.controller.handle_auth(Request.blank('/v1/test/auth', | 117 | res = self.controller.handle_auth(Request.blank('/v1/test/auth', |
878 | @@ -125,14 +119,14 @@ | |||
879 | 125 | headers={'X-Storage-User': 'tester', | 119 | headers={'X-Storage-User': 'tester', |
880 | 126 | 'X-Storage-Pass': 'testing'})) | 120 | 'X-Storage-Pass': 'testing'})) |
881 | 127 | token = res.headers['x-storage-token'] | 121 | token = res.headers['x-storage-token'] |
883 | 128 | ttl = self.controller.validate_token(token) | 122 | ttl, _, _, _ = self.controller.validate_token(token) |
884 | 129 | self.assert_(ttl > 0, repr(ttl)) | 123 | self.assert_(ttl > 0, repr(ttl)) |
885 | 130 | 124 | ||
886 | 131 | def test_validate_token_expired(self): | 125 | def test_validate_token_expired(self): |
887 | 132 | orig_time = auth_server.time | 126 | orig_time = auth_server.time |
888 | 133 | try: | 127 | try: |
889 | 134 | auth_server.time = lambda: 1 | 128 | auth_server.time = lambda: 1 |
891 | 135 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 129 | auth_server.http_connect = fake_http_connect(201) |
892 | 136 | cfaccount = self.controller.create_user('test', 'tester', | 130 | cfaccount = self.controller.create_user('test', 'tester', |
893 | 137 | 'testing').split('/')[-1] | 131 | 'testing').split('/')[-1] |
894 | 138 | res = self.controller.handle_auth(Request.blank('/v1/test/auth', | 132 | res = self.controller.handle_auth(Request.blank('/v1/test/auth', |
895 | @@ -140,7 +134,7 @@ | |||
896 | 140 | headers={'X-Storage-User': 'tester', | 134 | headers={'X-Storage-User': 'tester', |
897 | 141 | 'X-Storage-Pass': 'testing'})) | 135 | 'X-Storage-Pass': 'testing'})) |
898 | 142 | token = res.headers['x-storage-token'] | 136 | token = res.headers['x-storage-token'] |
900 | 143 | ttl = self.controller.validate_token(token) | 137 | ttl, _, _, _ = self.controller.validate_token(token) |
901 | 144 | self.assert_(ttl > 0, repr(ttl)) | 138 | self.assert_(ttl > 0, repr(ttl)) |
902 | 145 | auth_server.time = lambda: 1 + self.controller.token_life | 139 | auth_server.time = lambda: 1 + self.controller.token_life |
903 | 146 | self.assertEquals(self.controller.validate_token(token), False) | 140 | self.assertEquals(self.controller.validate_token(token), False) |
904 | @@ -148,107 +142,98 @@ | |||
905 | 148 | auth_server.time = orig_time | 142 | auth_server.time = orig_time |
906 | 149 | 143 | ||
907 | 150 | def test_create_user_no_new_account(self): | 144 | def test_create_user_no_new_account(self): |
909 | 151 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 145 | auth_server.http_connect = fake_http_connect(201) |
910 | 152 | result = self.controller.create_user('', 'tester', 'testing') | 146 | result = self.controller.create_user('', 'tester', 'testing') |
911 | 153 | self.assertFalse(result) | 147 | self.assertFalse(result) |
912 | 154 | 148 | ||
913 | 155 | def test_create_user_no_new_user(self): | 149 | def test_create_user_no_new_user(self): |
915 | 156 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 150 | auth_server.http_connect = fake_http_connect(201) |
916 | 157 | result = self.controller.create_user('test', '', 'testing') | 151 | result = self.controller.create_user('test', '', 'testing') |
917 | 158 | self.assertFalse(result) | 152 | self.assertFalse(result) |
918 | 159 | 153 | ||
919 | 160 | def test_create_user_no_new_password(self): | 154 | def test_create_user_no_new_password(self): |
921 | 161 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 155 | auth_server.http_connect = fake_http_connect(201) |
922 | 162 | result = self.controller.create_user('test', 'tester', '') | 156 | result = self.controller.create_user('test', 'tester', '') |
923 | 163 | self.assertFalse(result) | 157 | self.assertFalse(result) |
924 | 164 | 158 | ||
925 | 165 | def test_create_user_good(self): | 159 | def test_create_user_good(self): |
927 | 166 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 160 | auth_server.http_connect = fake_http_connect(201) |
928 | 167 | url = self.controller.create_user('test', 'tester', 'testing') | 161 | url = self.controller.create_user('test', 'tester', 'testing') |
929 | 168 | self.assert_(url) | 162 | self.assert_(url) |
930 | 169 | self.assertEquals('/'.join(url.split('/')[:-1]), | 163 | self.assertEquals('/'.join(url.split('/')[:-1]), |
931 | 170 | self.controller.default_cluster_url.rstrip('/'), repr(url)) | 164 | self.controller.default_cluster_url.rstrip('/'), repr(url)) |
932 | 171 | 165 | ||
933 | 172 | def test_recreate_accounts_none(self): | 166 | def test_recreate_accounts_none(self): |
935 | 173 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 167 | auth_server.http_connect = fake_http_connect(201) |
936 | 174 | rv = self.controller.recreate_accounts() | 168 | rv = self.controller.recreate_accounts() |
937 | 175 | self.assertEquals(rv.split()[0], '0', repr(rv)) | 169 | self.assertEquals(rv.split()[0], '0', repr(rv)) |
938 | 176 | self.assertEquals(rv.split()[-1], '[]', repr(rv)) | 170 | self.assertEquals(rv.split()[-1], '[]', repr(rv)) |
939 | 177 | 171 | ||
940 | 178 | def test_recreate_accounts_one(self): | 172 | def test_recreate_accounts_one(self): |
942 | 179 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 173 | auth_server.http_connect = fake_http_connect(201) |
943 | 180 | self.controller.create_user('test', 'tester', 'testing') | 174 | self.controller.create_user('test', 'tester', 'testing') |
945 | 181 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 175 | auth_server.http_connect = fake_http_connect(201) |
946 | 182 | rv = self.controller.recreate_accounts() | 176 | rv = self.controller.recreate_accounts() |
947 | 183 | self.assertEquals(rv.split()[0], '1', repr(rv)) | 177 | self.assertEquals(rv.split()[0], '1', repr(rv)) |
948 | 184 | self.assertEquals(rv.split()[-1], '[]', repr(rv)) | 178 | self.assertEquals(rv.split()[-1], '[]', repr(rv)) |
949 | 185 | 179 | ||
950 | 186 | def test_recreate_accounts_several(self): | 180 | def test_recreate_accounts_several(self): |
952 | 187 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 181 | auth_server.http_connect = fake_http_connect(201) |
953 | 188 | self.controller.create_user('test1', 'tester', 'testing') | 182 | self.controller.create_user('test1', 'tester', 'testing') |
955 | 189 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 183 | auth_server.http_connect = fake_http_connect(201) |
956 | 190 | self.controller.create_user('test2', 'tester', 'testing') | 184 | self.controller.create_user('test2', 'tester', 'testing') |
958 | 191 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 185 | auth_server.http_connect = fake_http_connect(201) |
959 | 192 | self.controller.create_user('test3', 'tester', 'testing') | 186 | self.controller.create_user('test3', 'tester', 'testing') |
961 | 193 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 187 | auth_server.http_connect = fake_http_connect(201) |
962 | 194 | self.controller.create_user('test4', 'tester', 'testing') | 188 | self.controller.create_user('test4', 'tester', 'testing') |
967 | 195 | auth_server.http_connect = fake_http_connect(201, 201, 201, | 189 | auth_server.http_connect = fake_http_connect(201, 201, 201, 201) |
964 | 196 | 201, 201, 201, | ||
965 | 197 | 201, 201, 201, | ||
966 | 198 | 201, 201, 201) | ||
968 | 199 | rv = self.controller.recreate_accounts() | 190 | rv = self.controller.recreate_accounts() |
969 | 200 | self.assertEquals(rv.split()[0], '4', repr(rv)) | 191 | self.assertEquals(rv.split()[0], '4', repr(rv)) |
970 | 201 | self.assertEquals(rv.split()[-1], '[]', repr(rv)) | 192 | self.assertEquals(rv.split()[-1], '[]', repr(rv)) |
971 | 202 | 193 | ||
972 | 203 | def test_recreate_accounts_one_fail(self): | 194 | def test_recreate_accounts_one_fail(self): |
974 | 204 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 195 | auth_server.http_connect = fake_http_connect(201) |
975 | 205 | url = self.controller.create_user('test', 'tester', 'testing') | 196 | url = self.controller.create_user('test', 'tester', 'testing') |
976 | 206 | cfaccount = url.split('/')[-1] | 197 | cfaccount = url.split('/')[-1] |
978 | 207 | auth_server.http_connect = fake_http_connect(500, 500, 500) | 198 | auth_server.http_connect = fake_http_connect(500) |
979 | 208 | rv = self.controller.recreate_accounts() | 199 | rv = self.controller.recreate_accounts() |
980 | 209 | self.assertEquals(rv.split()[0], '1', repr(rv)) | 200 | self.assertEquals(rv.split()[0], '1', repr(rv)) |
981 | 210 | self.assertEquals(rv.split()[-1], '[%s]' % repr(cfaccount), | 201 | self.assertEquals(rv.split()[-1], '[%s]' % repr(cfaccount), |
982 | 211 | repr(rv)) | 202 | repr(rv)) |
983 | 212 | 203 | ||
984 | 213 | def test_recreate_accounts_several_fail(self): | 204 | def test_recreate_accounts_several_fail(self): |
986 | 214 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 205 | auth_server.http_connect = fake_http_connect(201) |
987 | 215 | url = self.controller.create_user('test1', 'tester', 'testing') | 206 | url = self.controller.create_user('test1', 'tester', 'testing') |
988 | 216 | cfaccounts = [url.split('/')[-1]] | 207 | cfaccounts = [url.split('/')[-1]] |
990 | 217 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 208 | auth_server.http_connect = fake_http_connect(201) |
991 | 218 | url = self.controller.create_user('test2', 'tester', 'testing') | 209 | url = self.controller.create_user('test2', 'tester', 'testing') |
992 | 219 | cfaccounts.append(url.split('/')[-1]) | 210 | cfaccounts.append(url.split('/')[-1]) |
994 | 220 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 211 | auth_server.http_connect = fake_http_connect(201) |
995 | 221 | url = self.controller.create_user('test3', 'tester', 'testing') | 212 | url = self.controller.create_user('test3', 'tester', 'testing') |
996 | 222 | cfaccounts.append(url.split('/')[-1]) | 213 | cfaccounts.append(url.split('/')[-1]) |
998 | 223 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 214 | auth_server.http_connect = fake_http_connect(201) |
999 | 224 | url = self.controller.create_user('test4', 'tester', 'testing') | 215 | url = self.controller.create_user('test4', 'tester', 'testing') |
1000 | 225 | cfaccounts.append(url.split('/')[-1]) | 216 | cfaccounts.append(url.split('/')[-1]) |
1005 | 226 | auth_server.http_connect = fake_http_connect(500, 500, 500, | 217 | auth_server.http_connect = fake_http_connect(500, 500, 500, 500) |
1002 | 227 | 500, 500, 500, | ||
1003 | 228 | 500, 500, 500, | ||
1004 | 229 | 500, 500, 500) | ||
1006 | 230 | rv = self.controller.recreate_accounts() | 218 | rv = self.controller.recreate_accounts() |
1007 | 231 | self.assertEquals(rv.split()[0], '4', repr(rv)) | 219 | self.assertEquals(rv.split()[0], '4', repr(rv)) |
1008 | 232 | failed = rv.split('[', 1)[-1][:-1].split(', ') | 220 | failed = rv.split('[', 1)[-1][:-1].split(', ') |
1009 | 233 | self.assertEquals(set(failed), set(repr(a) for a in cfaccounts)) | 221 | self.assertEquals(set(failed), set(repr(a) for a in cfaccounts)) |
1010 | 234 | 222 | ||
1011 | 235 | def test_recreate_accounts_several_fail_some(self): | 223 | def test_recreate_accounts_several_fail_some(self): |
1013 | 236 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 224 | auth_server.http_connect = fake_http_connect(201) |
1014 | 237 | url = self.controller.create_user('test1', 'tester', 'testing') | 225 | url = self.controller.create_user('test1', 'tester', 'testing') |
1015 | 238 | cfaccounts = [url.split('/')[-1]] | 226 | cfaccounts = [url.split('/')[-1]] |
1017 | 239 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 227 | auth_server.http_connect = fake_http_connect(201) |
1018 | 240 | url = self.controller.create_user('test2', 'tester', 'testing') | 228 | url = self.controller.create_user('test2', 'tester', 'testing') |
1019 | 241 | cfaccounts.append(url.split('/')[-1]) | 229 | cfaccounts.append(url.split('/')[-1]) |
1021 | 242 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 230 | auth_server.http_connect = fake_http_connect(201) |
1022 | 243 | url = self.controller.create_user('test3', 'tester', 'testing') | 231 | url = self.controller.create_user('test3', 'tester', 'testing') |
1023 | 244 | cfaccounts.append(url.split('/')[-1]) | 232 | cfaccounts.append(url.split('/')[-1]) |
1025 | 245 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 233 | auth_server.http_connect = fake_http_connect(201) |
1026 | 246 | url = self.controller.create_user('test4', 'tester', 'testing') | 234 | url = self.controller.create_user('test4', 'tester', 'testing') |
1027 | 247 | cfaccounts.append(url.split('/')[-1]) | 235 | cfaccounts.append(url.split('/')[-1]) |
1032 | 248 | auth_server.http_connect = fake_http_connect(500, 500, 500, | 236 | auth_server.http_connect = fake_http_connect(500, 201, 500, 201) |
1029 | 249 | 201, 201, 201, | ||
1030 | 250 | 500, 500, 500, | ||
1031 | 251 | 201, 201, 201) | ||
1033 | 252 | rv = self.controller.recreate_accounts() | 237 | rv = self.controller.recreate_accounts() |
1034 | 253 | self.assertEquals(rv.split()[0], '4', repr(rv)) | 238 | self.assertEquals(rv.split()[0], '4', repr(rv)) |
1035 | 254 | failed = rv.split('[', 1)[-1][:-1].split(', ') | 239 | failed = rv.split('[', 1)[-1][:-1].split(', ') |
1036 | @@ -263,7 +248,7 @@ | |||
1037 | 263 | self.assertEquals(res.status_int, 400) | 248 | self.assertEquals(res.status_int, 400) |
1038 | 264 | 249 | ||
1039 | 265 | def test_auth_SOSO_missing_headers(self): | 250 | def test_auth_SOSO_missing_headers(self): |
1041 | 266 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 251 | auth_server.http_connect = fake_http_connect(201) |
1042 | 267 | cfaccount = self.controller.create_user( | 252 | cfaccount = self.controller.create_user( |
1043 | 268 | 'test', 'tester', 'testing').split('/')[-1] | 253 | 'test', 'tester', 'testing').split('/')[-1] |
1044 | 269 | res = self.controller.handle_auth(Request.blank('/v1/test/auth', | 254 | res = self.controller.handle_auth(Request.blank('/v1/test/auth', |
1045 | @@ -279,7 +264,7 @@ | |||
1046 | 279 | self.assertEquals(res.status_int, 401) | 264 | self.assertEquals(res.status_int, 401) |
1047 | 280 | 265 | ||
1048 | 281 | def test_auth_SOSO_bad_account(self): | 266 | def test_auth_SOSO_bad_account(self): |
1050 | 282 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 267 | auth_server.http_connect = fake_http_connect(201) |
1051 | 283 | cfaccount = self.controller.create_user( | 268 | cfaccount = self.controller.create_user( |
1052 | 284 | 'test', 'tester', 'testing').split('/')[-1] | 269 | 'test', 'tester', 'testing').split('/')[-1] |
1053 | 285 | res = self.controller.handle_auth(Request.blank('/v1/testbad/auth', | 270 | res = self.controller.handle_auth(Request.blank('/v1/testbad/auth', |
1054 | @@ -294,7 +279,7 @@ | |||
1055 | 294 | self.assertEquals(res.status_int, 401) | 279 | self.assertEquals(res.status_int, 401) |
1056 | 295 | 280 | ||
1057 | 296 | def test_auth_SOSO_bad_user(self): | 281 | def test_auth_SOSO_bad_user(self): |
1059 | 297 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 282 | auth_server.http_connect = fake_http_connect(201) |
1060 | 298 | cfaccount = self.controller.create_user( | 283 | cfaccount = self.controller.create_user( |
1061 | 299 | 'test', 'tester', 'testing').split('/')[-1] | 284 | 'test', 'tester', 'testing').split('/')[-1] |
1062 | 300 | res = self.controller.handle_auth(Request.blank('/v1/test/auth', | 285 | res = self.controller.handle_auth(Request.blank('/v1/test/auth', |
1063 | @@ -309,7 +294,7 @@ | |||
1064 | 309 | self.assertEquals(res.status_int, 401) | 294 | self.assertEquals(res.status_int, 401) |
1065 | 310 | 295 | ||
1066 | 311 | def test_auth_SOSO_bad_password(self): | 296 | def test_auth_SOSO_bad_password(self): |
1068 | 312 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 297 | auth_server.http_connect = fake_http_connect(201) |
1069 | 313 | cfaccount = self.controller.create_user( | 298 | cfaccount = self.controller.create_user( |
1070 | 314 | 'test', 'tester', 'testing').split('/')[-1] | 299 | 'test', 'tester', 'testing').split('/')[-1] |
1071 | 315 | res = self.controller.handle_auth(Request.blank('/v1/test/auth', | 300 | res = self.controller.handle_auth(Request.blank('/v1/test/auth', |
1072 | @@ -324,7 +309,7 @@ | |||
1073 | 324 | self.assertEquals(res.status_int, 401) | 309 | self.assertEquals(res.status_int, 401) |
1074 | 325 | 310 | ||
1075 | 326 | def test_auth_SOSO_good(self): | 311 | def test_auth_SOSO_good(self): |
1077 | 327 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 312 | auth_server.http_connect = fake_http_connect(201) |
1078 | 328 | cfaccount = self.controller.create_user( | 313 | cfaccount = self.controller.create_user( |
1079 | 329 | 'test', 'tester', 'testing').split('/')[-1] | 314 | 'test', 'tester', 'testing').split('/')[-1] |
1080 | 330 | res = self.controller.handle_auth(Request.blank('/v1/test/auth', | 315 | res = self.controller.handle_auth(Request.blank('/v1/test/auth', |
1081 | @@ -332,11 +317,11 @@ | |||
1082 | 332 | headers={'X-Storage-User': 'tester', | 317 | headers={'X-Storage-User': 'tester', |
1083 | 333 | 'X-Storage-Pass': 'testing'})) | 318 | 'X-Storage-Pass': 'testing'})) |
1084 | 334 | token = res.headers['x-storage-token'] | 319 | token = res.headers['x-storage-token'] |
1086 | 335 | ttl = self.controller.validate_token(token) | 320 | ttl, _, _, _ = self.controller.validate_token(token) |
1087 | 336 | self.assert_(ttl > 0, repr(ttl)) | 321 | self.assert_(ttl > 0, repr(ttl)) |
1088 | 337 | 322 | ||
1089 | 338 | def test_auth_SOSO_good_Mosso_headers(self): | 323 | def test_auth_SOSO_good_Mosso_headers(self): |
1091 | 339 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 324 | auth_server.http_connect = fake_http_connect(201) |
1092 | 340 | cfaccount = self.controller.create_user( | 325 | cfaccount = self.controller.create_user( |
1093 | 341 | 'test', 'tester', 'testing').split('/')[-1] | 326 | 'test', 'tester', 'testing').split('/')[-1] |
1094 | 342 | res = self.controller.handle_auth(Request.blank('/v1/test/auth', | 327 | res = self.controller.handle_auth(Request.blank('/v1/test/auth', |
1095 | @@ -344,11 +329,11 @@ | |||
1096 | 344 | headers={'X-Auth-User': 'test:tester', | 329 | headers={'X-Auth-User': 'test:tester', |
1097 | 345 | 'X-Auth-Key': 'testing'})) | 330 | 'X-Auth-Key': 'testing'})) |
1098 | 346 | token = res.headers['x-storage-token'] | 331 | token = res.headers['x-storage-token'] |
1100 | 347 | ttl = self.controller.validate_token(token) | 332 | ttl, _, _, _ = self.controller.validate_token(token) |
1101 | 348 | self.assert_(ttl > 0, repr(ttl)) | 333 | self.assert_(ttl > 0, repr(ttl)) |
1102 | 349 | 334 | ||
1103 | 350 | def test_auth_SOSO_bad_Mosso_headers(self): | 335 | def test_auth_SOSO_bad_Mosso_headers(self): |
1105 | 351 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 336 | auth_server.http_connect = fake_http_connect(201) |
1106 | 352 | cfaccount = self.controller.create_user( | 337 | cfaccount = self.controller.create_user( |
1107 | 353 | 'test', 'tester', 'testing',).split('/')[-1] | 338 | 'test', 'tester', 'testing',).split('/')[-1] |
1108 | 354 | res = self.controller.handle_auth(Request.blank('/v1/test/auth', | 339 | res = self.controller.handle_auth(Request.blank('/v1/test/auth', |
1109 | @@ -368,7 +353,7 @@ | |||
1110 | 368 | self.assertEquals(res.status_int, 401) | 353 | self.assertEquals(res.status_int, 401) |
1111 | 369 | 354 | ||
1112 | 370 | def test_auth_Mosso_missing_headers(self): | 355 | def test_auth_Mosso_missing_headers(self): |
1114 | 371 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 356 | auth_server.http_connect = fake_http_connect(201) |
1115 | 372 | cfaccount = self.controller.create_user( | 357 | cfaccount = self.controller.create_user( |
1116 | 373 | 'test', 'tester', 'testing').split('/')[-1] | 358 | 'test', 'tester', 'testing').split('/')[-1] |
1117 | 374 | res = self.controller.handle_auth(Request.blank('/auth', | 359 | res = self.controller.handle_auth(Request.blank('/auth', |
1118 | @@ -384,7 +369,7 @@ | |||
1119 | 384 | self.assertEquals(res.status_int, 401) | 369 | self.assertEquals(res.status_int, 401) |
1120 | 385 | 370 | ||
1121 | 386 | def test_auth_Mosso_bad_header_format(self): | 371 | def test_auth_Mosso_bad_header_format(self): |
1123 | 387 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 372 | auth_server.http_connect = fake_http_connect(201) |
1124 | 388 | cfaccount = self.controller.create_user( | 373 | cfaccount = self.controller.create_user( |
1125 | 389 | 'test', 'tester', 'testing').split('/')[-1] | 374 | 'test', 'tester', 'testing').split('/')[-1] |
1126 | 390 | res = self.controller.handle_auth(Request.blank('/auth', | 375 | res = self.controller.handle_auth(Request.blank('/auth', |
1127 | @@ -399,7 +384,7 @@ | |||
1128 | 399 | self.assertEquals(res.status_int, 401) | 384 | self.assertEquals(res.status_int, 401) |
1129 | 400 | 385 | ||
1130 | 401 | def test_auth_Mosso_bad_account(self): | 386 | def test_auth_Mosso_bad_account(self): |
1132 | 402 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 387 | auth_server.http_connect = fake_http_connect(201) |
1133 | 403 | cfaccount = self.controller.create_user( | 388 | cfaccount = self.controller.create_user( |
1134 | 404 | 'test', 'tester', 'testing').split('/')[-1] | 389 | 'test', 'tester', 'testing').split('/')[-1] |
1135 | 405 | res = self.controller.handle_auth(Request.blank('/auth', | 390 | res = self.controller.handle_auth(Request.blank('/auth', |
1136 | @@ -414,7 +399,7 @@ | |||
1137 | 414 | self.assertEquals(res.status_int, 401) | 399 | self.assertEquals(res.status_int, 401) |
1138 | 415 | 400 | ||
1139 | 416 | def test_auth_Mosso_bad_user(self): | 401 | def test_auth_Mosso_bad_user(self): |
1141 | 417 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 402 | auth_server.http_connect = fake_http_connect(201) |
1142 | 418 | cfaccount = self.controller.create_user( | 403 | cfaccount = self.controller.create_user( |
1143 | 419 | 'test', 'tester', 'testing').split('/')[-1] | 404 | 'test', 'tester', 'testing').split('/')[-1] |
1144 | 420 | res = self.controller.handle_auth(Request.blank('/auth', | 405 | res = self.controller.handle_auth(Request.blank('/auth', |
1145 | @@ -429,7 +414,7 @@ | |||
1146 | 429 | self.assertEquals(res.status_int, 401) | 414 | self.assertEquals(res.status_int, 401) |
1147 | 430 | 415 | ||
1148 | 431 | def test_auth_Mosso_bad_password(self): | 416 | def test_auth_Mosso_bad_password(self): |
1150 | 432 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 417 | auth_server.http_connect = fake_http_connect(201) |
1151 | 433 | cfaccount = self.controller.create_user( | 418 | cfaccount = self.controller.create_user( |
1152 | 434 | 'test', 'tester', 'testing').split('/')[-1] | 419 | 'test', 'tester', 'testing').split('/')[-1] |
1153 | 435 | res = self.controller.handle_auth(Request.blank('/auth', | 420 | res = self.controller.handle_auth(Request.blank('/auth', |
1154 | @@ -444,7 +429,7 @@ | |||
1155 | 444 | self.assertEquals(res.status_int, 401) | 429 | self.assertEquals(res.status_int, 401) |
1156 | 445 | 430 | ||
1157 | 446 | def test_auth_Mosso_good(self): | 431 | def test_auth_Mosso_good(self): |
1159 | 447 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 432 | auth_server.http_connect = fake_http_connect(201) |
1160 | 448 | cfaccount = self.controller.create_user( | 433 | cfaccount = self.controller.create_user( |
1161 | 449 | 'test', 'tester', 'testing').split('/')[-1] | 434 | 'test', 'tester', 'testing').split('/')[-1] |
1162 | 450 | res = self.controller.handle_auth(Request.blank('/auth', | 435 | res = self.controller.handle_auth(Request.blank('/auth', |
1163 | @@ -452,11 +437,11 @@ | |||
1164 | 452 | headers={'X-Auth-User': 'test:tester', | 437 | headers={'X-Auth-User': 'test:tester', |
1165 | 453 | 'X-Auth-Key': 'testing'})) | 438 | 'X-Auth-Key': 'testing'})) |
1166 | 454 | token = res.headers['x-storage-token'] | 439 | token = res.headers['x-storage-token'] |
1168 | 455 | ttl = self.controller.validate_token(token) | 440 | ttl, _, _, _ = self.controller.validate_token(token) |
1169 | 456 | self.assert_(ttl > 0, repr(ttl)) | 441 | self.assert_(ttl > 0, repr(ttl)) |
1170 | 457 | 442 | ||
1171 | 458 | def test_auth_Mosso_good_SOSO_header_names(self): | 443 | def test_auth_Mosso_good_SOSO_header_names(self): |
1173 | 459 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 444 | auth_server.http_connect = fake_http_connect(201) |
1174 | 460 | cfaccount = self.controller.create_user( | 445 | cfaccount = self.controller.create_user( |
1175 | 461 | 'test', 'tester', 'testing').split('/')[-1] | 446 | 'test', 'tester', 'testing').split('/')[-1] |
1176 | 462 | res = self.controller.handle_auth(Request.blank('/auth', | 447 | res = self.controller.handle_auth(Request.blank('/auth', |
1177 | @@ -464,7 +449,7 @@ | |||
1178 | 464 | headers={'X-Storage-User': 'test:tester', | 449 | headers={'X-Storage-User': 'test:tester', |
1179 | 465 | 'X-Storage-Pass': 'testing'})) | 450 | 'X-Storage-Pass': 'testing'})) |
1180 | 466 | token = res.headers['x-storage-token'] | 451 | token = res.headers['x-storage-token'] |
1182 | 467 | ttl = self.controller.validate_token(token) | 452 | ttl, _, _, _ = self.controller.validate_token(token) |
1183 | 468 | self.assert_(ttl > 0, repr(ttl)) | 453 | self.assert_(ttl > 0, repr(ttl)) |
1184 | 469 | 454 | ||
1185 | 470 | def test_basic_logging(self): | 455 | def test_basic_logging(self): |
1186 | @@ -473,11 +458,11 @@ | |||
1187 | 473 | logger = get_logger(self.conf, 'auth') | 458 | logger = get_logger(self.conf, 'auth') |
1188 | 474 | logger.logger.addHandler(log_handler) | 459 | logger.logger.addHandler(log_handler) |
1189 | 475 | try: | 460 | try: |
1191 | 476 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 461 | auth_server.http_connect = fake_http_connect(201) |
1192 | 477 | url = self.controller.create_user('test', 'tester', 'testing') | 462 | url = self.controller.create_user('test', 'tester', 'testing') |
1193 | 478 | self.assertEquals(log.getvalue().rsplit(' ', 1)[0], | 463 | self.assertEquals(log.getvalue().rsplit(' ', 1)[0], |
1196 | 479 | "auth SUCCESS create_user('test', 'tester', _, False) = %s" | 464 | "auth SUCCESS create_user('test', 'tester', _, False, False) " |
1197 | 480 | % repr(url)) | 465 | "= %s" % repr(url)) |
1198 | 481 | log.truncate(0) | 466 | log.truncate(0) |
1199 | 482 | def start_response(*args): | 467 | def start_response(*args): |
1200 | 483 | pass | 468 | pass |
1201 | @@ -603,8 +588,8 @@ | |||
1202 | 603 | conn.commit() | 588 | conn.commit() |
1203 | 604 | conn.close() | 589 | conn.close() |
1204 | 605 | # Upgrade to current db | 590 | # Upgrade to current db |
1207 | 606 | conf = {'swift_dir': swift_dir} | 591 | conf = {'swift_dir': swift_dir, 'super_admin_key': 'testkey'} |
1208 | 607 | controller = auth_server.AuthController(conf, FakeRing()) | 592 | controller = auth_server.AuthController(conf) |
1209 | 608 | # Check new items exist and are correct | 593 | # Check new items exist and are correct |
1210 | 609 | conn = get_db_connection(db_file) | 594 | conn = get_db_connection(db_file) |
1211 | 610 | row = conn.execute('SELECT admin FROM account').fetchone() | 595 | row = conn.execute('SELECT admin FROM account').fetchone() |
1212 | @@ -614,21 +599,360 @@ | |||
1213 | 614 | finally: | 599 | finally: |
1214 | 615 | rmtree(swift_dir) | 600 | rmtree(swift_dir) |
1215 | 616 | 601 | ||
1216 | 602 | def test_upgrading_from_db2(self): | ||
1217 | 603 | swift_dir = '/tmp/swift_test_auth_%s' % uuid4().hex | ||
1218 | 604 | os.mkdir(swift_dir) | ||
1219 | 605 | try: | ||
1220 | 606 | # Create db1 | ||
1221 | 607 | db_file = os.path.join(swift_dir, 'auth.db') | ||
1222 | 608 | conn = get_db_connection(db_file, okay_to_create=True) | ||
1223 | 609 | conn.execute('''CREATE TABLE IF NOT EXISTS account ( | ||
1224 | 610 | account TEXT, url TEXT, cfaccount TEXT, | ||
1225 | 611 | user TEXT, password TEXT, admin TEXT)''') | ||
1226 | 612 | conn.execute('''CREATE INDEX IF NOT EXISTS ix_account_account | ||
1227 | 613 | ON account (account)''') | ||
1228 | 614 | conn.execute('''CREATE TABLE IF NOT EXISTS token ( | ||
1229 | 615 | token TEXT, created FLOAT, | ||
1230 | 616 | account TEXT, user TEXT, cfaccount TEXT)''') | ||
1231 | 617 | conn.execute('''CREATE INDEX IF NOT EXISTS ix_token_token | ||
1232 | 618 | ON token (token)''') | ||
1233 | 619 | conn.execute('''CREATE INDEX IF NOT EXISTS ix_token_created | ||
1234 | 620 | ON token (created)''') | ||
1235 | 621 | conn.execute('''CREATE INDEX IF NOT EXISTS ix_token_account | ||
1236 | 622 | ON token (account)''') | ||
1237 | 623 | conn.execute('''INSERT INTO account | ||
1238 | 624 | (account, url, cfaccount, user, password, admin) | ||
1239 | 625 | VALUES ('act', 'url', 'cfa', 'us1', 'pas', '')''') | ||
1240 | 626 | conn.execute('''INSERT INTO account | ||
1241 | 627 | (account, url, cfaccount, user, password, admin) | ||
1242 | 628 | VALUES ('act', 'url', 'cfa', 'us2', 'pas', 't')''') | ||
1243 | 629 | conn.execute('''INSERT INTO token | ||
1244 | 630 | (token, created, account, user, cfaccount) | ||
1245 | 631 | VALUES ('tok', '1', 'act', 'us1', 'cfa')''') | ||
1246 | 632 | conn.commit() | ||
1247 | 633 | conn.close() | ||
1248 | 634 | # Upgrade to current db | ||
1249 | 635 | conf = {'swift_dir': swift_dir, 'super_admin_key': 'testkey'} | ||
1250 | 636 | controller = auth_server.AuthController(conf) | ||
1251 | 637 | # Check new items exist and are correct | ||
1252 | 638 | conn = get_db_connection(db_file) | ||
1253 | 639 | row = conn.execute('''SELECT admin, reseller_admin | ||
1254 | 640 | FROM account WHERE user = 'us1' ''').fetchone() | ||
1255 | 641 | self.assert_(not row[0], row[0]) | ||
1256 | 642 | self.assert_(not row[1], row[1]) | ||
1257 | 643 | row = conn.execute('''SELECT admin, reseller_admin | ||
1258 | 644 | FROM account WHERE user = 'us2' ''').fetchone() | ||
1259 | 645 | self.assertEquals(row[0], 't') | ||
1260 | 646 | self.assert_(not row[1], row[1]) | ||
1261 | 647 | row = conn.execute('SELECT user FROM token').fetchone() | ||
1262 | 648 | self.assert_(row) | ||
1263 | 649 | finally: | ||
1264 | 650 | rmtree(swift_dir) | ||
1265 | 651 | |||
1266 | 617 | def test_create_user_twice(self): | 652 | def test_create_user_twice(self): |
1268 | 618 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 653 | auth_server.http_connect = fake_http_connect(201) |
1269 | 619 | self.controller.create_user('test', 'tester', 'testing') | 654 | self.controller.create_user('test', 'tester', 'testing') |
1271 | 620 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 655 | auth_server.http_connect = fake_http_connect(201) |
1272 | 621 | self.assertEquals( | 656 | self.assertEquals( |
1273 | 622 | self.controller.create_user('test', 'tester', 'testing'), | 657 | self.controller.create_user('test', 'tester', 'testing'), |
1274 | 623 | 'already exists') | 658 | 'already exists') |
1275 | 624 | 659 | ||
1276 | 625 | def test_create_2users_1account(self): | 660 | def test_create_2users_1account(self): |
1278 | 626 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 661 | auth_server.http_connect = fake_http_connect(201) |
1279 | 627 | url = self.controller.create_user('test', 'tester', 'testing') | 662 | url = self.controller.create_user('test', 'tester', 'testing') |
1281 | 628 | auth_server.http_connect = fake_http_connect(201, 201, 201) | 663 | auth_server.http_connect = fake_http_connect(201) |
1282 | 629 | url2 = self.controller.create_user('test', 'tester2', 'testing2') | 664 | url2 = self.controller.create_user('test', 'tester2', 'testing2') |
1283 | 630 | self.assertEquals(url, url2) | 665 | self.assertEquals(url, url2) |
1284 | 631 | 666 | ||
1285 | 667 | def test_no_super_admin_key(self): | ||
1286 | 668 | conf = {'swift_dir': self.testdir, 'log_name': 'auth'} | ||
1287 | 669 | self.assertRaises(ValueError, auth_server.AuthController, conf) | ||
1288 | 670 | conf['super_admin_key'] = 'testkey' | ||
1289 | 671 | auth_server.AuthController(conf) | ||
1290 | 672 | |||
1291 | 673 | def test_add_storage_account(self): | ||
1292 | 674 | auth_server.http_connect = fake_http_connect(201) | ||
1293 | 675 | stgact = self.controller.add_storage_account() | ||
1294 | 676 | self.assert_(stgact.startswith(self.controller.reseller_prefix), | ||
1295 | 677 | stgact) | ||
1296 | 678 | # Make sure token given is the expected single use token | ||
1297 | 679 | token = auth_server.http_connect.last_args[-1]['X-Auth-Token'] | ||
1298 | 680 | self.assert_(self.controller.validate_token(token)) | ||
1299 | 681 | self.assert_(not self.controller.validate_token(token)) | ||
1300 | 682 | auth_server.http_connect = fake_http_connect(201) | ||
1301 | 683 | stgact = self.controller.add_storage_account('bob') | ||
1302 | 684 | self.assertEquals(stgact, 'bob') | ||
1303 | 685 | # Make sure token given is the expected single use token | ||
1304 | 686 | token = auth_server.http_connect.last_args[-1]['X-Auth-Token'] | ||
1305 | 687 | self.assert_(self.controller.validate_token(token)) | ||
1306 | 688 | self.assert_(not self.controller.validate_token(token)) | ||
1307 | 689 | |||
1308 | 690 | def test_regular_user(self): | ||
1309 | 691 | auth_server.http_connect = fake_http_connect(201) | ||
1310 | 692 | self.controller.create_user('act', 'usr', 'pas').split('/')[-1] | ||
1311 | 693 | res = self.controller.handle_auth(Request.blank('/v1.0', | ||
1312 | 694 | environ={'REQUEST_METHOD': 'GET'}, | ||
1313 | 695 | headers={'X-Auth-User': 'act:usr', 'X-Auth-Key': 'pas'})) | ||
1314 | 696 | _, _, _, stgact = \ | ||
1315 | 697 | self.controller.validate_token(res.headers['x-auth-token']) | ||
1316 | 698 | self.assertEquals(stgact, '') | ||
1317 | 699 | |||
1318 | 700 | def test_account_admin(self): | ||
1319 | 701 | auth_server.http_connect = fake_http_connect(201) | ||
1320 | 702 | stgact = self.controller.create_user( | ||
1321 | 703 | 'act', 'usr', 'pas', admin=True).split('/')[-1] | ||
1322 | 704 | res = self.controller.handle_auth(Request.blank('/v1.0', | ||
1323 | 705 | environ={'REQUEST_METHOD': 'GET'}, | ||
1324 | 706 | headers={'X-Auth-User': 'act:usr', 'X-Auth-Key': 'pas'})) | ||
1325 | 707 | _, _, _, vstgact = \ | ||
1326 | 708 | self.controller.validate_token(res.headers['x-auth-token']) | ||
1327 | 709 | self.assertEquals(stgact, vstgact) | ||
1328 | 710 | |||
1329 | 711 | def test_reseller_admin(self): | ||
1330 | 712 | auth_server.http_connect = fake_http_connect(201) | ||
1331 | 713 | self.controller.create_user( | ||
1332 | 714 | 'act', 'usr', 'pas', reseller_admin=True).split('/')[-1] | ||
1333 | 715 | res = self.controller.handle_auth(Request.blank('/v1.0', | ||
1334 | 716 | environ={'REQUEST_METHOD': 'GET'}, | ||
1335 | 717 | headers={'X-Auth-User': 'act:usr', 'X-Auth-Key': 'pas'})) | ||
1336 | 718 | _, _, _, stgact = \ | ||
1337 | 719 | self.controller.validate_token(res.headers['x-auth-token']) | ||
1338 | 720 | self.assertEquals(stgact, '.reseller_admin') | ||
1339 | 721 | |||
1340 | 722 | def test_is_account_admin(self): | ||
1341 | 723 | req = Request.blank('/', headers={'X-Auth-Admin-User': '.super_admin', | ||
1342 | 724 | 'X-Auth-Admin-Key': 'testkey'}) | ||
1343 | 725 | self.assert_(self.controller.is_account_admin(req, 'any')) | ||
1344 | 726 | req = Request.blank('/', headers={'X-Auth-Admin-User': '.super_admin', | ||
1345 | 727 | 'X-Auth-Admin-Key': 'testkey2'}) | ||
1346 | 728 | self.assert_(not self.controller.is_account_admin(req, 'any')) | ||
1347 | 729 | req = Request.blank('/', headers={'X-Auth-Admin-User': '.super_admi', | ||
1348 | 730 | 'X-Auth-Admin-Key': 'testkey'}) | ||
1349 | 731 | self.assert_(not self.controller.is_account_admin(req, 'any')) | ||
1350 | 732 | |||
1351 | 733 | auth_server.http_connect = fake_http_connect(201, 201) | ||
1352 | 734 | self.controller.create_user( | ||
1353 | 735 | 'act1', 'resadmin', 'pas', reseller_admin=True).split('/')[-1] | ||
1354 | 736 | self.controller.create_user('act1', 'usr', 'pas').split('/')[-1] | ||
1355 | 737 | self.controller.create_user( | ||
1356 | 738 | 'act2', 'actadmin', 'pas', admin=True).split('/')[-1] | ||
1357 | 739 | |||
1358 | 740 | req = Request.blank('/', headers={'X-Auth-Admin-User': 'act1:resadmin', | ||
1359 | 741 | 'X-Auth-Admin-Key': 'pas'}) | ||
1360 | 742 | self.assert_(self.controller.is_account_admin(req, 'any')) | ||
1361 | 743 | self.assert_(self.controller.is_account_admin(req, 'act1')) | ||
1362 | 744 | self.assert_(self.controller.is_account_admin(req, 'act2')) | ||
1363 | 745 | |||
1364 | 746 | req = Request.blank('/', headers={'X-Auth-Admin-User': 'act1:usr', | ||
1365 | 747 | 'X-Auth-Admin-Key': 'pas'}) | ||
1366 | 748 | self.assert_(not self.controller.is_account_admin(req, 'any')) | ||
1367 | 749 | self.assert_(not self.controller.is_account_admin(req, 'act1')) | ||
1368 | 750 | self.assert_(not self.controller.is_account_admin(req, 'act2')) | ||
1369 | 751 | |||
1370 | 752 | req = Request.blank('/', headers={'X-Auth-Admin-User': 'act2:actadmin', | ||
1371 | 753 | 'X-Auth-Admin-Key': 'pas'}) | ||
1372 | 754 | self.assert_(not self.controller.is_account_admin(req, 'any')) | ||
1373 | 755 | self.assert_(not self.controller.is_account_admin(req, 'act1')) | ||
1374 | 756 | self.assert_(self.controller.is_account_admin(req, 'act2')) | ||
1375 | 757 | |||
1376 | 758 | def test_handle_add_user_create_reseller_admin(self): | ||
1377 | 759 | auth_server.http_connect = fake_http_connect(201) | ||
1378 | 760 | self.controller.create_user('act', 'usr', 'pas') | ||
1379 | 761 | self.controller.create_user('act', 'actadmin', 'pas', admin=True) | ||
1380 | 762 | self.controller.create_user('act', 'resadmin', 'pas', | ||
1381 | 763 | reseller_admin=True) | ||
1382 | 764 | |||
1383 | 765 | req = Request.blank('/account/act/resadmin2', | ||
1384 | 766 | headers={'X-Auth-User-Key': 'pas', | ||
1385 | 767 | 'X-Auth-User-Reseller-Admin': 'true'}) | ||
1386 | 768 | resp = self.controller.handle_add_user(req) | ||
1387 | 769 | self.assert_(resp.status_int // 100 == 4, resp.status_int) | ||
1388 | 770 | |||
1389 | 771 | req = Request.blank('/account/act/resadmin2', | ||
1390 | 772 | headers={'X-Auth-User-Key': 'pas', | ||
1391 | 773 | 'X-Auth-User-Reseller-Admin': 'true', | ||
1392 | 774 | 'X-Auth-Admin-User': 'act:usr', | ||
1393 | 775 | 'X-Auth-Admin-Key': 'pas'}) | ||
1394 | 776 | resp = self.controller.handle_add_user(req) | ||
1395 | 777 | self.assert_(resp.status_int // 100 == 4, resp.status_int) | ||
1396 | 778 | |||
1397 | 779 | req = Request.blank('/account/act/resadmin2', | ||
1398 | 780 | headers={'X-Auth-User-Key': 'pas', | ||
1399 | 781 | 'X-Auth-User-Reseller-Admin': 'true', | ||
1400 | 782 | 'X-Auth-Admin-User': 'act:actadmin', | ||
1401 | 783 | 'X-Auth-Admin-Key': 'pas'}) | ||
1402 | 784 | resp = self.controller.handle_add_user(req) | ||
1403 | 785 | self.assert_(resp.status_int // 100 == 4, resp.status_int) | ||
1404 | 786 | |||
1405 | 787 | req = Request.blank('/account/act/resadmin2', | ||
1406 | 788 | headers={'X-Auth-User-Key': 'pas', | ||
1407 | 789 | 'X-Auth-User-Reseller-Admin': 'true', | ||
1408 | 790 | 'X-Auth-Admin-User': 'act:resadmin', | ||
1409 | 791 | 'X-Auth-Admin-Key': 'pas'}) | ||
1410 | 792 | resp = self.controller.handle_add_user(req) | ||
1411 | 793 | self.assert_(resp.status_int // 100 == 4, resp.status_int) | ||
1412 | 794 | |||
1413 | 795 | req = Request.blank('/account/act/resadmin2', | ||
1414 | 796 | headers={'X-Auth-User-Key': 'pas', | ||
1415 | 797 | 'X-Auth-User-Reseller-Admin': 'true', | ||
1416 | 798 | 'X-Auth-Admin-User': '.super_admin', | ||
1417 | 799 | 'X-Auth-Admin-Key': 'testkey'}) | ||
1418 | 800 | resp = self.controller.handle_add_user(req) | ||
1419 | 801 | self.assert_(resp.status_int // 100 == 2, resp.status_int) | ||
1420 | 802 | |||
1421 | 803 | def test_handle_add_user_create_account_admin(self): | ||
1422 | 804 | auth_server.http_connect = fake_http_connect(201, 201) | ||
1423 | 805 | self.controller.create_user('act', 'usr', 'pas') | ||
1424 | 806 | self.controller.create_user('act', 'actadmin', 'pas', admin=True) | ||
1425 | 807 | self.controller.create_user('act2', 'actadmin', 'pas', admin=True) | ||
1426 | 808 | self.controller.create_user('act2', 'resadmin', 'pas', | ||
1427 | 809 | reseller_admin=True) | ||
1428 | 810 | |||
1429 | 811 | req = Request.blank('/account/act/actadmin2', | ||
1430 | 812 | headers={'X-Auth-User-Key': 'pas', | ||
1431 | 813 | 'X-Auth-User-Admin': 'true'}) | ||
1432 | 814 | resp = self.controller.handle_add_user(req) | ||
1433 | 815 | self.assert_(resp.status_int // 100 == 4, resp.status_int) | ||
1434 | 816 | |||
1435 | 817 | req = Request.blank('/account/act/actadmin2', | ||
1436 | 818 | headers={'X-Auth-User-Key': 'pas', | ||
1437 | 819 | 'X-Auth-User-Admin': 'true', | ||
1438 | 820 | 'X-Auth-Admin-User': 'act:usr', | ||
1439 | 821 | 'X-Auth-Admin-Key': 'pas'}) | ||
1440 | 822 | resp = self.controller.handle_add_user(req) | ||
1441 | 823 | self.assert_(resp.status_int // 100 == 4, resp.status_int) | ||
1442 | 824 | |||
1443 | 825 | req = Request.blank('/account/act/actadmin2', | ||
1444 | 826 | headers={'X-Auth-User-Key': 'pas', | ||
1445 | 827 | 'X-Auth-User-Admin': 'true', | ||
1446 | 828 | 'X-Auth-Admin-User': 'act2:actadmin', | ||
1447 | 829 | 'X-Auth-Admin-Key': 'pas'}) | ||
1448 | 830 | resp = self.controller.handle_add_user(req) | ||
1449 | 831 | self.assert_(resp.status_int // 100 == 4, resp.status_int) | ||
1450 | 832 | |||
1451 | 833 | req = Request.blank('/account/act/actadmin2', | ||
1452 | 834 | headers={'X-Auth-User-Key': 'pas', | ||
1453 | 835 | 'X-Auth-User-Admin': 'true', | ||
1454 | 836 | 'X-Auth-Admin-User': 'act:actadmin', | ||
1455 | 837 | 'X-Auth-Admin-Key': 'pas'}) | ||
1456 | 838 | resp = self.controller.handle_add_user(req) | ||
1457 | 839 | self.assert_(resp.status_int // 100 == 2, resp.status_int) | ||
1458 | 840 | |||
1459 | 841 | req = Request.blank('/account/act/actadmin3', | ||
1460 | 842 | headers={'X-Auth-User-Key': 'pas', | ||
1461 | 843 | 'X-Auth-User-Admin': 'true', | ||
1462 | 844 | 'X-Auth-Admin-User': 'act2:resadmin', | ||
1463 | 845 | 'X-Auth-Admin-Key': 'pas'}) | ||
1464 | 846 | resp = self.controller.handle_add_user(req) | ||
1465 | 847 | self.assert_(resp.status_int // 100 == 2, resp.status_int) | ||
1466 | 848 | |||
1467 | 849 | req = Request.blank('/account/act/actadmin4', | ||
1468 | 850 | headers={'X-Auth-User-Key': 'pas', | ||
1469 | 851 | 'X-Auth-User-Admin': 'true', | ||
1470 | 852 | 'X-Auth-Admin-User': '.super_admin', | ||
1471 | 853 | 'X-Auth-Admin-Key': 'testkey'}) | ||
1472 | 854 | resp = self.controller.handle_add_user(req) | ||
1473 | 855 | self.assert_(resp.status_int // 100 == 2, resp.status_int) | ||
1474 | 856 | |||
1475 | 857 | def test_handle_add_user_create_normal_user(self): | ||
1476 | 858 | auth_server.http_connect = fake_http_connect(201, 201) | ||
1477 | 859 | self.controller.create_user('act', 'usr', 'pas') | ||
1478 | 860 | self.controller.create_user('act', 'actadmin', 'pas', admin=True) | ||
1479 | 861 | self.controller.create_user('act2', 'actadmin', 'pas', admin=True) | ||
1480 | 862 | self.controller.create_user('act2', 'resadmin', 'pas', | ||
1481 | 863 | reseller_admin=True) | ||
1482 | 864 | |||
1483 | 865 | req = Request.blank('/account/act/usr2', | ||
1484 | 866 | headers={'X-Auth-User-Key': 'pas', | ||
1485 | 867 | 'X-Auth-User-Admin': 'true'}) | ||
1486 | 868 | resp = self.controller.handle_add_user(req) | ||
1487 | 869 | self.assert_(resp.status_int // 100 == 4, resp.status_int) | ||
1488 | 870 | |||
1489 | 871 | req = Request.blank('/account/act/usr2', | ||
1490 | 872 | headers={'X-Auth-User-Key': 'pas', | ||
1491 | 873 | 'X-Auth-User-Admin': 'true', | ||
1492 | 874 | 'X-Auth-Admin-User': 'act:usr', | ||
1493 | 875 | 'X-Auth-Admin-Key': 'pas'}) | ||
1494 | 876 | resp = self.controller.handle_add_user(req) | ||
1495 | 877 | self.assert_(resp.status_int // 100 == 4, resp.status_int) | ||
1496 | 878 | |||
1497 | 879 | req = Request.blank('/account/act/usr2', | ||
1498 | 880 | headers={'X-Auth-User-Key': 'pas', | ||
1499 | 881 | 'X-Auth-User-Admin': 'true', | ||
1500 | 882 | 'X-Auth-Admin-User': 'act2:actadmin', | ||
1501 | 883 | 'X-Auth-Admin-Key': 'pas'}) | ||
1502 | 884 | resp = self.controller.handle_add_user(req) | ||
1503 | 885 | self.assert_(resp.status_int // 100 == 4, resp.status_int) | ||
1504 | 886 | |||
1505 | 887 | req = Request.blank('/account/act/usr2', | ||
1506 | 888 | headers={'X-Auth-User-Key': 'pas', | ||
1507 | 889 | 'X-Auth-User-Admin': 'true', | ||
1508 | 890 | 'X-Auth-Admin-User': 'act:actadmin', | ||
1509 | 891 | 'X-Auth-Admin-Key': 'pas'}) | ||
1510 | 892 | resp = self.controller.handle_add_user(req) | ||
1511 | 893 | self.assert_(resp.status_int // 100 == 2, resp.status_int) | ||
1512 | 894 | |||
1513 | 895 | req = Request.blank('/account/act/usr3', | ||
1514 | 896 | headers={'X-Auth-User-Key': 'pas', | ||
1515 | 897 | 'X-Auth-User-Admin': 'true', | ||
1516 | 898 | 'X-Auth-Admin-User': 'act2:resadmin', | ||
1517 | 899 | 'X-Auth-Admin-Key': 'pas'}) | ||
1518 | 900 | resp = self.controller.handle_add_user(req) | ||
1519 | 901 | self.assert_(resp.status_int // 100 == 2, resp.status_int) | ||
1520 | 902 | |||
1521 | 903 | req = Request.blank('/account/act/usr4', | ||
1522 | 904 | headers={'X-Auth-User-Key': 'pas', | ||
1523 | 905 | 'X-Auth-User-Admin': 'true', | ||
1524 | 906 | 'X-Auth-Admin-User': '.super_admin', | ||
1525 | 907 | 'X-Auth-Admin-Key': 'testkey'}) | ||
1526 | 908 | resp = self.controller.handle_add_user(req) | ||
1527 | 909 | self.assert_(resp.status_int // 100 == 2, resp.status_int) | ||
1528 | 910 | |||
1529 | 911 | def test_handle_account_recreate_permissions(self): | ||
1530 | 912 | auth_server.http_connect = fake_http_connect(201, 201) | ||
1531 | 913 | self.controller.create_user('act', 'usr', 'pas') | ||
1532 | 914 | self.controller.create_user('act', 'actadmin', 'pas', admin=True) | ||
1533 | 915 | self.controller.create_user('act', 'resadmin', 'pas', | ||
1534 | 916 | reseller_admin=True) | ||
1535 | 917 | |||
1536 | 918 | req = Request.blank('/recreate_accounts', | ||
1537 | 919 | headers={'X-Auth-User-Key': 'pas', | ||
1538 | 920 | 'X-Auth-User-Admin': 'true'}) | ||
1539 | 921 | resp = self.controller.handle_account_recreate(req) | ||
1540 | 922 | self.assert_(resp.status_int // 100 == 4, resp.status_int) | ||
1541 | 923 | |||
1542 | 924 | req = Request.blank('/recreate_accounts', | ||
1543 | 925 | headers={'X-Auth-User-Key': 'pas', | ||
1544 | 926 | 'X-Auth-User-Admin': 'true', | ||
1545 | 927 | 'X-Auth-Admin-User': 'act:usr', | ||
1546 | 928 | 'X-Auth-Admin-Key': 'pas'}) | ||
1547 | 929 | resp = self.controller.handle_account_recreate(req) | ||
1548 | 930 | self.assert_(resp.status_int // 100 == 4, resp.status_int) | ||
1549 | 931 | |||
1550 | 932 | req = Request.blank('/recreate_accounts', | ||
1551 | 933 | headers={'X-Auth-User-Key': 'pas', | ||
1552 | 934 | 'X-Auth-User-Admin': 'true', | ||
1553 | 935 | 'X-Auth-Admin-User': 'act:actadmin', | ||
1554 | 936 | 'X-Auth-Admin-Key': 'pas'}) | ||
1555 | 937 | resp = self.controller.handle_account_recreate(req) | ||
1556 | 938 | self.assert_(resp.status_int // 100 == 4, resp.status_int) | ||
1557 | 939 | |||
1558 | 940 | req = Request.blank('/recreate_accounts', | ||
1559 | 941 | headers={'X-Auth-User-Key': 'pas', | ||
1560 | 942 | 'X-Auth-User-Admin': 'true', | ||
1561 | 943 | 'X-Auth-Admin-User': 'act:resadmin', | ||
1562 | 944 | 'X-Auth-Admin-Key': 'pas'}) | ||
1563 | 945 | resp = self.controller.handle_account_recreate(req) | ||
1564 | 946 | self.assert_(resp.status_int // 100 == 4, resp.status_int) | ||
1565 | 947 | |||
1566 | 948 | req = Request.blank('/recreate_accounts', | ||
1567 | 949 | headers={'X-Auth-User-Key': 'pas', | ||
1568 | 950 | 'X-Auth-User-Admin': 'true', | ||
1569 | 951 | 'X-Auth-Admin-User': '.super_admin', | ||
1570 | 952 | 'X-Auth-Admin-Key': 'testkey'}) | ||
1571 | 953 | resp = self.controller.handle_account_recreate(req) | ||
1572 | 954 | self.assert_(resp.status_int // 100 == 2, resp.status_int) | ||
1573 | 955 | |||
1574 | 632 | 956 | ||
1575 | 633 | if __name__ == '__main__': | 957 | if __name__ == '__main__': |
1576 | 634 | unittest.main() | 958 | unittest.main() |
1577 | 635 | 959 | ||
1578 | === modified file 'test/unit/common/middleware/test_auth.py' | |||
1579 | --- test/unit/common/middleware/test_auth.py 2010-09-09 17:24:25 +0000 | |||
1580 | +++ test/unit/common/middleware/test_auth.py 2010-09-12 00:25:58 +0000 | |||
1581 | @@ -289,6 +289,37 @@ | |||
1582 | 289 | req.acl = '.r:.example.com' | 289 | req.acl = '.r:.example.com' |
1583 | 290 | self.assertEquals(self.test_auth.authorize(req), None) | 290 | self.assertEquals(self.test_auth.authorize(req), None) |
1584 | 291 | 291 | ||
1585 | 292 | def test_account_put_permissions(self): | ||
1586 | 293 | req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'}) | ||
1587 | 294 | req.remote_user = 'act:usr,act' | ||
1588 | 295 | resp = str(self.test_auth.authorize(req)) | ||
1589 | 296 | self.assert_(resp.startswith('403'), resp) | ||
1590 | 297 | |||
1591 | 298 | req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'}) | ||
1592 | 299 | req.remote_user = 'act:usr,act,AUTH_other' | ||
1593 | 300 | resp = str(self.test_auth.authorize(req)) | ||
1594 | 301 | self.assert_(resp.startswith('403'), resp) | ||
1595 | 302 | |||
1596 | 303 | # Even PUTs to your own account as account admin should fail | ||
1597 | 304 | req = Request.blank('/v1/AUTH_old', environ={'REQUEST_METHOD': 'PUT'}) | ||
1598 | 305 | req.remote_user = 'act:usr,act,AUTH_old' | ||
1599 | 306 | resp = str(self.test_auth.authorize(req)) | ||
1600 | 307 | self.assert_(resp.startswith('403'), resp) | ||
1601 | 308 | |||
1602 | 309 | req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'}) | ||
1603 | 310 | req.remote_user = 'act:usr,act,.reseller_admin' | ||
1604 | 311 | resp = self.test_auth.authorize(req) | ||
1605 | 312 | self.assertEquals(resp, None) | ||
1606 | 313 | |||
1607 | 314 | # .super_admin is not something the middleware should ever see or care | ||
1608 | 315 | # about | ||
1609 | 316 | req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'}) | ||
1610 | 317 | req.remote_user = 'act:usr,act,.super_admin' | ||
1611 | 318 | resp = self.test_auth.authorize(req) | ||
1612 | 319 | resp = str(self.test_auth.authorize(req)) | ||
1613 | 320 | self.assert_(resp.startswith('403'), resp) | ||
1614 | 321 | |||
1615 | 322 | |||
1616 | 292 | 323 | ||
1617 | 293 | if __name__ == '__main__': | 324 | if __name__ == '__main__': |
1618 | 294 | unittest.main() | 325 | unittest.main() |
1619 | 295 | 326 | ||
1620 | === modified file 'test/unit/proxy/test_server.py' | |||
1621 | --- test/unit/proxy/test_server.py 2010-09-09 05:37:27 +0000 | |||
1622 | +++ test/unit/proxy/test_server.py 2010-09-12 00:25:58 +0000 | |||
1623 | @@ -2153,90 +2153,136 @@ | |||
1624 | 2153 | finally: | 2153 | finally: |
1625 | 2154 | self.app.object_chunk_size = orig_object_chunk_size | 2154 | self.app.object_chunk_size = orig_object_chunk_size |
1626 | 2155 | 2155 | ||
1627 | 2156 | def test_PUT(self): | ||
1628 | 2157 | with save_globals(): | ||
1629 | 2158 | controller = proxy_server.AccountController(self.app, 'account') | ||
1630 | 2159 | def test_status_map(statuses, expected, **kwargs): | ||
1631 | 2160 | proxy_server.http_connect = \ | ||
1632 | 2161 | fake_http_connect(*statuses, **kwargs) | ||
1633 | 2162 | self.app.memcache.store = {} | ||
1634 | 2163 | req = Request.blank('/a', {}) | ||
1635 | 2164 | req.content_length = 0 | ||
1636 | 2165 | self.app.update_request(req) | ||
1637 | 2166 | res = controller.PUT(req) | ||
1638 | 2167 | expected = str(expected) | ||
1639 | 2168 | self.assertEquals(res.status[:len(expected)], expected) | ||
1640 | 2169 | test_status_map((201, 201, 201), 201) | ||
1641 | 2170 | test_status_map((201, 201, 500), 201) | ||
1642 | 2171 | test_status_map((201, 500, 500), 503) | ||
1643 | 2172 | test_status_map((204, 500, 404), 503) | ||
1644 | 2173 | |||
1645 | 2174 | def test_PUT_max_account_name_length(self): | ||
1646 | 2175 | with save_globals(): | ||
1647 | 2176 | controller = proxy_server.AccountController(self.app, '1'*256) | ||
1648 | 2177 | self.assert_status_map(controller.PUT, (201, 201, 201), 201) | ||
1649 | 2178 | controller = proxy_server.AccountController(self.app, '2'*257) | ||
1650 | 2179 | self.assert_status_map(controller.PUT, (201, 201, 201), 400) | ||
1651 | 2180 | |||
1652 | 2181 | def test_PUT_connect_exceptions(self): | ||
1653 | 2182 | with save_globals(): | ||
1654 | 2183 | controller = proxy_server.AccountController(self.app, 'account') | ||
1655 | 2184 | self.assert_status_map(controller.PUT, (201, 201, -1), 201) | ||
1656 | 2185 | self.assert_status_map(controller.PUT, (201, -1, -1), 503) | ||
1657 | 2186 | self.assert_status_map(controller.PUT, (503, 503, -1), 503) | ||
1658 | 2187 | |||
1659 | 2188 | def test_PUT_metadata(self): | ||
1660 | 2189 | self.metadata_helper('PUT') | ||
1661 | 2190 | |||
1662 | 2156 | def test_POST_metadata(self): | 2191 | def test_POST_metadata(self): |
1663 | 2192 | self.metadata_helper('POST') | ||
1664 | 2193 | |||
1665 | 2194 | def metadata_helper(self, method): | ||
1666 | 2157 | for test_header, test_value in ( | 2195 | for test_header, test_value in ( |
1667 | 2158 | ('X-Account-Meta-TestHeader', 'TestValue'), | 2196 | ('X-Account-Meta-TestHeader', 'TestValue'), |
1668 | 2159 | ('X-Account-Meta-TestHeader', '')): | 2197 | ('X-Account-Meta-TestHeader', '')): |
1669 | 2160 | test_errors = [] | 2198 | test_errors = [] |
1670 | 2161 | def test_connect(ipaddr, port, device, partition, method, path, | 2199 | def test_connect(ipaddr, port, device, partition, method, path, |
1671 | 2162 | headers=None, query_string=None): | 2200 | headers=None, query_string=None): |
1679 | 2163 | for k, v in headers.iteritems(): | 2201 | if path == '/a': |
1680 | 2164 | if k.lower() == test_header.lower() and \ | 2202 | for k, v in headers.iteritems(): |
1681 | 2165 | v == test_value: | 2203 | if k.lower() == test_header.lower() and \ |
1682 | 2166 | break | 2204 | v == test_value: |
1683 | 2167 | else: | 2205 | break |
1684 | 2168 | test_errors.append('%s: %s not in %s' % | 2206 | else: |
1685 | 2169 | (test_header, test_value, headers)) | 2207 | test_errors.append('%s: %s not in %s' % |
1686 | 2208 | (test_header, test_value, headers)) | ||
1687 | 2170 | with save_globals(): | 2209 | with save_globals(): |
1688 | 2171 | controller = \ | 2210 | controller = \ |
1689 | 2172 | proxy_server.AccountController(self.app, 'a') | 2211 | proxy_server.AccountController(self.app, 'a') |
1690 | 2173 | proxy_server.http_connect = fake_http_connect(201, 201, 201, | 2212 | proxy_server.http_connect = fake_http_connect(201, 201, 201, |
1693 | 2174 | give_connect=test_connect) | 2213 | give_connect=test_connect) |
1694 | 2175 | req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'}, | 2214 | req = Request.blank('/a/c', environ={'REQUEST_METHOD': method}, |
1695 | 2176 | headers={test_header: test_value}) | 2215 | headers={test_header: test_value}) |
1696 | 2177 | self.app.update_request(req) | 2216 | self.app.update_request(req) |
1698 | 2178 | res = controller.POST(req) | 2217 | res = getattr(controller, method)(req) |
1699 | 2179 | self.assertEquals(test_errors, []) | 2218 | self.assertEquals(test_errors, []) |
1700 | 2180 | 2219 | ||
1701 | 2220 | |||
1702 | 2221 | def test_PUT_bad_metadata(self): | ||
1703 | 2222 | self.bad_metadata_helper('PUT') | ||
1704 | 2223 | |||
1705 | 2181 | def test_POST_bad_metadata(self): | 2224 | def test_POST_bad_metadata(self): |
1706 | 2225 | self.bad_metadata_helper('POST') | ||
1707 | 2226 | |||
1708 | 2227 | def bad_metadata_helper(self, method): | ||
1709 | 2182 | with save_globals(): | 2228 | with save_globals(): |
1710 | 2183 | controller = proxy_server.AccountController(self.app, 'a') | 2229 | controller = proxy_server.AccountController(self.app, 'a') |
1713 | 2184 | proxy_server.http_connect = fake_http_connect(204, 204, 204) | 2230 | proxy_server.http_connect = fake_http_connect(200, 201, 201, 201) |
1714 | 2185 | req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'}) | 2231 | req = Request.blank('/a/c', environ={'REQUEST_METHOD': method}) |
1715 | 2186 | self.app.update_request(req) | 2232 | self.app.update_request(req) |
1718 | 2187 | resp = controller.POST(req) | 2233 | resp = getattr(controller, method)(req) |
1719 | 2188 | self.assertEquals(resp.status_int, 204) | 2234 | self.assertEquals(resp.status_int, 201) |
1720 | 2189 | 2235 | ||
1723 | 2190 | proxy_server.http_connect = fake_http_connect(204, 204, 204) | 2236 | proxy_server.http_connect = fake_http_connect(201, 201, 201) |
1724 | 2191 | req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'}, | 2237 | req = Request.blank('/a/c', environ={'REQUEST_METHOD': method}, |
1725 | 2192 | headers={'X-Account-Meta-' + | 2238 | headers={'X-Account-Meta-' + |
1726 | 2193 | ('a' * MAX_META_NAME_LENGTH): 'v'}) | 2239 | ('a' * MAX_META_NAME_LENGTH): 'v'}) |
1727 | 2194 | self.app.update_request(req) | 2240 | self.app.update_request(req) |
1732 | 2195 | resp = controller.POST(req) | 2241 | resp = getattr(controller, method)(req) |
1733 | 2196 | self.assertEquals(resp.status_int, 204) | 2242 | self.assertEquals(resp.status_int, 201) |
1734 | 2197 | proxy_server.http_connect = fake_http_connect(204, 204, 204) | 2243 | proxy_server.http_connect = fake_http_connect(201, 201, 201) |
1735 | 2198 | req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'}, | 2244 | req = Request.blank('/a/c', environ={'REQUEST_METHOD': method}, |
1736 | 2199 | headers={'X-Account-Meta-' + | 2245 | headers={'X-Account-Meta-' + |
1737 | 2200 | ('a' * (MAX_META_NAME_LENGTH + 1)): 'v'}) | 2246 | ('a' * (MAX_META_NAME_LENGTH + 1)): 'v'}) |
1738 | 2201 | self.app.update_request(req) | 2247 | self.app.update_request(req) |
1740 | 2202 | resp = controller.POST(req) | 2248 | resp = getattr(controller, method)(req) |
1741 | 2203 | self.assertEquals(resp.status_int, 400) | 2249 | self.assertEquals(resp.status_int, 400) |
1742 | 2204 | 2250 | ||
1745 | 2205 | proxy_server.http_connect = fake_http_connect(204, 204, 204) | 2251 | proxy_server.http_connect = fake_http_connect(201, 201, 201) |
1746 | 2206 | req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'}, | 2252 | req = Request.blank('/a/c', environ={'REQUEST_METHOD': method}, |
1747 | 2207 | headers={'X-Account-Meta-Too-Long': | 2253 | headers={'X-Account-Meta-Too-Long': |
1748 | 2208 | 'a' * MAX_META_VALUE_LENGTH}) | 2254 | 'a' * MAX_META_VALUE_LENGTH}) |
1749 | 2209 | self.app.update_request(req) | 2255 | self.app.update_request(req) |
1754 | 2210 | resp = controller.POST(req) | 2256 | resp = getattr(controller, method)(req) |
1755 | 2211 | self.assertEquals(resp.status_int, 204) | 2257 | self.assertEquals(resp.status_int, 201) |
1756 | 2212 | proxy_server.http_connect = fake_http_connect(204, 204, 204) | 2258 | proxy_server.http_connect = fake_http_connect(201, 201, 201) |
1757 | 2213 | req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'}, | 2259 | req = Request.blank('/a/c', environ={'REQUEST_METHOD': method}, |
1758 | 2214 | headers={'X-Account-Meta-Too-Long': | 2260 | headers={'X-Account-Meta-Too-Long': |
1759 | 2215 | 'a' * (MAX_META_VALUE_LENGTH + 1)}) | 2261 | 'a' * (MAX_META_VALUE_LENGTH + 1)}) |
1760 | 2216 | self.app.update_request(req) | 2262 | self.app.update_request(req) |
1762 | 2217 | resp = controller.POST(req) | 2263 | resp = getattr(controller, method)(req) |
1763 | 2218 | self.assertEquals(resp.status_int, 400) | 2264 | self.assertEquals(resp.status_int, 400) |
1764 | 2219 | 2265 | ||
1766 | 2220 | proxy_server.http_connect = fake_http_connect(204, 204, 204) | 2266 | proxy_server.http_connect = fake_http_connect(201, 201, 201) |
1767 | 2221 | headers = {} | 2267 | headers = {} |
1768 | 2222 | for x in xrange(MAX_META_COUNT): | 2268 | for x in xrange(MAX_META_COUNT): |
1769 | 2223 | headers['X-Account-Meta-%d' % x] = 'v' | 2269 | headers['X-Account-Meta-%d' % x] = 'v' |
1771 | 2224 | req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'}, | 2270 | req = Request.blank('/a/c', environ={'REQUEST_METHOD': method}, |
1772 | 2225 | headers=headers) | 2271 | headers=headers) |
1773 | 2226 | self.app.update_request(req) | 2272 | self.app.update_request(req) |
1777 | 2227 | resp = controller.POST(req) | 2273 | resp = getattr(controller, method)(req) |
1778 | 2228 | self.assertEquals(resp.status_int, 204) | 2274 | self.assertEquals(resp.status_int, 201) |
1779 | 2229 | proxy_server.http_connect = fake_http_connect(204, 204, 204) | 2275 | proxy_server.http_connect = fake_http_connect(201, 201, 201) |
1780 | 2230 | headers = {} | 2276 | headers = {} |
1781 | 2231 | for x in xrange(MAX_META_COUNT + 1): | 2277 | for x in xrange(MAX_META_COUNT + 1): |
1782 | 2232 | headers['X-Account-Meta-%d' % x] = 'v' | 2278 | headers['X-Account-Meta-%d' % x] = 'v' |
1784 | 2233 | req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'}, | 2279 | req = Request.blank('/a/c', environ={'REQUEST_METHOD': method}, |
1785 | 2234 | headers=headers) | 2280 | headers=headers) |
1786 | 2235 | self.app.update_request(req) | 2281 | self.app.update_request(req) |
1788 | 2236 | resp = controller.POST(req) | 2282 | resp = getattr(controller, method)(req) |
1789 | 2237 | self.assertEquals(resp.status_int, 400) | 2283 | self.assertEquals(resp.status_int, 400) |
1790 | 2238 | 2284 | ||
1792 | 2239 | proxy_server.http_connect = fake_http_connect(204, 204, 204) | 2285 | proxy_server.http_connect = fake_http_connect(201, 201, 201) |
1793 | 2240 | headers = {} | 2286 | headers = {} |
1794 | 2241 | header_value = 'a' * MAX_META_VALUE_LENGTH | 2287 | header_value = 'a' * MAX_META_VALUE_LENGTH |
1795 | 2242 | size = 0 | 2288 | size = 0 |
1796 | @@ -2248,18 +2294,18 @@ | |||
1797 | 2248 | if MAX_META_OVERALL_SIZE - size > 1: | 2294 | if MAX_META_OVERALL_SIZE - size > 1: |
1798 | 2249 | headers['X-Account-Meta-a'] = \ | 2295 | headers['X-Account-Meta-a'] = \ |
1799 | 2250 | 'a' * (MAX_META_OVERALL_SIZE - size - 1) | 2296 | 'a' * (MAX_META_OVERALL_SIZE - size - 1) |
1801 | 2251 | req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'}, | 2297 | req = Request.blank('/a/c', environ={'REQUEST_METHOD': method}, |
1802 | 2252 | headers=headers) | 2298 | headers=headers) |
1803 | 2253 | self.app.update_request(req) | 2299 | self.app.update_request(req) |
1807 | 2254 | resp = controller.POST(req) | 2300 | resp = getattr(controller, method)(req) |
1808 | 2255 | self.assertEquals(resp.status_int, 204) | 2301 | self.assertEquals(resp.status_int, 201) |
1809 | 2256 | proxy_server.http_connect = fake_http_connect(204, 204, 204) | 2302 | proxy_server.http_connect = fake_http_connect(201, 201, 201) |
1810 | 2257 | headers['X-Account-Meta-a'] = \ | 2303 | headers['X-Account-Meta-a'] = \ |
1811 | 2258 | 'a' * (MAX_META_OVERALL_SIZE - size) | 2304 | 'a' * (MAX_META_OVERALL_SIZE - size) |
1813 | 2259 | req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'}, | 2305 | req = Request.blank('/a/c', environ={'REQUEST_METHOD': method}, |
1814 | 2260 | headers=headers) | 2306 | headers=headers) |
1815 | 2261 | self.app.update_request(req) | 2307 | self.app.update_request(req) |
1817 | 2262 | resp = controller.POST(req) | 2308 | resp = getattr(controller, method)(req) |
1818 | 2263 | self.assertEquals(resp.status_int, 400) | 2309 | self.assertEquals(resp.status_int, 400) |
1819 | 2264 | 2310 | ||
1820 | 2265 | 2311 |
The implementation is done, existing tests and documentation has been updated. Just need tests and docs for new functionality.