Merge lp:~gholt/swift/authlock into lp:~hudson-openstack/swift/trunk

Proposed by gholt
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
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-add-user:
    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-auth-add-user account user key" becomes "swift-auth-add-user -K devauth account user key"

swift-auth-recreate-accounts:
    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-auth-recreate-accounts" becomes "swift-auth-recreate-accounts -K devauth"

Code changes:

swift/auth/server.py:
    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/middware/auth.py:
    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/server.py:
    Supports account PUTs. Moved MAX_CONTAINER_NAME_LENGTH to swift/common/constraints.py

And, of course, doc and test updates.

To post a comment you must log in.
Revision history for this message
gholt (gholt) wrote :

The implementation is done, existing tests and documentation has been updated. Just need tests and docs for new functionality.

Revision history for this message
Chuck Thier (cthier) wrote :

Looks good to me

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bin/swift-auth-add-user'
--- bin/swift-auth-add-user 2010-09-06 02:53:08 +0000
+++ bin/swift-auth-add-user 2010-09-12 00:25:58 +0000
@@ -33,6 +33,16 @@
33 default=False, help='Give the user administrator access; otherwise '33 default=False, help='Give the user administrator access; otherwise '
34 'the user will only have access to containers specifically allowed '34 'the user will only have access to containers specifically allowed '
35 'with ACLs.')35 'with ACLs.')
36 parser.add_option('-r', '--reseller-admin', dest='reseller_admin',
37 action='store_true', default=False, help='Give the user full reseller '
38 'administrator access, giving them full access to all accounts within '
39 'the reseller, including the ability to create new accounts. Creating '
40 'a new reseller admin requires super_admin rights.')
41 parser.add_option('-U', '--admin-user', dest='admin_user',
42 default='.super_admin', help='The user with admin rights to add users '
43 '(default: .super_admin).')
44 parser.add_option('-K', '--admin-key', dest='admin_key',
45 help='The key for the user with admin rights to add users.')
36 args = argv[1:]46 args = argv[1:]
37 if not args:47 if not args:
38 args.append('-h')48 args.append('-h')
@@ -48,9 +58,13 @@
48 port = int(conf.get('bind_port', 11000))58 port = int(conf.get('bind_port', 11000))
49 ssl = conf.get('cert_file') is not None59 ssl = conf.get('cert_file') is not None
50 path = '/account/%s/%s' % (account, user)60 path = '/account/%s/%s' % (account, user)
51 headers = {'X-Auth-User-Key': password}61 headers = {'X-Auth-Admin-User': options.admin_user,
62 'X-Auth-Admin-Key': options.admin_key,
63 'X-Auth-User-Key': password}
52 if options.admin:64 if options.admin:
53 headers['X-Auth-User-Admin'] = 'true'65 headers['X-Auth-User-Admin'] = 'true'
66 if options.reseller_admin:
67 headers['X-Auth-User-Reseller-Admin'] = 'true'
54 conn = http_connect(host, port, 'PUT', path, headers, ssl=ssl)68 conn = http_connect(host, port, 'PUT', path, headers, ssl=ssl)
55 resp = conn.getresponse()69 resp = conn.getresponse()
56 if resp.status == 204:70 if resp.status == 204:
5771
=== modified file 'bin/swift-auth-recreate-accounts'
--- bin/swift-auth-recreate-accounts 2010-08-20 00:50:12 +0000
+++ bin/swift-auth-recreate-accounts 2010-09-12 00:25:58 +0000
@@ -15,25 +15,37 @@
15# limitations under the License.15# limitations under the License.
1616
17from ConfigParser import ConfigParser17from ConfigParser import ConfigParser
18from optparse import OptionParser
18from sys import argv, exit19from sys import argv, exit
1920
20from swift.common.bufferedhttp import http_connect_raw as http_connect21from swift.common.bufferedhttp import http_connect_raw as http_connect
2122
22if __name__ == '__main__':23if __name__ == '__main__':
23 f = '/etc/swift/auth-server.conf'24 default_conf = '/etc/swift/auth-server.conf'
24 if len(argv) == 2:25 parser = OptionParser(usage='Usage: %prog [options]')
25 f = argv[1]26 parser.add_option('-c', '--conf', dest='conf', default=default_conf,
26 elif len(argv) != 1:27 help='Configuration file to determine how to connect to the local '
27 exit('Syntax: %s [conf_file]' % argv[0])28 'auth server (default: %s).' % default_conf)
29 parser.add_option('-U', '--admin-user', dest='admin_user',
30 default='.super_admin', help='The user with admin rights to recreate '
31 'accounts (default: .super_admin).')
32 parser.add_option('-K', '--admin-key', dest='admin_key',
33 help='The key for the user with admin rights to recreate accounts.')
34 args = argv[1:]
35 if not args:
36 args.append('-h')
37 (options, args) = parser.parse_args(args)
28 c = ConfigParser()38 c = ConfigParser()
29 if not c.read(f):39 if not c.read(options.conf):
30 exit('Unable to read conf file: %s' % f)40 exit('Unable to read conf file: %s' % options.conf)
31 conf = dict(c.items('app:auth-server'))41 conf = dict(c.items('app:auth-server'))
32 host = conf.get('bind_ip', '127.0.0.1')42 host = conf.get('bind_ip', '127.0.0.1')
33 port = int(conf.get('bind_port', 11000))43 port = int(conf.get('bind_port', 11000))
34 ssl = conf.get('cert_file') is not None44 ssl = conf.get('cert_file') is not None
35 path = '/recreate_accounts'45 path = '/recreate_accounts'
36 conn = http_connect(host, port, 'POST', path, ssl=ssl)46 conn = http_connect(host, port, 'POST', path, ssl=ssl,
47 headers={'X-Auth-Admin-User': options.admin_user,
48 'X-Auth-Admin-Key': options.admin_key})
37 resp = conn.getresponse()49 resp = conn.getresponse()
38 if resp.status == 200:50 if resp.status == 200:
39 print resp.read()51 print resp.read()
4052
=== modified file 'doc/source/development_auth.rst'
--- doc/source/development_auth.rst 2010-09-09 17:24:25 +0000
+++ doc/source/development_auth.rst 2010-09-12 00:25:58 +0000
@@ -6,10 +6,13 @@
6Creating Your Own Auth Server and Middleware6Creating Your Own Auth Server and Middleware
7--------------------------------------------7--------------------------------------------
88
9The included swift/common/middleware/auth.py is a good minimal example of how9The included swift/auth/server.py and swift/common/middleware/auth.py are good
10to create auth middleware. The main points are that the auth middleware can10minimal examples of how to create an external auth server and proxy server auth
11reject requests up front, before they ever get to the Swift Proxy application,11middleware. Also, see the `Swauth <https://launchpad.net/swauth>`_ project for
12and afterwards when the proxy issues callbacks to verify authorization.12a more complete implementation. The main points are that the auth middleware
13can reject requests up front, before they ever get to the Swift Proxy
14application, and afterwards when the proxy issues callbacks to verify
15authorization.
1316
14It's generally good to separate the authentication and authorization17It's generally good to separate the authentication and authorization
15procedures. Authentication verifies that a request actually comes from who it18procedures. Authentication verifies that a request actually comes from who it
1619
=== modified file 'doc/source/development_saio.rst'
--- doc/source/development_saio.rst 2010-09-06 02:53:08 +0000
+++ doc/source/development_saio.rst 2010-09-12 00:25:58 +0000
@@ -177,6 +177,8 @@
177 [app:auth-server]177 [app:auth-server]
178 use = egg:swift#auth178 use = egg:swift#auth
179 default_cluster_url = http://127.0.0.1:8080/v1179 default_cluster_url = http://127.0.0.1:8080/v1
180 # Highly recommended to change this.
181 super_admin_key = devauth
180182
181 #. Create `/etc/swift/proxy-server.conf`::183 #. Create `/etc/swift/proxy-server.conf`::
182184
@@ -511,7 +513,9 @@
511513
512 #!/bin/bash514 #!/bin/bash
513515
514 swift-auth-recreate-accounts516 # Replace devauth with whatever your super_admin key is (recorded in
517 # /etc/swift/auth-server.conf).
518 swift-auth-recreate-accounts -K devauth
515 swift-init object-updater start519 swift-init object-updater start
516 swift-init container-updater start520 swift-init container-updater start
517 swift-init object-replicator start521 swift-init object-replicator start
@@ -526,12 +530,12 @@
526 #. `remakerings`530 #. `remakerings`
527 #. `cd ~/swift/trunk; ./.unittests`531 #. `cd ~/swift/trunk; ./.unittests`
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.)
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).
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``
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>``
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`
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).
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).
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`
536 #. `cd ~/swift/trunk; ./.functests` (Note: functional tests will first delete540 #. `cd ~/swift/trunk; ./.functests` (Note: functional tests will first delete
537 everything in the configured accounts.)541 everything in the configured accounts.)
538542
=== modified file 'doc/source/howto_cyberduck.rst'
--- doc/source/howto_cyberduck.rst 2010-09-06 02:21:08 +0000
+++ doc/source/howto_cyberduck.rst 2010-09-12 00:25:58 +0000
@@ -90,26 +90,43 @@
9090
91#. Example proxy-server config::91#. Example proxy-server config::
9292
93 [proxy-server]93 [DEFAULT]
94 bind_port = 8080
95 user = swift
96 cert_file = /etc/swift/cert.crt94 cert_file = /etc/swift/cert.crt
97 key_file = /etc/swift/cert.key95 key_file = /etc/swift/cert.key
9896
99 [auth-server]97 [pipeline:main]
98 pipeline = healthcheck cache auth proxy-server
99
100 [app:proxy-server]
101 use = egg:swift#proxy
102
103 [filter:auth]
104 use = egg:swift#auth
100 ssl = true105 ssl = true
106
107 [filter:healthcheck]
108 use = egg:swift#healthcheck
109
110 [filter:cache]
111 use = egg:swift#memcache
101112
102#. Example auth-server config::113#. Example auth-server config::
103114
104 [auth-server]115 [DEFAULT]
116 cert_file = /etc/swift/cert.crt
117 key_file = /etc/swift/cert.key
118
119 [pipeline:main]
120 pipeline = auth-server
121
122 [app:auth-server]
123 use = egg:swift#auth
124 super_admin_key = devauth
105 default_cluster_url = https://ec2-184-72-156-130.compute-1.amazonaws.com:8080/v1125 default_cluster_url = https://ec2-184-72-156-130.compute-1.amazonaws.com:8080/v1
106 user = swift
107 cert_file = /etc/swift/cert.crt
108 key_file = /etc/swift/cert.key
109126
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::
111128
112 ubuntu@domU-12-31-39-03-CD-06:/home/swift/swift/bin$ swift-auth-add-user --admin a3 b3 c3129 ubuntu@domU-12-31-39-03-CD-06:/home/swift/swift/bin$ swift-auth-add-user -K devauth -a a3 b3 c3
113 https://ec2-184-72-156-130.compute-1.amazonaws.com:8080/v1/06228ccf-6d0a-4395-889e-e971e8de8781130 https://ec2-184-72-156-130.compute-1.amazonaws.com:8080/v1/06228ccf-6d0a-4395-889e-e971e8de8781
114131
115 .. note::132 .. note::
116133
=== modified file 'etc/auth-server.conf-sample'
--- etc/auth-server.conf-sample 2010-08-24 14:08:16 +0000
+++ etc/auth-server.conf-sample 2010-09-12 00:25:58 +0000
@@ -12,6 +12,8 @@
1212
13[app:auth-server]13[app:auth-server]
14use = egg:swift#auth14use = egg:swift#auth
15# Highly recommended to change this.
16super_admin_key = devauth
15# log_name = auth-server17# log_name = auth-server
16# log_facility = LOG_LOCAL018# log_facility = LOG_LOCAL0
17# log_level = INFO19# log_level = INFO
1820
=== modified file 'swift/auth/server.py'
--- swift/auth/server.py 2010-09-09 17:24:25 +0000
+++ swift/auth/server.py 2010-09-12 00:25:58 +0000
@@ -14,23 +14,21 @@
14# limitations under the License.14# limitations under the License.
1515
16from __future__ import with_statement16from __future__ import with_statement
17import errno
18import os17import os
19import socket
20from contextlib import contextmanager18from contextlib import contextmanager
21from time import gmtime, strftime, time19from time import gmtime, strftime, time
22from urllib import unquote, quote20from urllib import unquote, quote
23from uuid import uuid421from uuid import uuid4
22from urlparse import urlparse
2423
25import sqlite324import sqlite3
26from webob import Request, Response25from webob import Request, Response
27from webob.exc import HTTPBadRequest, HTTPNoContent, HTTPUnauthorized, \26from webob.exc import HTTPBadRequest, HTTPForbidden, HTTPNoContent, \
28 HTTPServiceUnavailable, HTTPNotFound27 HTTPUnauthorized, HTTPServiceUnavailable, HTTPNotFound
2928
30from swift.common.bufferedhttp import http_connect29from swift.common.bufferedhttp import http_connect_raw as http_connect
31from swift.common.db import get_db_connection30from swift.common.db import get_db_connection
32from swift.common.ring import Ring31from swift.common.utils import get_logger, split_path
33from swift.common.utils import get_logger, normalize_timestamp, split_path
3432
3533
36class AuthController(object):34class AuthController(object):
@@ -69,8 +67,7 @@
6967
70 * The developer makes a ReST call to create a new user.68 * The developer makes a ReST call to create a new user.
71 * If the account for the user does not yet exist, the auth server makes69 * If the account for the user does not yet exist, the auth server makes
72 ReST calls to the Swift cluster's account servers to create a new account70 a ReST call to the Swift cluster to create a new account on its end.
73 on its end.
74 * The auth server records the information in its database.71 * The auth server records the information in its database.
7572
76 A last use case is recreating existing accounts; this is really only useful73 A last use case is recreating existing accounts; this is really only useful
@@ -78,34 +75,34 @@
78 the auth server's database is retained:75 the auth server's database is retained:
7976
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.
81 * For each account in its database, the auth server makes ReST calls to78 * For each account in its database, the auth server makes a ReST call to
82 the Swift cluster's account servers to create a specific account on its79 the Swift cluster to create the specific account on its end.
83 end.
8480
85 :param conf: The [auth-server] dictionary of the auth server configuration81 :param conf: The [auth-server] dictionary of the auth server configuration
86 file82 file
87 :param ring: Overrides loading the account ring from a file; useful for
88 testing.
8983
90 See the etc/auth-server.conf-sample for information on the possible84 See the etc/auth-server.conf-sample for information on the possible
91 configuration parameters.85 configuration parameters.
92 """86 """
9387
94 def __init__(self, conf, ring=None):88 def __init__(self, conf):
95 self.logger = get_logger(conf)89 self.logger = get_logger(conf)
90 self.super_admin_key = conf.get('super_admin_key')
91 if not self.super_admin_key:
92 msg = 'No super_admin_key set in conf file! Exiting.'
93 try:
94 self.logger.critical(msg)
95 except:
96 pass
97 raise ValueError(msg)
96 self.swift_dir = conf.get('swift_dir', '/etc/swift')98 self.swift_dir = conf.get('swift_dir', '/etc/swift')
97 self.reseller_prefix = conf.get('reseller_prefix', 'AUTH').strip()99 self.reseller_prefix = conf.get('reseller_prefix', 'AUTH').strip()
98 if self.reseller_prefix and self.reseller_prefix[-1] != '_':100 if self.reseller_prefix and self.reseller_prefix[-1] != '_':
99 self.reseller_prefix += '_'101 self.reseller_prefix += '_'
100 self.default_cluster_url = \102 self.default_cluster_url = conf.get('default_cluster_url',
101 conf.get('default_cluster_url', 'http://127.0.0.1:8080/v1')103 'http://127.0.0.1:8080/v1').rstrip('/')
102 self.token_life = int(conf.get('token_life', 86400))104 self.token_life = int(conf.get('token_life', 86400))
103 self.log_headers = conf.get('log_headers') == 'True'105 self.log_headers = conf.get('log_headers') == 'True'
104 if ring:
105 self.account_ring = ring
106 else:
107 self.account_ring = \
108 Ring(os.path.join(self.swift_dir, 'account.ring.gz'))
109 self.db_file = os.path.join(self.swift_dir, 'auth.db')106 self.db_file = os.path.join(self.swift_dir, 'auth.db')
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)
111 try:108 try:
@@ -114,9 +111,16 @@
114 if str(err) == 'no such column: admin':111 if str(err) == 'no such column: admin':
115 self.conn.execute("ALTER TABLE account ADD COLUMN admin TEXT")112 self.conn.execute("ALTER TABLE account ADD COLUMN admin TEXT")
116 self.conn.execute("UPDATE account SET admin = 't'")113 self.conn.execute("UPDATE account SET admin = 't'")
114 try:
115 self.conn.execute('SELECT reseller_admin FROM account LIMIT 1')
116 except sqlite3.OperationalError, err:
117 if str(err) == 'no such column: reseller_admin':
118 self.conn.execute(
119 "ALTER TABLE account ADD COLUMN reseller_admin TEXT")
117 self.conn.execute('''CREATE TABLE IF NOT EXISTS account (120 self.conn.execute('''CREATE TABLE IF NOT EXISTS account (
118 account TEXT, url TEXT, cfaccount TEXT,121 account TEXT, url TEXT, cfaccount TEXT,
119 user TEXT, password TEXT, admin TEXT)''')122 user TEXT, password TEXT, admin TEXT,
123 reseller_admin TEXT)''')
120 self.conn.execute('''CREATE INDEX IF NOT EXISTS ix_account_account124 self.conn.execute('''CREATE INDEX IF NOT EXISTS ix_account_account
121 ON account (account)''')125 ON account (account)''')
122 try:126 try:
@@ -139,51 +143,36 @@
139143
140 def add_storage_account(self, account_name=''):144 def add_storage_account(self, account_name=''):
141 """145 """
142 Creates an account within the Swift cluster by making a ReST call to146 Creates an account within the Swift cluster by making a ReST call.
143 each of the responsible account servers.
144147
145 :param account_name: The desired name for the account; if omitted a148 :param account_name: The desired name for the account; if omitted a
146 UUID4 will be used.149 UUID4 will be used.
147 :returns: False upon failure, otherwise the name of the account150 :returns: False upon failure, otherwise the name of the account
148 within the Swift cluster.151 within the Swift cluster.
149 """152 """
150 begin = time()
151 orig_account_name = account_name153 orig_account_name = account_name
152 if not account_name:154 if not account_name:
153 account_name = '%s%s' % (self.reseller_prefix, uuid4().hex)155 account_name = '%s%s' % (self.reseller_prefix, uuid4().hex)
154 partition, nodes = self.account_ring.get_nodes(account_name)156 url = '%s/%s' % (self.default_cluster_url, account_name)
155 headers = {'X-Timestamp': normalize_timestamp(time()),157 parsed = urlparse(url)
156 'x-cf-trans-id': 'tx' + str(uuid4())}158 # Create a single use token.
157 statuses = []159 token = '%stk%s' % (self.reseller_prefix, uuid4().hex)
158 for node in nodes:160 with self.get_conn() as conn:
159 try:161 conn.execute('''
160 conn = None162 INSERT INTO token
161 conn = http_connect(node['ip'], node['port'], node['device'],163 (token, created, account, user, cfaccount) VALUES
162 partition, 'PUT', '/' + account_name, headers)164 (?, ?, '.super_admin', '.single_use', '.reseller_admin')''',
163 source = conn.getresponse()165 (token, time()))
164 statuses.append(source.status)166 conn.commit()
165 if source.status >= 500:167 conn = http_connect(parsed.hostname, parsed.port, 'PUT', parsed.path,
166 self.logger.error('ERROR With account server %s:%s/%s: '168 {'X-Auth-Token': token}, ssl=(parsed.scheme == 'https'))
167 'Response %s %s: %s' %169 resp = conn.getresponse()
168 (node['ip'], node['port'], node['device'],170 resp.read()
169 source.status, source.reason, source.read(1024)))171 if resp.status // 100 != 2:
170 conn = None172 self.logger.error('ERROR attempting to create account %s: %s %s' %
171 except BaseException, err:173 (url, resp.status, resp.reason))
172 log_call = self.logger.exception174 return False
173 msg = 'ERROR With account server ' \175 return account_name
174 '%(ip)s:%(port)s/%(device)s (will retry later): ' % node
175 if isinstance(err, socket.error):
176 if err[0] == errno.ECONNREFUSED:
177 log_call = self.logger.error
178 msg += 'Connection refused'
179 elif err[0] == errno.EHOSTUNREACH:
180 log_call = self.logger.error
181 msg += 'Host unreachable'
182 log_call(msg)
183 rv = False
184 if len([s for s in statuses if (200 <= s < 300)]) > len(nodes) / 2:
185 rv = account_name
186 return rv
187176
188 @contextmanager177 @contextmanager
189 def get_conn(self):178 def get_conn(self):
@@ -229,7 +218,9 @@
229218
230 :param token: The token to validate219 :param token: The token to validate
231 :returns: (TTL, account, user, cfaccount) if valid, False otherwise.220 :returns: (TTL, account, user, cfaccount) if valid, False otherwise.
232 cfaccount will be None for users without admin access.221 cfaccount will be None for users without admin access for the
222 account. cfaccount will be .reseller_admin for users with
223 full reseller admin rights.
233 """224 """
234 begin = time()225 begin = time()
235 self.purge_old_tokens()226 self.purge_old_tokens()
@@ -241,18 +232,20 @@
241 (token,)).fetchone()232 (token,)).fetchone()
242 if row is not None:233 if row is not None:
243 created = row[0]234 created = row[0]
244 if time() - created >= self.token_life:235 if time() - created < self.token_life:
236 rv = (self.token_life - (time() - created), row[1], row[2],
237 row[3])
238 # Remove the token if it was expired or single use.
239 if not rv or rv[2] == '.single_use':
245 conn.execute('''240 conn.execute('''
246 DELETE FROM token WHERE token = ?''', (token,))241 DELETE FROM token WHERE token = ?''', (token,))
247 conn.commit()242 conn.commit()
248 else:
249 rv = (self.token_life - (time() - created), row[1], row[2],
250 row[3])
251 self.logger.info('validate_token(%s, _, _) = %s [%.02f]' %243 self.logger.info('validate_token(%s, _, _) = %s [%.02f]' %
252 (repr(token), repr(rv), time() - begin))244 (repr(token), repr(rv), time() - begin))
253 return rv245 return rv
254246
255 def create_user(self, account, user, password, admin=False):247 def create_user(self, account, user, password, admin=False,
248 reseller_admin=False):
256 """249 """
257 Handles the create_user call for developers, used to request a user be250 Handles the create_user call for developers, used to request a user be
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,
@@ -274,6 +267,9 @@
274 :param admin: If true, the user will be granted full access to the267 :param admin: If true, the user will be granted full access to the
275 account; otherwise, another user will have to add the268 account; otherwise, another user will have to add the
276 user to the ACLs for containers to grant access.269 user to the ACLs for containers to grant access.
270 :param reseller_admin: If true, the user will be granted full access to
271 all accounts within this reseller, including the
272 ability to create additional accounts.
277273
278 :returns: False if the create fails, 'already exists' if the user274 :returns: False if the create fails, 'already exists' if the user
279 already exists, or storage url if successful275 already exists, or storage url if successful
@@ -287,9 +283,9 @@
287 (account, user)).fetchone()283 (account, user)).fetchone()
288 if row:284 if row:
289 self.logger.info(285 self.logger.info(
290 'ALREADY EXISTS create_user(%s, %s, _, %s) [%.02f]' %286 'ALREADY EXISTS create_user(%s, %s, _, %s, %s) [%.02f]' %
291 (repr(account), repr(user), repr(admin),287 (repr(account), repr(user), repr(admin),
292 time() - begin))288 repr(reseller_admin), time() - begin))
293 return 'already exists'289 return 'already exists'
294 row = conn.execute(290 row = conn.execute(
295 'SELECT url, cfaccount FROM account WHERE account = ?',291 'SELECT url, cfaccount FROM account WHERE account = ?',
@@ -301,21 +297,22 @@
301 account_hash = self.add_storage_account()297 account_hash = self.add_storage_account()
302 if not account_hash:298 if not account_hash:
303 self.logger.info(299 self.logger.info(
304 'FAILED create_user(%s, %s, _, %s) [%.02f]' %300 'FAILED create_user(%s, %s, _, %s, %s) [%.02f]' %
305 (repr(account), repr(user), repr(admin),301 (repr(account), repr(user), repr(admin),
306 time() - begin))302 repr(reseller_admin), time() - begin))
307 return False303 return False
308 url = self.default_cluster_url.rstrip('/') + '/' + account_hash304 url = self.default_cluster_url.rstrip('/') + '/' + account_hash
309 conn.execute('''INSERT INTO account305 conn.execute('''INSERT INTO account
310 (account, url, cfaccount, user, password, admin)306 (account, url, cfaccount, user, password, admin,
311 VALUES (?, ?, ?, ?, ?, ?)''',307 reseller_admin)
308 VALUES (?, ?, ?, ?, ?, ?, ?)''',
312 (account, url, account_hash, user, password,309 (account, url, account_hash, user, password,
313 admin and 't' or ''))310 admin and 't' or '', reseller_admin and 't' or ''))
314 conn.commit()311 conn.commit()
315 self.logger.info(312 self.logger.info(
316 'SUCCESS create_user(%s, %s, _, %s) = %s [%.02f]' %313 'SUCCESS create_user(%s, %s, _, %s, %s) = %s [%.02f]' %
317 (repr(account), repr(user), repr(admin), repr(url),314 (repr(account), repr(user), repr(admin), repr(reseller_admin),
318 time() - begin))315 repr(url), time() - begin))
319 return url316 return url
320317
321 def recreate_accounts(self):318 def recreate_accounts(self):
@@ -339,6 +336,32 @@
339 (rv, time() - begin))336 (rv, time() - begin))
340 return rv337 return rv
341338
339 def is_account_admin(self, request, for_account):
340 """
341 Returns True if the request represents coming from .super_admin, a
342 .reseller_admin, or an admin for the account specified.
343 """
344 if request.headers.get('X-Auth-Admin-User') == '.super_admin' and \
345 request.headers.get('X-Auth-Admin-Key') == self.super_admin_key:
346 return True
347 try:
348 account, user = \
349 request.headers.get('X-Auth-Admin-User').split(':', 1)
350 except (AttributeError, ValueError):
351 return False
352 with self.get_conn() as conn:
353 row = conn.execute('''
354 SELECT reseller_admin, admin FROM account
355 WHERE account = ? AND user = ? AND password = ?''',
356 (account, user,
357 request.headers.get('X-Auth-Admin-Key'))).fetchone()
358 if row:
359 if row[0] == 't':
360 return True
361 if row[1] == 't' and account == for_account:
362 return True
363 return False
364
342 def handle_token(self, request):365 def handle_token(self, request):
343 """366 """
344 Handles ReST requests from Swift to validate tokens367 Handles ReST requests from Swift to validate tokens
@@ -362,7 +385,9 @@
362 if not validation:385 if not validation:
363 return HTTPNotFound()386 return HTTPNotFound()
364 groups = ['%s:%s' % (validation[1], validation[2]), validation[1]]387 groups = ['%s:%s' % (validation[1], validation[2]), validation[1]]
365 if validation[3]: # admin access to a cfaccount388 if validation[3]:
389 # admin access to a cfaccount or ".reseller_admin" to access to all
390 # accounts, including creating new ones.
366 groups.append(validation[3])391 groups.append(validation[3])
367 return HTTPNoContent(headers={'X-Auth-TTL': validation[0],392 return HTTPNoContent(headers={'X-Auth-TTL': validation[0],
368 'X-Auth-Groups': ','.join(groups)})393 'X-Auth-Groups': ','.join(groups)})
@@ -380,6 +405,7 @@
380 Valid headers:405 Valid headers:
381 * X-Auth-User-Key: <password>406 * X-Auth-User-Key: <password>
382 * X-Auth-User-Admin: <true|false>407 * X-Auth-User-Admin: <true|false>
408 * X-Auth-User-Reseller-Admin: <true|false>
383409
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,
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.
@@ -390,13 +416,24 @@
390 _, account_name, user_name = split_path(request.path, minsegs=3)416 _, account_name, user_name = split_path(request.path, minsegs=3)
391 except ValueError:417 except ValueError:
392 return HTTPBadRequest()418 return HTTPBadRequest()
419 create_reseller_admin = \
420 request.headers.get('x-auth-user-reseller-admin') == 'true'
421 if create_reseller_admin and (
422 request.headers.get('X-Auth-Admin-User') != '.super_admin' or
423 request.headers.get('X-Auth-Admin-Key') != self.super_admin_key):
424 return HTTPForbidden(request=request)
425 create_account_admin = \
426 request.headers.get('x-auth-user-admin') == 'true'
427 if create_account_admin and \
428 not self.is_account_admin(request, account_name):
429 return HTTPForbidden(request=request)
393 if 'X-Auth-User-Key' not in request.headers:430 if 'X-Auth-User-Key' not in request.headers:
394 return HTTPBadRequest('X-Auth-User-Key is required')431 return HTTPBadRequest(body='X-Auth-User-Key is required')
395 password = request.headers['x-auth-user-key']432 password = request.headers['x-auth-user-key']
396 storage_url = self.create_user(account_name, user_name, password,433 storage_url = self.create_user(account_name, user_name, password,
397 request.headers.get('x-auth-user-admin') == 'true')434 create_account_admin, create_reseller_admin)
398 if storage_url == 'already exists':435 if storage_url == 'already exists':
399 return HTTPBadRequest(storage_url)436 return HTTPBadRequest(body=storage_url)
400 if not storage_url:437 if not storage_url:
401 return HTTPServiceUnavailable()438 return HTTPServiceUnavailable()
402 return HTTPNoContent(headers={'x-storage-url': storage_url})439 return HTTPNoContent(headers={'x-storage-url': storage_url})
@@ -412,6 +449,9 @@
412449
413 :param request: webob.Request object450 :param request: webob.Request object
414 """451 """
452 if request.headers.get('X-Auth-Admin-User') != '.super_admin' or \
453 request.headers.get('X-Auth-Admin-Key') != self.super_admin_key:
454 return HTTPForbidden(request=request)
415 result = self.recreate_accounts()455 result = self.recreate_accounts()
416 return Response(result, 200, request=request)456 return Response(result, 200, request=request)
417457
@@ -471,7 +511,7 @@
471 self.purge_old_tokens()511 self.purge_old_tokens()
472 with self.get_conn() as conn:512 with self.get_conn() as conn:
473 row = conn.execute('''513 row = conn.execute('''
474 SELECT cfaccount, url, admin FROM account514 SELECT cfaccount, url, admin, reseller_admin FROM account
475 WHERE account = ? AND user = ? AND password = ?''',515 WHERE account = ? AND user = ? AND password = ?''',
476 (account, user, password)).fetchone()516 (account, user, password)).fetchone()
477 if row is None:517 if row is None:
@@ -479,6 +519,7 @@
479 cfaccount = row[0]519 cfaccount = row[0]
480 url = row[1]520 url = row[1]
481 admin = row[2] == 't'521 admin = row[2] == 't'
522 reseller_admin = row[3] == 't'
482 row = conn.execute('''523 row = conn.execute('''
483 SELECT token FROM token WHERE account = ? AND user = ?''',524 SELECT token FROM token WHERE account = ? AND user = ?''',
484 (account, user)).fetchone()525 (account, user)).fetchone()
@@ -486,11 +527,16 @@
486 token = row[0]527 token = row[0]
487 else:528 else:
488 token = '%stk%s' % (self.reseller_prefix, uuid4().hex)529 token = '%stk%s' % (self.reseller_prefix, uuid4().hex)
530 token_cfaccount = ''
531 if admin:
532 token_cfaccount = cfaccount
533 if reseller_admin:
534 token_cfaccount = '.reseller_admin'
489 conn.execute('''535 conn.execute('''
490 INSERT INTO token536 INSERT INTO token
491 (token, created, account, user, cfaccount)537 (token, created, account, user, cfaccount)
492 VALUES (?, ?, ?, ?, ?)''',538 VALUES (?, ?, ?, ?, ?)''',
493 (token, time(), account, user, admin and cfaccount or ''))539 (token, time(), account, user, token_cfaccount))
494 conn.commit()540 conn.commit()
495 return HTTPNoContent(headers={'x-auth-token': token,541 return HTTPNoContent(headers={'x-auth-token': token,
496 'x-storage-token': token,542 'x-storage-token': token,
497543
=== modified file 'swift/common/constraints.py'
--- swift/common/constraints.py 2010-08-16 22:30:27 +0000
+++ swift/common/constraints.py 2010-09-12 00:25:58 +0000
@@ -36,6 +36,8 @@
36CONTAINER_LISTING_LIMIT = 1000036CONTAINER_LISTING_LIMIT = 10000
37#: Max container list length of a get request for an account37#: Max container list length of a get request for an account
38ACCOUNT_LISTING_LIMIT = 1000038ACCOUNT_LISTING_LIMIT = 10000
39MAX_ACCOUNT_NAME_LENGTH = 256
40MAX_CONTAINER_NAME_LENGTH = 256
3941
4042
41def check_metadata(req, target_type):43def check_metadata(req, target_type):
4244
=== modified file 'swift/common/middleware/auth.py'
--- swift/common/middleware/auth.py 2010-09-09 17:24:25 +0000
+++ swift/common/middleware/auth.py 2010-09-12 00:25:58 +0000
@@ -49,7 +49,7 @@
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'))
50 if token and token.startswith(self.reseller_prefix):50 if token and token.startswith(self.reseller_prefix):
51 memcache_client = cache_from_env(env)51 memcache_client = cache_from_env(env)
52 key = 'devauth/%s' % token52 key = '%s/token/%s' % (self.reseller_prefix, token)
53 cached_auth_data = memcache_client.get(key)53 cached_auth_data = memcache_client.get(key)
54 if cached_auth_data:54 if cached_auth_data:
55 start, expiration, groups = cached_auth_data55 start, expiration, groups = cached_auth_data
@@ -85,14 +85,19 @@
85 version, account, container, obj = split_path(req.path, 1, 4, True)85 version, account, container, obj = split_path(req.path, 1, 4, True)
86 if not account or not account.startswith(self.reseller_prefix):86 if not account or not account.startswith(self.reseller_prefix):
87 return self.denied_response(req)87 return self.denied_response(req)
88 if req.remote_user and account in req.remote_user.split(','):88 user_groups = (req.remote_user or '').split(',')
89 if '.reseller_admin' in user_groups:
90 return None
91 if account in user_groups and (req.method != 'PUT' or container):
92 # If the user is admin for the account and is not trying to do an
93 # account PUT...
89 return None94 return None
90 referrers, groups = parse_acl(getattr(req, 'acl', None))95 referrers, groups = parse_acl(getattr(req, 'acl', None))
91 if referrer_allowed(req.referer, referrers):96 if referrer_allowed(req.referer, referrers):
92 return None97 return None
93 if not req.remote_user:98 if not req.remote_user:
94 return self.denied_response(req)99 return self.denied_response(req)
95 for user_group in req.remote_user.split(','):100 for user_group in user_groups:
96 if user_group in groups:101 if user_group in groups:
97 return None102 return None
98 return self.denied_response(req)103 return self.denied_response(req)
99104
=== modified file 'swift/proxy/server.py'
--- swift/proxy/server.py 2010-09-10 14:52:10 +0000
+++ swift/proxy/server.py 2010-09-12 00:25:58 +0000
@@ -35,13 +35,12 @@
35from swift.common.utils import get_logger, normalize_timestamp, split_path, \35from swift.common.utils import get_logger, normalize_timestamp, split_path, \
36 cache_from_env36 cache_from_env
37from swift.common.bufferedhttp import http_connect37from swift.common.bufferedhttp import http_connect
38from swift.common.constraints import check_object_creation, check_metadata, \38from swift.common.constraints import check_metadata, check_object_creation, \
39 MAX_FILE_SIZE, check_xml_encodable39 check_xml_encodable, MAX_ACCOUNT_NAME_LENGTH, MAX_CONTAINER_NAME_LENGTH, \
40 MAX_FILE_SIZE
40from swift.common.exceptions import ChunkReadTimeout, \41from swift.common.exceptions import ChunkReadTimeout, \
41 ChunkWriteTimeout, ConnectionTimeout42 ChunkWriteTimeout, ConnectionTimeout
4243
43MAX_CONTAINER_NAME_LENGTH = 256
44
4544
46def update_headers(response, headers):45def update_headers(response, headers):
47 """46 """
@@ -1080,6 +1079,59 @@
1080 req.path_info.rstrip('/'), self.app.account_ring.replica_count)1079 req.path_info.rstrip('/'), self.app.account_ring.replica_count)
10811080
1082 @public1081 @public
1082 def PUT(self, req):
1083 """HTTP PUT request handler."""
1084 error_response = check_metadata(req, 'account')
1085 if error_response:
1086 return error_response
1087 if len(self.account_name) > MAX_ACCOUNT_NAME_LENGTH:
1088 resp = HTTPBadRequest(request=req)
1089 resp.body = 'Account name length of %d longer than %d' % \
1090 (len(self.account_name), MAX_ACCOUNT_NAME_LENGTH)
1091 return resp
1092 account_partition, accounts = \
1093 self.app.account_ring.get_nodes(self.account_name)
1094 headers = {'X-Timestamp': normalize_timestamp(time.time()),
1095 'x-cf-trans-id': self.trans_id}
1096 headers.update(value for value in req.headers.iteritems()
1097 if value[0].lower().startswith('x-account-meta-'))
1098 statuses = []
1099 reasons = []
1100 bodies = []
1101 for node in self.iter_nodes(account_partition, accounts,
1102 self.app.account_ring):
1103 if self.error_limited(node):
1104 continue
1105 try:
1106 with ConnectionTimeout(self.app.conn_timeout):
1107 conn = http_connect(node['ip'], node['port'],
1108 node['device'], account_partition, 'PUT',
1109 req.path_info, headers)
1110 with Timeout(self.app.node_timeout):
1111 source = conn.getresponse()
1112 body = source.read()
1113 if 200 <= source.status < 300 \
1114 or 400 <= source.status < 500:
1115 statuses.append(source.status)
1116 reasons.append(source.reason)
1117 bodies.append(body)
1118 else:
1119 if source.status == 507:
1120 self.error_limit(node)
1121 except:
1122 self.exception_occurred(node, 'Account',
1123 'Trying to PUT to %s' % req.path)
1124 if len(statuses) >= len(accounts):
1125 break
1126 while len(statuses) < len(accounts):
1127 statuses.append(503)
1128 reasons.append('')
1129 bodies.append('')
1130 self.app.memcache.delete('account%s' % req.path_info.rstrip('/'))
1131 return self.best_response(req, statuses, reasons, bodies,
1132 'Account PUT')
1133
1134 @public
1083 def POST(self, req):1135 def POST(self, req):
1084 """HTTP POST request handler."""1136 """HTTP POST request handler."""
1085 error_response = check_metadata(req, 'account')1137 error_response = check_metadata(req, 'account')
10861138
=== modified file 'test/functional/swift.py'
--- test/functional/swift.py 2010-07-12 22:03:45 +0000
+++ test/functional/swift.py 2010-09-12 00:25:58 +0000
@@ -124,7 +124,7 @@
124 if response.status == 401:124 if response.status == 401:
125 raise AuthenticationFailed()125 raise AuthenticationFailed()
126126
127 if response.status != 204:127 if response.status not in (200, 204):
128 raise ResponseError(response)128 raise ResponseError(response)
129129
130 for hdr in response.getheaders():130 for hdr in response.getheaders():
131131
=== modified file 'test/functional/tests.py'
--- test/functional/tests.py 2010-09-03 04:50:16 +0000
+++ test/functional/tests.py 2010-09-12 00:25:58 +0000
@@ -172,7 +172,7 @@
172172
173 def testPUT(self):173 def testPUT(self):
174 self.env.account.conn.make_request('PUT')174 self.env.account.conn.make_request('PUT')
175 self.assert_status(405)175 self.assert_status([403, 405])
176176
177 def testAccountHead(self):177 def testAccountHead(self):
178 try_count = 0178 try_count = 0
179179
=== modified file 'test/probe/common.py'
--- test/probe/common.py 2010-07-19 03:00:28 +0000
+++ test/probe/common.py 2010-09-12 00:25:58 +0000
@@ -13,16 +13,26 @@
13# See the License for the specific language governing permissions and13# See the License for the specific language governing permissions and
14# limitations under the License.14# limitations under the License.
1515
16from os import kill16from os import environ, kill
17from signal import SIGTERM17from signal import SIGTERM
18from subprocess import call, Popen18from subprocess import call, Popen
19from time import sleep19from time import sleep
20from ConfigParser import ConfigParser
2021
21from swift.common.bufferedhttp import http_connect_raw as http_connect22from swift.common.bufferedhttp import http_connect_raw as http_connect
22from swift.common.client import get_auth23from swift.common.client import get_auth
23from swift.common.ring import Ring24from swift.common.ring import Ring
2425
2526
27AUTH_SERVER_CONF_FILE = environ.get('SWIFT_AUTH_SERVER_CONF_FILE',
28 '/etc/swift/auth-server.conf')
29c = ConfigParser()
30if not c.read(AUTH_SERVER_CONF_FILE):
31 exit('Unable to read config file: %s' % AUTH_SERVER_CONF_FILE)
32conf = dict(c.items('app:auth-server'))
33SUPER_ADMIN_KEY = conf.get('super_admin_key', 'devauth')
34
35
26def kill_pids(pids):36def kill_pids(pids):
27 for pid in pids.values():37 for pid in pids.values():
28 try:38 try:
@@ -50,7 +60,9 @@
50 container_ring = Ring('/etc/swift/container.ring.gz')60 container_ring = Ring('/etc/swift/container.ring.gz')
51 object_ring = Ring('/etc/swift/object.ring.gz')61 object_ring = Ring('/etc/swift/object.ring.gz')
52 sleep(5)62 sleep(5)
53 conn = http_connect('127.0.0.1', '11000', 'POST', '/recreate_accounts')63 conn = http_connect('127.0.0.1', '11000', 'POST', '/recreate_accounts',
64 headers={'X-Auth-Admin-User': '.super_admin',
65 'X-Auth-Admin-Key': SUPER_ADMIN_KEY})
54 resp = conn.getresponse()66 resp = conn.getresponse()
55 if resp.status != 200:67 if resp.status != 200:
56 raise Exception('Recreating accounts failed. (%d)' % resp.status)68 raise Exception('Recreating accounts failed. (%d)' % resp.status)
5769
=== modified file 'test/unit/auth/test_server.py'
--- test/unit/auth/test_server.py 2010-09-06 20:26:31 +0000
+++ test/unit/auth/test_server.py 2010-09-12 00:25:58 +0000
@@ -53,33 +53,27 @@
53 def getheader(self, name):53 def getheader(self, name):
54 return self.getheaders().get(name.lower())54 return self.getheaders().get(name.lower())
55 code_iter = iter(code_iter)55 code_iter = iter(code_iter)
56 def connect(*args, **ckwargs):56 def connect(*args, **kwargs):
57 if 'give_content_type' in kwargs:57 connect.last_args = args
58 if len(args) >= 7 and 'content_type' in args[6]:58 connect.last_kwargs = kwargs
59 kwargs['give_content_type'](args[6]['content-type'])
60 else:
61 kwargs['give_content_type']('')
62 return FakeConn(code_iter.next())59 return FakeConn(code_iter.next())
63 return connect60 return connect
6461
6562
66class FakeRing(object):
67 def get_nodes(self, path):
68 return 1, [{'ip': '10.0.0.%s' % x, 'port': 1000+x, 'device': 'sda'}
69 for x in xrange(3)]
70
71
72class TestAuthServer(unittest.TestCase):63class TestAuthServer(unittest.TestCase):
7364
74 def setUp(self):65 def setUp(self):
66 self.ohttp_connect = auth_server.http_connect
75 self.testdir = os.path.join(os.path.dirname(__file__),67 self.testdir = os.path.join(os.path.dirname(__file__),
76 'auth_server')68 'auth_server')
77 rmtree(self.testdir, ignore_errors=1)69 rmtree(self.testdir, ignore_errors=1)
78 os.mkdir(self.testdir)70 os.mkdir(self.testdir)
79 self.conf = {'swift_dir': self.testdir, 'log_name': 'auth'}71 self.conf = {'swift_dir': self.testdir, 'log_name': 'auth',
80 self.controller = auth_server.AuthController(self.conf, FakeRing())72 'super_admin_key': 'testkey'}
73 self.controller = auth_server.AuthController(self.conf)
8174
82 def tearDown(self):75 def tearDown(self):
76 auth_server.http_connect = self.ohttp_connect
83 rmtree(self.testdir, ignore_errors=1)77 rmtree(self.testdir, ignore_errors=1)
8478
85 def test_get_conn(self):79 def test_get_conn(self):
@@ -106,7 +100,7 @@
106 self.assert_(conn is not None)100 self.assert_(conn is not None)
107101
108 def test_validate_token_non_existant_token(self):102 def test_validate_token_non_existant_token(self):
109 auth_server.http_connect = fake_http_connect(201, 201, 201)103 auth_server.http_connect = fake_http_connect(201)
110 cfaccount = self.controller.create_user(104 cfaccount = self.controller.create_user(
111 'test', 'tester', 'testing',).split('/')[-1]105 'test', 'tester', 'testing',).split('/')[-1]
112 res = self.controller.handle_auth(Request.blank('/v1/test/auth',106 res = self.controller.handle_auth(Request.blank('/v1/test/auth',
@@ -117,7 +111,7 @@
117 self.assertEquals(self.controller.validate_token(token + 'bad'), False)111 self.assertEquals(self.controller.validate_token(token + 'bad'), False)
118112
119 def test_validate_token_good(self):113 def test_validate_token_good(self):
120 auth_server.http_connect = fake_http_connect(201, 201, 201)114 auth_server.http_connect = fake_http_connect(201)
121 cfaccount = self.controller.create_user(115 cfaccount = self.controller.create_user(
122 'test', 'tester', 'testing',).split('/')[-1]116 'test', 'tester', 'testing',).split('/')[-1]
123 res = self.controller.handle_auth(Request.blank('/v1/test/auth',117 res = self.controller.handle_auth(Request.blank('/v1/test/auth',
@@ -125,14 +119,14 @@
125 headers={'X-Storage-User': 'tester',119 headers={'X-Storage-User': 'tester',
126 'X-Storage-Pass': 'testing'}))120 'X-Storage-Pass': 'testing'}))
127 token = res.headers['x-storage-token']121 token = res.headers['x-storage-token']
128 ttl = self.controller.validate_token(token)122 ttl, _, _, _ = self.controller.validate_token(token)
129 self.assert_(ttl > 0, repr(ttl))123 self.assert_(ttl > 0, repr(ttl))
130124
131 def test_validate_token_expired(self):125 def test_validate_token_expired(self):
132 orig_time = auth_server.time126 orig_time = auth_server.time
133 try:127 try:
134 auth_server.time = lambda: 1128 auth_server.time = lambda: 1
135 auth_server.http_connect = fake_http_connect(201, 201, 201)129 auth_server.http_connect = fake_http_connect(201)
136 cfaccount = self.controller.create_user('test', 'tester',130 cfaccount = self.controller.create_user('test', 'tester',
137 'testing').split('/')[-1]131 'testing').split('/')[-1]
138 res = self.controller.handle_auth(Request.blank('/v1/test/auth',132 res = self.controller.handle_auth(Request.blank('/v1/test/auth',
@@ -140,7 +134,7 @@
140 headers={'X-Storage-User': 'tester',134 headers={'X-Storage-User': 'tester',
141 'X-Storage-Pass': 'testing'}))135 'X-Storage-Pass': 'testing'}))
142 token = res.headers['x-storage-token']136 token = res.headers['x-storage-token']
143 ttl = self.controller.validate_token(token)137 ttl, _, _, _ = self.controller.validate_token(token)
144 self.assert_(ttl > 0, repr(ttl))138 self.assert_(ttl > 0, repr(ttl))
145 auth_server.time = lambda: 1 + self.controller.token_life139 auth_server.time = lambda: 1 + self.controller.token_life
146 self.assertEquals(self.controller.validate_token(token), False)140 self.assertEquals(self.controller.validate_token(token), False)
@@ -148,107 +142,98 @@
148 auth_server.time = orig_time142 auth_server.time = orig_time
149143
150 def test_create_user_no_new_account(self):144 def test_create_user_no_new_account(self):
151 auth_server.http_connect = fake_http_connect(201, 201, 201)145 auth_server.http_connect = fake_http_connect(201)
152 result = self.controller.create_user('', 'tester', 'testing')146 result = self.controller.create_user('', 'tester', 'testing')
153 self.assertFalse(result)147 self.assertFalse(result)
154148
155 def test_create_user_no_new_user(self):149 def test_create_user_no_new_user(self):
156 auth_server.http_connect = fake_http_connect(201, 201, 201)150 auth_server.http_connect = fake_http_connect(201)
157 result = self.controller.create_user('test', '', 'testing')151 result = self.controller.create_user('test', '', 'testing')
158 self.assertFalse(result)152 self.assertFalse(result)
159153
160 def test_create_user_no_new_password(self):154 def test_create_user_no_new_password(self):
161 auth_server.http_connect = fake_http_connect(201, 201, 201)155 auth_server.http_connect = fake_http_connect(201)
162 result = self.controller.create_user('test', 'tester', '')156 result = self.controller.create_user('test', 'tester', '')
163 self.assertFalse(result)157 self.assertFalse(result)
164158
165 def test_create_user_good(self):159 def test_create_user_good(self):
166 auth_server.http_connect = fake_http_connect(201, 201, 201)160 auth_server.http_connect = fake_http_connect(201)
167 url = self.controller.create_user('test', 'tester', 'testing')161 url = self.controller.create_user('test', 'tester', 'testing')
168 self.assert_(url)162 self.assert_(url)
169 self.assertEquals('/'.join(url.split('/')[:-1]),163 self.assertEquals('/'.join(url.split('/')[:-1]),
170 self.controller.default_cluster_url.rstrip('/'), repr(url))164 self.controller.default_cluster_url.rstrip('/'), repr(url))
171165
172 def test_recreate_accounts_none(self):166 def test_recreate_accounts_none(self):
173 auth_server.http_connect = fake_http_connect(201, 201, 201)167 auth_server.http_connect = fake_http_connect(201)
174 rv = self.controller.recreate_accounts()168 rv = self.controller.recreate_accounts()
175 self.assertEquals(rv.split()[0], '0', repr(rv))169 self.assertEquals(rv.split()[0], '0', repr(rv))
176 self.assertEquals(rv.split()[-1], '[]', repr(rv))170 self.assertEquals(rv.split()[-1], '[]', repr(rv))
177171
178 def test_recreate_accounts_one(self):172 def test_recreate_accounts_one(self):
179 auth_server.http_connect = fake_http_connect(201, 201, 201)173 auth_server.http_connect = fake_http_connect(201)
180 self.controller.create_user('test', 'tester', 'testing')174 self.controller.create_user('test', 'tester', 'testing')
181 auth_server.http_connect = fake_http_connect(201, 201, 201)175 auth_server.http_connect = fake_http_connect(201)
182 rv = self.controller.recreate_accounts()176 rv = self.controller.recreate_accounts()
183 self.assertEquals(rv.split()[0], '1', repr(rv))177 self.assertEquals(rv.split()[0], '1', repr(rv))
184 self.assertEquals(rv.split()[-1], '[]', repr(rv))178 self.assertEquals(rv.split()[-1], '[]', repr(rv))
185179
186 def test_recreate_accounts_several(self):180 def test_recreate_accounts_several(self):
187 auth_server.http_connect = fake_http_connect(201, 201, 201)181 auth_server.http_connect = fake_http_connect(201)
188 self.controller.create_user('test1', 'tester', 'testing')182 self.controller.create_user('test1', 'tester', 'testing')
189 auth_server.http_connect = fake_http_connect(201, 201, 201)183 auth_server.http_connect = fake_http_connect(201)
190 self.controller.create_user('test2', 'tester', 'testing')184 self.controller.create_user('test2', 'tester', 'testing')
191 auth_server.http_connect = fake_http_connect(201, 201, 201)185 auth_server.http_connect = fake_http_connect(201)
192 self.controller.create_user('test3', 'tester', 'testing')186 self.controller.create_user('test3', 'tester', 'testing')
193 auth_server.http_connect = fake_http_connect(201, 201, 201)187 auth_server.http_connect = fake_http_connect(201)
194 self.controller.create_user('test4', 'tester', 'testing')188 self.controller.create_user('test4', 'tester', 'testing')
195 auth_server.http_connect = fake_http_connect(201, 201, 201,189 auth_server.http_connect = fake_http_connect(201, 201, 201, 201)
196 201, 201, 201,
197 201, 201, 201,
198 201, 201, 201)
199 rv = self.controller.recreate_accounts()190 rv = self.controller.recreate_accounts()
200 self.assertEquals(rv.split()[0], '4', repr(rv))191 self.assertEquals(rv.split()[0], '4', repr(rv))
201 self.assertEquals(rv.split()[-1], '[]', repr(rv))192 self.assertEquals(rv.split()[-1], '[]', repr(rv))
202193
203 def test_recreate_accounts_one_fail(self):194 def test_recreate_accounts_one_fail(self):
204 auth_server.http_connect = fake_http_connect(201, 201, 201)195 auth_server.http_connect = fake_http_connect(201)
205 url = self.controller.create_user('test', 'tester', 'testing')196 url = self.controller.create_user('test', 'tester', 'testing')
206 cfaccount = url.split('/')[-1]197 cfaccount = url.split('/')[-1]
207 auth_server.http_connect = fake_http_connect(500, 500, 500)198 auth_server.http_connect = fake_http_connect(500)
208 rv = self.controller.recreate_accounts()199 rv = self.controller.recreate_accounts()
209 self.assertEquals(rv.split()[0], '1', repr(rv))200 self.assertEquals(rv.split()[0], '1', repr(rv))
210 self.assertEquals(rv.split()[-1], '[%s]' % repr(cfaccount),201 self.assertEquals(rv.split()[-1], '[%s]' % repr(cfaccount),
211 repr(rv))202 repr(rv))
212203
213 def test_recreate_accounts_several_fail(self):204 def test_recreate_accounts_several_fail(self):
214 auth_server.http_connect = fake_http_connect(201, 201, 201)205 auth_server.http_connect = fake_http_connect(201)
215 url = self.controller.create_user('test1', 'tester', 'testing')206 url = self.controller.create_user('test1', 'tester', 'testing')
216 cfaccounts = [url.split('/')[-1]]207 cfaccounts = [url.split('/')[-1]]
217 auth_server.http_connect = fake_http_connect(201, 201, 201)208 auth_server.http_connect = fake_http_connect(201)
218 url = self.controller.create_user('test2', 'tester', 'testing')209 url = self.controller.create_user('test2', 'tester', 'testing')
219 cfaccounts.append(url.split('/')[-1])210 cfaccounts.append(url.split('/')[-1])
220 auth_server.http_connect = fake_http_connect(201, 201, 201)211 auth_server.http_connect = fake_http_connect(201)
221 url = self.controller.create_user('test3', 'tester', 'testing')212 url = self.controller.create_user('test3', 'tester', 'testing')
222 cfaccounts.append(url.split('/')[-1])213 cfaccounts.append(url.split('/')[-1])
223 auth_server.http_connect = fake_http_connect(201, 201, 201)214 auth_server.http_connect = fake_http_connect(201)
224 url = self.controller.create_user('test4', 'tester', 'testing')215 url = self.controller.create_user('test4', 'tester', 'testing')
225 cfaccounts.append(url.split('/')[-1])216 cfaccounts.append(url.split('/')[-1])
226 auth_server.http_connect = fake_http_connect(500, 500, 500,217 auth_server.http_connect = fake_http_connect(500, 500, 500, 500)
227 500, 500, 500,
228 500, 500, 500,
229 500, 500, 500)
230 rv = self.controller.recreate_accounts()218 rv = self.controller.recreate_accounts()
231 self.assertEquals(rv.split()[0], '4', repr(rv))219 self.assertEquals(rv.split()[0], '4', repr(rv))
232 failed = rv.split('[', 1)[-1][:-1].split(', ')220 failed = rv.split('[', 1)[-1][:-1].split(', ')
233 self.assertEquals(set(failed), set(repr(a) for a in cfaccounts))221 self.assertEquals(set(failed), set(repr(a) for a in cfaccounts))
234222
235 def test_recreate_accounts_several_fail_some(self):223 def test_recreate_accounts_several_fail_some(self):
236 auth_server.http_connect = fake_http_connect(201, 201, 201)224 auth_server.http_connect = fake_http_connect(201)
237 url = self.controller.create_user('test1', 'tester', 'testing')225 url = self.controller.create_user('test1', 'tester', 'testing')
238 cfaccounts = [url.split('/')[-1]]226 cfaccounts = [url.split('/')[-1]]
239 auth_server.http_connect = fake_http_connect(201, 201, 201)227 auth_server.http_connect = fake_http_connect(201)
240 url = self.controller.create_user('test2', 'tester', 'testing')228 url = self.controller.create_user('test2', 'tester', 'testing')
241 cfaccounts.append(url.split('/')[-1])229 cfaccounts.append(url.split('/')[-1])
242 auth_server.http_connect = fake_http_connect(201, 201, 201)230 auth_server.http_connect = fake_http_connect(201)
243 url = self.controller.create_user('test3', 'tester', 'testing')231 url = self.controller.create_user('test3', 'tester', 'testing')
244 cfaccounts.append(url.split('/')[-1])232 cfaccounts.append(url.split('/')[-1])
245 auth_server.http_connect = fake_http_connect(201, 201, 201)233 auth_server.http_connect = fake_http_connect(201)
246 url = self.controller.create_user('test4', 'tester', 'testing')234 url = self.controller.create_user('test4', 'tester', 'testing')
247 cfaccounts.append(url.split('/')[-1])235 cfaccounts.append(url.split('/')[-1])
248 auth_server.http_connect = fake_http_connect(500, 500, 500,236 auth_server.http_connect = fake_http_connect(500, 201, 500, 201)
249 201, 201, 201,
250 500, 500, 500,
251 201, 201, 201)
252 rv = self.controller.recreate_accounts()237 rv = self.controller.recreate_accounts()
253 self.assertEquals(rv.split()[0], '4', repr(rv))238 self.assertEquals(rv.split()[0], '4', repr(rv))
254 failed = rv.split('[', 1)[-1][:-1].split(', ')239 failed = rv.split('[', 1)[-1][:-1].split(', ')
@@ -263,7 +248,7 @@
263 self.assertEquals(res.status_int, 400)248 self.assertEquals(res.status_int, 400)
264249
265 def test_auth_SOSO_missing_headers(self):250 def test_auth_SOSO_missing_headers(self):
266 auth_server.http_connect = fake_http_connect(201, 201, 201)251 auth_server.http_connect = fake_http_connect(201)
267 cfaccount = self.controller.create_user(252 cfaccount = self.controller.create_user(
268 'test', 'tester', 'testing').split('/')[-1]253 'test', 'tester', 'testing').split('/')[-1]
269 res = self.controller.handle_auth(Request.blank('/v1/test/auth',254 res = self.controller.handle_auth(Request.blank('/v1/test/auth',
@@ -279,7 +264,7 @@
279 self.assertEquals(res.status_int, 401)264 self.assertEquals(res.status_int, 401)
280265
281 def test_auth_SOSO_bad_account(self):266 def test_auth_SOSO_bad_account(self):
282 auth_server.http_connect = fake_http_connect(201, 201, 201)267 auth_server.http_connect = fake_http_connect(201)
283 cfaccount = self.controller.create_user(268 cfaccount = self.controller.create_user(
284 'test', 'tester', 'testing').split('/')[-1]269 'test', 'tester', 'testing').split('/')[-1]
285 res = self.controller.handle_auth(Request.blank('/v1/testbad/auth',270 res = self.controller.handle_auth(Request.blank('/v1/testbad/auth',
@@ -294,7 +279,7 @@
294 self.assertEquals(res.status_int, 401)279 self.assertEquals(res.status_int, 401)
295280
296 def test_auth_SOSO_bad_user(self):281 def test_auth_SOSO_bad_user(self):
297 auth_server.http_connect = fake_http_connect(201, 201, 201)282 auth_server.http_connect = fake_http_connect(201)
298 cfaccount = self.controller.create_user(283 cfaccount = self.controller.create_user(
299 'test', 'tester', 'testing').split('/')[-1]284 'test', 'tester', 'testing').split('/')[-1]
300 res = self.controller.handle_auth(Request.blank('/v1/test/auth',285 res = self.controller.handle_auth(Request.blank('/v1/test/auth',
@@ -309,7 +294,7 @@
309 self.assertEquals(res.status_int, 401)294 self.assertEquals(res.status_int, 401)
310295
311 def test_auth_SOSO_bad_password(self):296 def test_auth_SOSO_bad_password(self):
312 auth_server.http_connect = fake_http_connect(201, 201, 201)297 auth_server.http_connect = fake_http_connect(201)
313 cfaccount = self.controller.create_user(298 cfaccount = self.controller.create_user(
314 'test', 'tester', 'testing').split('/')[-1]299 'test', 'tester', 'testing').split('/')[-1]
315 res = self.controller.handle_auth(Request.blank('/v1/test/auth',300 res = self.controller.handle_auth(Request.blank('/v1/test/auth',
@@ -324,7 +309,7 @@
324 self.assertEquals(res.status_int, 401)309 self.assertEquals(res.status_int, 401)
325310
326 def test_auth_SOSO_good(self):311 def test_auth_SOSO_good(self):
327 auth_server.http_connect = fake_http_connect(201, 201, 201)312 auth_server.http_connect = fake_http_connect(201)
328 cfaccount = self.controller.create_user(313 cfaccount = self.controller.create_user(
329 'test', 'tester', 'testing').split('/')[-1]314 'test', 'tester', 'testing').split('/')[-1]
330 res = self.controller.handle_auth(Request.blank('/v1/test/auth',315 res = self.controller.handle_auth(Request.blank('/v1/test/auth',
@@ -332,11 +317,11 @@
332 headers={'X-Storage-User': 'tester',317 headers={'X-Storage-User': 'tester',
333 'X-Storage-Pass': 'testing'}))318 'X-Storage-Pass': 'testing'}))
334 token = res.headers['x-storage-token']319 token = res.headers['x-storage-token']
335 ttl = self.controller.validate_token(token)320 ttl, _, _, _ = self.controller.validate_token(token)
336 self.assert_(ttl > 0, repr(ttl))321 self.assert_(ttl > 0, repr(ttl))
337322
338 def test_auth_SOSO_good_Mosso_headers(self):323 def test_auth_SOSO_good_Mosso_headers(self):
339 auth_server.http_connect = fake_http_connect(201, 201, 201)324 auth_server.http_connect = fake_http_connect(201)
340 cfaccount = self.controller.create_user(325 cfaccount = self.controller.create_user(
341 'test', 'tester', 'testing').split('/')[-1]326 'test', 'tester', 'testing').split('/')[-1]
342 res = self.controller.handle_auth(Request.blank('/v1/test/auth',327 res = self.controller.handle_auth(Request.blank('/v1/test/auth',
@@ -344,11 +329,11 @@
344 headers={'X-Auth-User': 'test:tester',329 headers={'X-Auth-User': 'test:tester',
345 'X-Auth-Key': 'testing'}))330 'X-Auth-Key': 'testing'}))
346 token = res.headers['x-storage-token']331 token = res.headers['x-storage-token']
347 ttl = self.controller.validate_token(token)332 ttl, _, _, _ = self.controller.validate_token(token)
348 self.assert_(ttl > 0, repr(ttl))333 self.assert_(ttl > 0, repr(ttl))
349334
350 def test_auth_SOSO_bad_Mosso_headers(self):335 def test_auth_SOSO_bad_Mosso_headers(self):
351 auth_server.http_connect = fake_http_connect(201, 201, 201)336 auth_server.http_connect = fake_http_connect(201)
352 cfaccount = self.controller.create_user(337 cfaccount = self.controller.create_user(
353 'test', 'tester', 'testing',).split('/')[-1]338 'test', 'tester', 'testing',).split('/')[-1]
354 res = self.controller.handle_auth(Request.blank('/v1/test/auth',339 res = self.controller.handle_auth(Request.blank('/v1/test/auth',
@@ -368,7 +353,7 @@
368 self.assertEquals(res.status_int, 401)353 self.assertEquals(res.status_int, 401)
369354
370 def test_auth_Mosso_missing_headers(self):355 def test_auth_Mosso_missing_headers(self):
371 auth_server.http_connect = fake_http_connect(201, 201, 201)356 auth_server.http_connect = fake_http_connect(201)
372 cfaccount = self.controller.create_user(357 cfaccount = self.controller.create_user(
373 'test', 'tester', 'testing').split('/')[-1]358 'test', 'tester', 'testing').split('/')[-1]
374 res = self.controller.handle_auth(Request.blank('/auth',359 res = self.controller.handle_auth(Request.blank('/auth',
@@ -384,7 +369,7 @@
384 self.assertEquals(res.status_int, 401)369 self.assertEquals(res.status_int, 401)
385370
386 def test_auth_Mosso_bad_header_format(self):371 def test_auth_Mosso_bad_header_format(self):
387 auth_server.http_connect = fake_http_connect(201, 201, 201)372 auth_server.http_connect = fake_http_connect(201)
388 cfaccount = self.controller.create_user(373 cfaccount = self.controller.create_user(
389 'test', 'tester', 'testing').split('/')[-1]374 'test', 'tester', 'testing').split('/')[-1]
390 res = self.controller.handle_auth(Request.blank('/auth',375 res = self.controller.handle_auth(Request.blank('/auth',
@@ -399,7 +384,7 @@
399 self.assertEquals(res.status_int, 401)384 self.assertEquals(res.status_int, 401)
400385
401 def test_auth_Mosso_bad_account(self):386 def test_auth_Mosso_bad_account(self):
402 auth_server.http_connect = fake_http_connect(201, 201, 201)387 auth_server.http_connect = fake_http_connect(201)
403 cfaccount = self.controller.create_user(388 cfaccount = self.controller.create_user(
404 'test', 'tester', 'testing').split('/')[-1]389 'test', 'tester', 'testing').split('/')[-1]
405 res = self.controller.handle_auth(Request.blank('/auth',390 res = self.controller.handle_auth(Request.blank('/auth',
@@ -414,7 +399,7 @@
414 self.assertEquals(res.status_int, 401)399 self.assertEquals(res.status_int, 401)
415400
416 def test_auth_Mosso_bad_user(self):401 def test_auth_Mosso_bad_user(self):
417 auth_server.http_connect = fake_http_connect(201, 201, 201)402 auth_server.http_connect = fake_http_connect(201)
418 cfaccount = self.controller.create_user(403 cfaccount = self.controller.create_user(
419 'test', 'tester', 'testing').split('/')[-1]404 'test', 'tester', 'testing').split('/')[-1]
420 res = self.controller.handle_auth(Request.blank('/auth',405 res = self.controller.handle_auth(Request.blank('/auth',
@@ -429,7 +414,7 @@
429 self.assertEquals(res.status_int, 401)414 self.assertEquals(res.status_int, 401)
430415
431 def test_auth_Mosso_bad_password(self):416 def test_auth_Mosso_bad_password(self):
432 auth_server.http_connect = fake_http_connect(201, 201, 201)417 auth_server.http_connect = fake_http_connect(201)
433 cfaccount = self.controller.create_user(418 cfaccount = self.controller.create_user(
434 'test', 'tester', 'testing').split('/')[-1]419 'test', 'tester', 'testing').split('/')[-1]
435 res = self.controller.handle_auth(Request.blank('/auth',420 res = self.controller.handle_auth(Request.blank('/auth',
@@ -444,7 +429,7 @@
444 self.assertEquals(res.status_int, 401)429 self.assertEquals(res.status_int, 401)
445430
446 def test_auth_Mosso_good(self):431 def test_auth_Mosso_good(self):
447 auth_server.http_connect = fake_http_connect(201, 201, 201)432 auth_server.http_connect = fake_http_connect(201)
448 cfaccount = self.controller.create_user(433 cfaccount = self.controller.create_user(
449 'test', 'tester', 'testing').split('/')[-1]434 'test', 'tester', 'testing').split('/')[-1]
450 res = self.controller.handle_auth(Request.blank('/auth',435 res = self.controller.handle_auth(Request.blank('/auth',
@@ -452,11 +437,11 @@
452 headers={'X-Auth-User': 'test:tester',437 headers={'X-Auth-User': 'test:tester',
453 'X-Auth-Key': 'testing'}))438 'X-Auth-Key': 'testing'}))
454 token = res.headers['x-storage-token']439 token = res.headers['x-storage-token']
455 ttl = self.controller.validate_token(token)440 ttl, _, _, _ = self.controller.validate_token(token)
456 self.assert_(ttl > 0, repr(ttl))441 self.assert_(ttl > 0, repr(ttl))
457442
458 def test_auth_Mosso_good_SOSO_header_names(self):443 def test_auth_Mosso_good_SOSO_header_names(self):
459 auth_server.http_connect = fake_http_connect(201, 201, 201)444 auth_server.http_connect = fake_http_connect(201)
460 cfaccount = self.controller.create_user(445 cfaccount = self.controller.create_user(
461 'test', 'tester', 'testing').split('/')[-1]446 'test', 'tester', 'testing').split('/')[-1]
462 res = self.controller.handle_auth(Request.blank('/auth',447 res = self.controller.handle_auth(Request.blank('/auth',
@@ -464,7 +449,7 @@
464 headers={'X-Storage-User': 'test:tester',449 headers={'X-Storage-User': 'test:tester',
465 'X-Storage-Pass': 'testing'}))450 'X-Storage-Pass': 'testing'}))
466 token = res.headers['x-storage-token']451 token = res.headers['x-storage-token']
467 ttl = self.controller.validate_token(token)452 ttl, _, _, _ = self.controller.validate_token(token)
468 self.assert_(ttl > 0, repr(ttl))453 self.assert_(ttl > 0, repr(ttl))
469454
470 def test_basic_logging(self):455 def test_basic_logging(self):
@@ -473,11 +458,11 @@
473 logger = get_logger(self.conf, 'auth')458 logger = get_logger(self.conf, 'auth')
474 logger.logger.addHandler(log_handler)459 logger.logger.addHandler(log_handler)
475 try:460 try:
476 auth_server.http_connect = fake_http_connect(201, 201, 201)461 auth_server.http_connect = fake_http_connect(201)
477 url = self.controller.create_user('test', 'tester', 'testing')462 url = self.controller.create_user('test', 'tester', 'testing')
478 self.assertEquals(log.getvalue().rsplit(' ', 1)[0],463 self.assertEquals(log.getvalue().rsplit(' ', 1)[0],
479 "auth SUCCESS create_user('test', 'tester', _, False) = %s"464 "auth SUCCESS create_user('test', 'tester', _, False, False) "
480 % repr(url))465 "= %s" % repr(url))
481 log.truncate(0)466 log.truncate(0)
482 def start_response(*args):467 def start_response(*args):
483 pass468 pass
@@ -603,8 +588,8 @@
603 conn.commit()588 conn.commit()
604 conn.close()589 conn.close()
605 # Upgrade to current db590 # Upgrade to current db
606 conf = {'swift_dir': swift_dir}591 conf = {'swift_dir': swift_dir, 'super_admin_key': 'testkey'}
607 controller = auth_server.AuthController(conf, FakeRing())592 controller = auth_server.AuthController(conf)
608 # Check new items exist and are correct593 # Check new items exist and are correct
609 conn = get_db_connection(db_file)594 conn = get_db_connection(db_file)
610 row = conn.execute('SELECT admin FROM account').fetchone()595 row = conn.execute('SELECT admin FROM account').fetchone()
@@ -614,21 +599,360 @@
614 finally:599 finally:
615 rmtree(swift_dir)600 rmtree(swift_dir)
616601
602 def test_upgrading_from_db2(self):
603 swift_dir = '/tmp/swift_test_auth_%s' % uuid4().hex
604 os.mkdir(swift_dir)
605 try:
606 # Create db1
607 db_file = os.path.join(swift_dir, 'auth.db')
608 conn = get_db_connection(db_file, okay_to_create=True)
609 conn.execute('''CREATE TABLE IF NOT EXISTS account (
610 account TEXT, url TEXT, cfaccount TEXT,
611 user TEXT, password TEXT, admin TEXT)''')
612 conn.execute('''CREATE INDEX IF NOT EXISTS ix_account_account
613 ON account (account)''')
614 conn.execute('''CREATE TABLE IF NOT EXISTS token (
615 token TEXT, created FLOAT,
616 account TEXT, user TEXT, cfaccount TEXT)''')
617 conn.execute('''CREATE INDEX IF NOT EXISTS ix_token_token
618 ON token (token)''')
619 conn.execute('''CREATE INDEX IF NOT EXISTS ix_token_created
620 ON token (created)''')
621 conn.execute('''CREATE INDEX IF NOT EXISTS ix_token_account
622 ON token (account)''')
623 conn.execute('''INSERT INTO account
624 (account, url, cfaccount, user, password, admin)
625 VALUES ('act', 'url', 'cfa', 'us1', 'pas', '')''')
626 conn.execute('''INSERT INTO account
627 (account, url, cfaccount, user, password, admin)
628 VALUES ('act', 'url', 'cfa', 'us2', 'pas', 't')''')
629 conn.execute('''INSERT INTO token
630 (token, created, account, user, cfaccount)
631 VALUES ('tok', '1', 'act', 'us1', 'cfa')''')
632 conn.commit()
633 conn.close()
634 # Upgrade to current db
635 conf = {'swift_dir': swift_dir, 'super_admin_key': 'testkey'}
636 controller = auth_server.AuthController(conf)
637 # Check new items exist and are correct
638 conn = get_db_connection(db_file)
639 row = conn.execute('''SELECT admin, reseller_admin
640 FROM account WHERE user = 'us1' ''').fetchone()
641 self.assert_(not row[0], row[0])
642 self.assert_(not row[1], row[1])
643 row = conn.execute('''SELECT admin, reseller_admin
644 FROM account WHERE user = 'us2' ''').fetchone()
645 self.assertEquals(row[0], 't')
646 self.assert_(not row[1], row[1])
647 row = conn.execute('SELECT user FROM token').fetchone()
648 self.assert_(row)
649 finally:
650 rmtree(swift_dir)
651
617 def test_create_user_twice(self):652 def test_create_user_twice(self):
618 auth_server.http_connect = fake_http_connect(201, 201, 201)653 auth_server.http_connect = fake_http_connect(201)
619 self.controller.create_user('test', 'tester', 'testing')654 self.controller.create_user('test', 'tester', 'testing')
620 auth_server.http_connect = fake_http_connect(201, 201, 201)655 auth_server.http_connect = fake_http_connect(201)
621 self.assertEquals(656 self.assertEquals(
622 self.controller.create_user('test', 'tester', 'testing'),657 self.controller.create_user('test', 'tester', 'testing'),
623 'already exists')658 'already exists')
624659
625 def test_create_2users_1account(self):660 def test_create_2users_1account(self):
626 auth_server.http_connect = fake_http_connect(201, 201, 201)661 auth_server.http_connect = fake_http_connect(201)
627 url = self.controller.create_user('test', 'tester', 'testing')662 url = self.controller.create_user('test', 'tester', 'testing')
628 auth_server.http_connect = fake_http_connect(201, 201, 201)663 auth_server.http_connect = fake_http_connect(201)
629 url2 = self.controller.create_user('test', 'tester2', 'testing2')664 url2 = self.controller.create_user('test', 'tester2', 'testing2')
630 self.assertEquals(url, url2)665 self.assertEquals(url, url2)
631666
667 def test_no_super_admin_key(self):
668 conf = {'swift_dir': self.testdir, 'log_name': 'auth'}
669 self.assertRaises(ValueError, auth_server.AuthController, conf)
670 conf['super_admin_key'] = 'testkey'
671 auth_server.AuthController(conf)
672
673 def test_add_storage_account(self):
674 auth_server.http_connect = fake_http_connect(201)
675 stgact = self.controller.add_storage_account()
676 self.assert_(stgact.startswith(self.controller.reseller_prefix),
677 stgact)
678 # Make sure token given is the expected single use token
679 token = auth_server.http_connect.last_args[-1]['X-Auth-Token']
680 self.assert_(self.controller.validate_token(token))
681 self.assert_(not self.controller.validate_token(token))
682 auth_server.http_connect = fake_http_connect(201)
683 stgact = self.controller.add_storage_account('bob')
684 self.assertEquals(stgact, 'bob')
685 # Make sure token given is the expected single use token
686 token = auth_server.http_connect.last_args[-1]['X-Auth-Token']
687 self.assert_(self.controller.validate_token(token))
688 self.assert_(not self.controller.validate_token(token))
689
690 def test_regular_user(self):
691 auth_server.http_connect = fake_http_connect(201)
692 self.controller.create_user('act', 'usr', 'pas').split('/')[-1]
693 res = self.controller.handle_auth(Request.blank('/v1.0',
694 environ={'REQUEST_METHOD': 'GET'},
695 headers={'X-Auth-User': 'act:usr', 'X-Auth-Key': 'pas'}))
696 _, _, _, stgact = \
697 self.controller.validate_token(res.headers['x-auth-token'])
698 self.assertEquals(stgact, '')
699
700 def test_account_admin(self):
701 auth_server.http_connect = fake_http_connect(201)
702 stgact = self.controller.create_user(
703 'act', 'usr', 'pas', admin=True).split('/')[-1]
704 res = self.controller.handle_auth(Request.blank('/v1.0',
705 environ={'REQUEST_METHOD': 'GET'},
706 headers={'X-Auth-User': 'act:usr', 'X-Auth-Key': 'pas'}))
707 _, _, _, vstgact = \
708 self.controller.validate_token(res.headers['x-auth-token'])
709 self.assertEquals(stgact, vstgact)
710
711 def test_reseller_admin(self):
712 auth_server.http_connect = fake_http_connect(201)
713 self.controller.create_user(
714 'act', 'usr', 'pas', reseller_admin=True).split('/')[-1]
715 res = self.controller.handle_auth(Request.blank('/v1.0',
716 environ={'REQUEST_METHOD': 'GET'},
717 headers={'X-Auth-User': 'act:usr', 'X-Auth-Key': 'pas'}))
718 _, _, _, stgact = \
719 self.controller.validate_token(res.headers['x-auth-token'])
720 self.assertEquals(stgact, '.reseller_admin')
721
722 def test_is_account_admin(self):
723 req = Request.blank('/', headers={'X-Auth-Admin-User': '.super_admin',
724 'X-Auth-Admin-Key': 'testkey'})
725 self.assert_(self.controller.is_account_admin(req, 'any'))
726 req = Request.blank('/', headers={'X-Auth-Admin-User': '.super_admin',
727 'X-Auth-Admin-Key': 'testkey2'})
728 self.assert_(not self.controller.is_account_admin(req, 'any'))
729 req = Request.blank('/', headers={'X-Auth-Admin-User': '.super_admi',
730 'X-Auth-Admin-Key': 'testkey'})
731 self.assert_(not self.controller.is_account_admin(req, 'any'))
732
733 auth_server.http_connect = fake_http_connect(201, 201)
734 self.controller.create_user(
735 'act1', 'resadmin', 'pas', reseller_admin=True).split('/')[-1]
736 self.controller.create_user('act1', 'usr', 'pas').split('/')[-1]
737 self.controller.create_user(
738 'act2', 'actadmin', 'pas', admin=True).split('/')[-1]
739
740 req = Request.blank('/', headers={'X-Auth-Admin-User': 'act1:resadmin',
741 'X-Auth-Admin-Key': 'pas'})
742 self.assert_(self.controller.is_account_admin(req, 'any'))
743 self.assert_(self.controller.is_account_admin(req, 'act1'))
744 self.assert_(self.controller.is_account_admin(req, 'act2'))
745
746 req = Request.blank('/', headers={'X-Auth-Admin-User': 'act1:usr',
747 'X-Auth-Admin-Key': 'pas'})
748 self.assert_(not self.controller.is_account_admin(req, 'any'))
749 self.assert_(not self.controller.is_account_admin(req, 'act1'))
750 self.assert_(not self.controller.is_account_admin(req, 'act2'))
751
752 req = Request.blank('/', headers={'X-Auth-Admin-User': 'act2:actadmin',
753 'X-Auth-Admin-Key': 'pas'})
754 self.assert_(not self.controller.is_account_admin(req, 'any'))
755 self.assert_(not self.controller.is_account_admin(req, 'act1'))
756 self.assert_(self.controller.is_account_admin(req, 'act2'))
757
758 def test_handle_add_user_create_reseller_admin(self):
759 auth_server.http_connect = fake_http_connect(201)
760 self.controller.create_user('act', 'usr', 'pas')
761 self.controller.create_user('act', 'actadmin', 'pas', admin=True)
762 self.controller.create_user('act', 'resadmin', 'pas',
763 reseller_admin=True)
764
765 req = Request.blank('/account/act/resadmin2',
766 headers={'X-Auth-User-Key': 'pas',
767 'X-Auth-User-Reseller-Admin': 'true'})
768 resp = self.controller.handle_add_user(req)
769 self.assert_(resp.status_int // 100 == 4, resp.status_int)
770
771 req = Request.blank('/account/act/resadmin2',
772 headers={'X-Auth-User-Key': 'pas',
773 'X-Auth-User-Reseller-Admin': 'true',
774 'X-Auth-Admin-User': 'act:usr',
775 'X-Auth-Admin-Key': 'pas'})
776 resp = self.controller.handle_add_user(req)
777 self.assert_(resp.status_int // 100 == 4, resp.status_int)
778
779 req = Request.blank('/account/act/resadmin2',
780 headers={'X-Auth-User-Key': 'pas',
781 'X-Auth-User-Reseller-Admin': 'true',
782 'X-Auth-Admin-User': 'act:actadmin',
783 'X-Auth-Admin-Key': 'pas'})
784 resp = self.controller.handle_add_user(req)
785 self.assert_(resp.status_int // 100 == 4, resp.status_int)
786
787 req = Request.blank('/account/act/resadmin2',
788 headers={'X-Auth-User-Key': 'pas',
789 'X-Auth-User-Reseller-Admin': 'true',
790 'X-Auth-Admin-User': 'act:resadmin',
791 'X-Auth-Admin-Key': 'pas'})
792 resp = self.controller.handle_add_user(req)
793 self.assert_(resp.status_int // 100 == 4, resp.status_int)
794
795 req = Request.blank('/account/act/resadmin2',
796 headers={'X-Auth-User-Key': 'pas',
797 'X-Auth-User-Reseller-Admin': 'true',
798 'X-Auth-Admin-User': '.super_admin',
799 'X-Auth-Admin-Key': 'testkey'})
800 resp = self.controller.handle_add_user(req)
801 self.assert_(resp.status_int // 100 == 2, resp.status_int)
802
803 def test_handle_add_user_create_account_admin(self):
804 auth_server.http_connect = fake_http_connect(201, 201)
805 self.controller.create_user('act', 'usr', 'pas')
806 self.controller.create_user('act', 'actadmin', 'pas', admin=True)
807 self.controller.create_user('act2', 'actadmin', 'pas', admin=True)
808 self.controller.create_user('act2', 'resadmin', 'pas',
809 reseller_admin=True)
810
811 req = Request.blank('/account/act/actadmin2',
812 headers={'X-Auth-User-Key': 'pas',
813 'X-Auth-User-Admin': 'true'})
814 resp = self.controller.handle_add_user(req)
815 self.assert_(resp.status_int // 100 == 4, resp.status_int)
816
817 req = Request.blank('/account/act/actadmin2',
818 headers={'X-Auth-User-Key': 'pas',
819 'X-Auth-User-Admin': 'true',
820 'X-Auth-Admin-User': 'act:usr',
821 'X-Auth-Admin-Key': 'pas'})
822 resp = self.controller.handle_add_user(req)
823 self.assert_(resp.status_int // 100 == 4, resp.status_int)
824
825 req = Request.blank('/account/act/actadmin2',
826 headers={'X-Auth-User-Key': 'pas',
827 'X-Auth-User-Admin': 'true',
828 'X-Auth-Admin-User': 'act2:actadmin',
829 'X-Auth-Admin-Key': 'pas'})
830 resp = self.controller.handle_add_user(req)
831 self.assert_(resp.status_int // 100 == 4, resp.status_int)
832
833 req = Request.blank('/account/act/actadmin2',
834 headers={'X-Auth-User-Key': 'pas',
835 'X-Auth-User-Admin': 'true',
836 'X-Auth-Admin-User': 'act:actadmin',
837 'X-Auth-Admin-Key': 'pas'})
838 resp = self.controller.handle_add_user(req)
839 self.assert_(resp.status_int // 100 == 2, resp.status_int)
840
841 req = Request.blank('/account/act/actadmin3',
842 headers={'X-Auth-User-Key': 'pas',
843 'X-Auth-User-Admin': 'true',
844 'X-Auth-Admin-User': 'act2:resadmin',
845 'X-Auth-Admin-Key': 'pas'})
846 resp = self.controller.handle_add_user(req)
847 self.assert_(resp.status_int // 100 == 2, resp.status_int)
848
849 req = Request.blank('/account/act/actadmin4',
850 headers={'X-Auth-User-Key': 'pas',
851 'X-Auth-User-Admin': 'true',
852 'X-Auth-Admin-User': '.super_admin',
853 'X-Auth-Admin-Key': 'testkey'})
854 resp = self.controller.handle_add_user(req)
855 self.assert_(resp.status_int // 100 == 2, resp.status_int)
856
857 def test_handle_add_user_create_normal_user(self):
858 auth_server.http_connect = fake_http_connect(201, 201)
859 self.controller.create_user('act', 'usr', 'pas')
860 self.controller.create_user('act', 'actadmin', 'pas', admin=True)
861 self.controller.create_user('act2', 'actadmin', 'pas', admin=True)
862 self.controller.create_user('act2', 'resadmin', 'pas',
863 reseller_admin=True)
864
865 req = Request.blank('/account/act/usr2',
866 headers={'X-Auth-User-Key': 'pas',
867 'X-Auth-User-Admin': 'true'})
868 resp = self.controller.handle_add_user(req)
869 self.assert_(resp.status_int // 100 == 4, resp.status_int)
870
871 req = Request.blank('/account/act/usr2',
872 headers={'X-Auth-User-Key': 'pas',
873 'X-Auth-User-Admin': 'true',
874 'X-Auth-Admin-User': 'act:usr',
875 'X-Auth-Admin-Key': 'pas'})
876 resp = self.controller.handle_add_user(req)
877 self.assert_(resp.status_int // 100 == 4, resp.status_int)
878
879 req = Request.blank('/account/act/usr2',
880 headers={'X-Auth-User-Key': 'pas',
881 'X-Auth-User-Admin': 'true',
882 'X-Auth-Admin-User': 'act2:actadmin',
883 'X-Auth-Admin-Key': 'pas'})
884 resp = self.controller.handle_add_user(req)
885 self.assert_(resp.status_int // 100 == 4, resp.status_int)
886
887 req = Request.blank('/account/act/usr2',
888 headers={'X-Auth-User-Key': 'pas',
889 'X-Auth-User-Admin': 'true',
890 'X-Auth-Admin-User': 'act:actadmin',
891 'X-Auth-Admin-Key': 'pas'})
892 resp = self.controller.handle_add_user(req)
893 self.assert_(resp.status_int // 100 == 2, resp.status_int)
894
895 req = Request.blank('/account/act/usr3',
896 headers={'X-Auth-User-Key': 'pas',
897 'X-Auth-User-Admin': 'true',
898 'X-Auth-Admin-User': 'act2:resadmin',
899 'X-Auth-Admin-Key': 'pas'})
900 resp = self.controller.handle_add_user(req)
901 self.assert_(resp.status_int // 100 == 2, resp.status_int)
902
903 req = Request.blank('/account/act/usr4',
904 headers={'X-Auth-User-Key': 'pas',
905 'X-Auth-User-Admin': 'true',
906 'X-Auth-Admin-User': '.super_admin',
907 'X-Auth-Admin-Key': 'testkey'})
908 resp = self.controller.handle_add_user(req)
909 self.assert_(resp.status_int // 100 == 2, resp.status_int)
910
911 def test_handle_account_recreate_permissions(self):
912 auth_server.http_connect = fake_http_connect(201, 201)
913 self.controller.create_user('act', 'usr', 'pas')
914 self.controller.create_user('act', 'actadmin', 'pas', admin=True)
915 self.controller.create_user('act', 'resadmin', 'pas',
916 reseller_admin=True)
917
918 req = Request.blank('/recreate_accounts',
919 headers={'X-Auth-User-Key': 'pas',
920 'X-Auth-User-Admin': 'true'})
921 resp = self.controller.handle_account_recreate(req)
922 self.assert_(resp.status_int // 100 == 4, resp.status_int)
923
924 req = Request.blank('/recreate_accounts',
925 headers={'X-Auth-User-Key': 'pas',
926 'X-Auth-User-Admin': 'true',
927 'X-Auth-Admin-User': 'act:usr',
928 'X-Auth-Admin-Key': 'pas'})
929 resp = self.controller.handle_account_recreate(req)
930 self.assert_(resp.status_int // 100 == 4, resp.status_int)
931
932 req = Request.blank('/recreate_accounts',
933 headers={'X-Auth-User-Key': 'pas',
934 'X-Auth-User-Admin': 'true',
935 'X-Auth-Admin-User': 'act:actadmin',
936 'X-Auth-Admin-Key': 'pas'})
937 resp = self.controller.handle_account_recreate(req)
938 self.assert_(resp.status_int // 100 == 4, resp.status_int)
939
940 req = Request.blank('/recreate_accounts',
941 headers={'X-Auth-User-Key': 'pas',
942 'X-Auth-User-Admin': 'true',
943 'X-Auth-Admin-User': 'act:resadmin',
944 'X-Auth-Admin-Key': 'pas'})
945 resp = self.controller.handle_account_recreate(req)
946 self.assert_(resp.status_int // 100 == 4, resp.status_int)
947
948 req = Request.blank('/recreate_accounts',
949 headers={'X-Auth-User-Key': 'pas',
950 'X-Auth-User-Admin': 'true',
951 'X-Auth-Admin-User': '.super_admin',
952 'X-Auth-Admin-Key': 'testkey'})
953 resp = self.controller.handle_account_recreate(req)
954 self.assert_(resp.status_int // 100 == 2, resp.status_int)
955
632956
633if __name__ == '__main__':957if __name__ == '__main__':
634 unittest.main()958 unittest.main()
635959
=== modified file 'test/unit/common/middleware/test_auth.py'
--- test/unit/common/middleware/test_auth.py 2010-09-09 17:24:25 +0000
+++ test/unit/common/middleware/test_auth.py 2010-09-12 00:25:58 +0000
@@ -289,6 +289,37 @@
289 req.acl = '.r:.example.com'289 req.acl = '.r:.example.com'
290 self.assertEquals(self.test_auth.authorize(req), None)290 self.assertEquals(self.test_auth.authorize(req), None)
291291
292 def test_account_put_permissions(self):
293 req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'})
294 req.remote_user = 'act:usr,act'
295 resp = str(self.test_auth.authorize(req))
296 self.assert_(resp.startswith('403'), resp)
297
298 req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'})
299 req.remote_user = 'act:usr,act,AUTH_other'
300 resp = str(self.test_auth.authorize(req))
301 self.assert_(resp.startswith('403'), resp)
302
303 # Even PUTs to your own account as account admin should fail
304 req = Request.blank('/v1/AUTH_old', environ={'REQUEST_METHOD': 'PUT'})
305 req.remote_user = 'act:usr,act,AUTH_old'
306 resp = str(self.test_auth.authorize(req))
307 self.assert_(resp.startswith('403'), resp)
308
309 req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'})
310 req.remote_user = 'act:usr,act,.reseller_admin'
311 resp = self.test_auth.authorize(req)
312 self.assertEquals(resp, None)
313
314 # .super_admin is not something the middleware should ever see or care
315 # about
316 req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'})
317 req.remote_user = 'act:usr,act,.super_admin'
318 resp = self.test_auth.authorize(req)
319 resp = str(self.test_auth.authorize(req))
320 self.assert_(resp.startswith('403'), resp)
321
322
292323
293if __name__ == '__main__':324if __name__ == '__main__':
294 unittest.main()325 unittest.main()
295326
=== modified file 'test/unit/proxy/test_server.py'
--- test/unit/proxy/test_server.py 2010-09-09 05:37:27 +0000
+++ test/unit/proxy/test_server.py 2010-09-12 00:25:58 +0000
@@ -2153,90 +2153,136 @@
2153 finally: 2153 finally:
2154 self.app.object_chunk_size = orig_object_chunk_size2154 self.app.object_chunk_size = orig_object_chunk_size
21552155
2156 def test_PUT(self):
2157 with save_globals():
2158 controller = proxy_server.AccountController(self.app, 'account')
2159 def test_status_map(statuses, expected, **kwargs):
2160 proxy_server.http_connect = \
2161 fake_http_connect(*statuses, **kwargs)
2162 self.app.memcache.store = {}
2163 req = Request.blank('/a', {})
2164 req.content_length = 0
2165 self.app.update_request(req)
2166 res = controller.PUT(req)
2167 expected = str(expected)
2168 self.assertEquals(res.status[:len(expected)], expected)
2169 test_status_map((201, 201, 201), 201)
2170 test_status_map((201, 201, 500), 201)
2171 test_status_map((201, 500, 500), 503)
2172 test_status_map((204, 500, 404), 503)
2173
2174 def test_PUT_max_account_name_length(self):
2175 with save_globals():
2176 controller = proxy_server.AccountController(self.app, '1'*256)
2177 self.assert_status_map(controller.PUT, (201, 201, 201), 201)
2178 controller = proxy_server.AccountController(self.app, '2'*257)
2179 self.assert_status_map(controller.PUT, (201, 201, 201), 400)
2180
2181 def test_PUT_connect_exceptions(self):
2182 with save_globals():
2183 controller = proxy_server.AccountController(self.app, 'account')
2184 self.assert_status_map(controller.PUT, (201, 201, -1), 201)
2185 self.assert_status_map(controller.PUT, (201, -1, -1), 503)
2186 self.assert_status_map(controller.PUT, (503, 503, -1), 503)
2187
2188 def test_PUT_metadata(self):
2189 self.metadata_helper('PUT')
2190
2156 def test_POST_metadata(self):2191 def test_POST_metadata(self):
2192 self.metadata_helper('POST')
2193
2194 def metadata_helper(self, method):
2157 for test_header, test_value in (2195 for test_header, test_value in (
2158 ('X-Account-Meta-TestHeader', 'TestValue'),2196 ('X-Account-Meta-TestHeader', 'TestValue'),
2159 ('X-Account-Meta-TestHeader', '')):2197 ('X-Account-Meta-TestHeader', '')):
2160 test_errors = []2198 test_errors = []
2161 def test_connect(ipaddr, port, device, partition, method, path,2199 def test_connect(ipaddr, port, device, partition, method, path,
2162 headers=None, query_string=None):2200 headers=None, query_string=None):
2163 for k, v in headers.iteritems():2201 if path == '/a':
2164 if k.lower() == test_header.lower() and \2202 for k, v in headers.iteritems():
2165 v == test_value:2203 if k.lower() == test_header.lower() and \
2166 break2204 v == test_value:
2167 else:2205 break
2168 test_errors.append('%s: %s not in %s' %2206 else:
2169 (test_header, test_value, headers))2207 test_errors.append('%s: %s not in %s' %
2208 (test_header, test_value, headers))
2170 with save_globals():2209 with save_globals():
2171 controller = \2210 controller = \
2172 proxy_server.AccountController(self.app, 'a')2211 proxy_server.AccountController(self.app, 'a')
2173 proxy_server.http_connect = fake_http_connect(201, 201, 201,2212 proxy_server.http_connect = fake_http_connect(201, 201, 201,
2174 give_connect=test_connect)2213 give_connect=test_connect)
2175 req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'},2214 req = Request.blank('/a/c', environ={'REQUEST_METHOD': method},
2176 headers={test_header: test_value})2215 headers={test_header: test_value})
2177 self.app.update_request(req)2216 self.app.update_request(req)
2178 res = controller.POST(req)2217 res = getattr(controller, method)(req)
2179 self.assertEquals(test_errors, [])2218 self.assertEquals(test_errors, [])
21802219
2220
2221 def test_PUT_bad_metadata(self):
2222 self.bad_metadata_helper('PUT')
2223
2181 def test_POST_bad_metadata(self):2224 def test_POST_bad_metadata(self):
2225 self.bad_metadata_helper('POST')
2226
2227 def bad_metadata_helper(self, method):
2182 with save_globals():2228 with save_globals():
2183 controller = proxy_server.AccountController(self.app, 'a')2229 controller = proxy_server.AccountController(self.app, 'a')
2184 proxy_server.http_connect = fake_http_connect(204, 204, 204)2230 proxy_server.http_connect = fake_http_connect(200, 201, 201, 201)
2185 req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'})2231 req = Request.blank('/a/c', environ={'REQUEST_METHOD': method})
2186 self.app.update_request(req)2232 self.app.update_request(req)
2187 resp = controller.POST(req)2233 resp = getattr(controller, method)(req)
2188 self.assertEquals(resp.status_int, 204)2234 self.assertEquals(resp.status_int, 201)
21892235
2190 proxy_server.http_connect = fake_http_connect(204, 204, 204)2236 proxy_server.http_connect = fake_http_connect(201, 201, 201)
2191 req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'},2237 req = Request.blank('/a/c', environ={'REQUEST_METHOD': method},
2192 headers={'X-Account-Meta-' +2238 headers={'X-Account-Meta-' +
2193 ('a' * MAX_META_NAME_LENGTH): 'v'})2239 ('a' * MAX_META_NAME_LENGTH): 'v'})
2194 self.app.update_request(req)2240 self.app.update_request(req)
2195 resp = controller.POST(req)2241 resp = getattr(controller, method)(req)
2196 self.assertEquals(resp.status_int, 204)2242 self.assertEquals(resp.status_int, 201)
2197 proxy_server.http_connect = fake_http_connect(204, 204, 204)2243 proxy_server.http_connect = fake_http_connect(201, 201, 201)
2198 req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'},2244 req = Request.blank('/a/c', environ={'REQUEST_METHOD': method},
2199 headers={'X-Account-Meta-' +2245 headers={'X-Account-Meta-' +
2200 ('a' * (MAX_META_NAME_LENGTH + 1)): 'v'})2246 ('a' * (MAX_META_NAME_LENGTH + 1)): 'v'})
2201 self.app.update_request(req)2247 self.app.update_request(req)
2202 resp = controller.POST(req)2248 resp = getattr(controller, method)(req)
2203 self.assertEquals(resp.status_int, 400)2249 self.assertEquals(resp.status_int, 400)
22042250
2205 proxy_server.http_connect = fake_http_connect(204, 204, 204)2251 proxy_server.http_connect = fake_http_connect(201, 201, 201)
2206 req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'},2252 req = Request.blank('/a/c', environ={'REQUEST_METHOD': method},
2207 headers={'X-Account-Meta-Too-Long':2253 headers={'X-Account-Meta-Too-Long':
2208 'a' * MAX_META_VALUE_LENGTH})2254 'a' * MAX_META_VALUE_LENGTH})
2209 self.app.update_request(req)2255 self.app.update_request(req)
2210 resp = controller.POST(req)2256 resp = getattr(controller, method)(req)
2211 self.assertEquals(resp.status_int, 204)2257 self.assertEquals(resp.status_int, 201)
2212 proxy_server.http_connect = fake_http_connect(204, 204, 204)2258 proxy_server.http_connect = fake_http_connect(201, 201, 201)
2213 req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'},2259 req = Request.blank('/a/c', environ={'REQUEST_METHOD': method},
2214 headers={'X-Account-Meta-Too-Long':2260 headers={'X-Account-Meta-Too-Long':
2215 'a' * (MAX_META_VALUE_LENGTH + 1)})2261 'a' * (MAX_META_VALUE_LENGTH + 1)})
2216 self.app.update_request(req)2262 self.app.update_request(req)
2217 resp = controller.POST(req)2263 resp = getattr(controller, method)(req)
2218 self.assertEquals(resp.status_int, 400)2264 self.assertEquals(resp.status_int, 400)
22192265
2220 proxy_server.http_connect = fake_http_connect(204, 204, 204)2266 proxy_server.http_connect = fake_http_connect(201, 201, 201)
2221 headers = {}2267 headers = {}
2222 for x in xrange(MAX_META_COUNT):2268 for x in xrange(MAX_META_COUNT):
2223 headers['X-Account-Meta-%d' % x] = 'v'2269 headers['X-Account-Meta-%d' % x] = 'v'
2224 req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'},2270 req = Request.blank('/a/c', environ={'REQUEST_METHOD': method},
2225 headers=headers)2271 headers=headers)
2226 self.app.update_request(req)2272 self.app.update_request(req)
2227 resp = controller.POST(req)2273 resp = getattr(controller, method)(req)
2228 self.assertEquals(resp.status_int, 204)2274 self.assertEquals(resp.status_int, 201)
2229 proxy_server.http_connect = fake_http_connect(204, 204, 204)2275 proxy_server.http_connect = fake_http_connect(201, 201, 201)
2230 headers = {}2276 headers = {}
2231 for x in xrange(MAX_META_COUNT + 1):2277 for x in xrange(MAX_META_COUNT + 1):
2232 headers['X-Account-Meta-%d' % x] = 'v'2278 headers['X-Account-Meta-%d' % x] = 'v'
2233 req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'},2279 req = Request.blank('/a/c', environ={'REQUEST_METHOD': method},
2234 headers=headers)2280 headers=headers)
2235 self.app.update_request(req)2281 self.app.update_request(req)
2236 resp = controller.POST(req)2282 resp = getattr(controller, method)(req)
2237 self.assertEquals(resp.status_int, 400)2283 self.assertEquals(resp.status_int, 400)
22382284
2239 proxy_server.http_connect = fake_http_connect(204, 204, 204)2285 proxy_server.http_connect = fake_http_connect(201, 201, 201)
2240 headers = {}2286 headers = {}
2241 header_value = 'a' * MAX_META_VALUE_LENGTH2287 header_value = 'a' * MAX_META_VALUE_LENGTH
2242 size = 02288 size = 0
@@ -2248,18 +2294,18 @@
2248 if MAX_META_OVERALL_SIZE - size > 1:2294 if MAX_META_OVERALL_SIZE - size > 1:
2249 headers['X-Account-Meta-a'] = \2295 headers['X-Account-Meta-a'] = \
2250 'a' * (MAX_META_OVERALL_SIZE - size - 1)2296 'a' * (MAX_META_OVERALL_SIZE - size - 1)
2251 req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'},2297 req = Request.blank('/a/c', environ={'REQUEST_METHOD': method},
2252 headers=headers)2298 headers=headers)
2253 self.app.update_request(req)2299 self.app.update_request(req)
2254 resp = controller.POST(req)2300 resp = getattr(controller, method)(req)
2255 self.assertEquals(resp.status_int, 204)2301 self.assertEquals(resp.status_int, 201)
2256 proxy_server.http_connect = fake_http_connect(204, 204, 204)2302 proxy_server.http_connect = fake_http_connect(201, 201, 201)
2257 headers['X-Account-Meta-a'] = \2303 headers['X-Account-Meta-a'] = \
2258 'a' * (MAX_META_OVERALL_SIZE - size)2304 'a' * (MAX_META_OVERALL_SIZE - size)
2259 req = Request.blank('/a', environ={'REQUEST_METHOD': 'POST'},2305 req = Request.blank('/a/c', environ={'REQUEST_METHOD': method},
2260 headers=headers)2306 headers=headers)
2261 self.app.update_request(req)2307 self.app.update_request(req)
2262 resp = controller.POST(req)2308 resp = getattr(controller, method)(req)
2263 self.assertEquals(resp.status_int, 400)2309 self.assertEquals(resp.status_int, 400)
22642310
22652311