diff --git a/bin/swift-auth-add-user b/bin/swift-auth-add-user index 76c1be6ac3..0f7eb3c3a2 100755 --- a/bin/swift-auth-add-user +++ b/bin/swift-auth-add-user @@ -31,7 +31,7 @@ if __name__ == '__main__': 'auth server (default: %s).' % default_conf) parser.add_option('-a', '--admin', dest='admin', action='store_true', default=False, help='Give the user administrator access; otherwise ' - 'the user will only have access to container specifically allowed ' + 'the user will only have access to containers specifically allowed ' 'with ACLs.') args = argv[1:] if not args: diff --git a/doc/source/development_saio.rst b/doc/source/development_saio.rst index 6725121bca..b583641ab5 100644 --- a/doc/source/development_saio.rst +++ b/doc/source/development_saio.rst @@ -532,12 +532,9 @@ good idea what to do on other environments. #. Check that `st` works: `st -A http://127.0.0.1:11000/v1.0 -U test:tester -K testing stat` #. `swift-auth-add-user --admin test2 tester2 testing2` #. `swift-auth-add-user test tester3 testing3` - #. Create `/etc/swift/func_test.conf`:: - - cp ~/swift/trunk/test/functional/sample.conf /etc/swift/func_test.conf - + #. `cp ~/swift/trunk/test/functional/sample.conf /etc/swift/func_test.conf` #. `cd ~/swift/trunk; ./.functests` (Note: functional tests will first delete - everything in the configured account.) + everything in the configured accounts.) #. `cd ~/swift/trunk; ./.probetests` (Note: probe tests will reset your environment as they call `resetswift` for each test.) diff --git a/swift/auth/server.py b/swift/auth/server.py index 2b9a6fe2c7..e047397072 100644 --- a/swift/auth/server.py +++ b/swift/auth/server.py @@ -108,8 +108,8 @@ class AuthController(object): self.conn.execute('SELECT admin FROM account LIMIT 1') except sqlite3.OperationalError, err: if str(err) == 'no such column: admin': - self.conn.execute( - "ALTER TABLE account ADD COLUMN admin TEXT DEFAULT 't'") + self.conn.execute("ALTER TABLE account ADD COLUMN admin TEXT") + self.conn.execute("UPDATE account SET admin = 't'") self.conn.execute('''CREATE TABLE IF NOT EXISTS account ( account TEXT, url TEXT, cfaccount TEXT, user TEXT, password TEXT, admin TEXT)''') @@ -248,25 +248,22 @@ class AuthController(object): (repr(token), repr(rv), time() - begin)) return rv - def create_account(self, new_account, new_user, new_password, - admin=False): + def create_user(self, account, user, password, admin=False): """ - Handles the create_account call for developers, used to request - an account be created both on a Swift cluster and in the auth server - database. + Handles the create_user call for developers, used to request a user be + added in the auth server database. If the account does not yet exist, + it will be created on the Swift cluster and the details recorded in the + auth server database. - This will make ReST requests to the Swift cluster's account servers - to have an account created on its side. The resulting account hash - along with the URL to use to access the account, the account name, the - user name, and the password is recorded in the auth server's database. - The url is constructed now and stored separately to support changing - the configuration file's default_cluster_url for directing new accounts - to a different Swift cluster while still supporting old accounts going - to the Swift clusters they were created on. + The url for the storage account is constructed now and stored + separately to support changing the configuration file's + default_cluster_url for directing new accounts to a different Swift + cluster while still supporting old accounts going to the Swift clusters + they were created on. - :param new_account: The name for the new account - :param new_user: The name for the new user - :param new_password: The password for the new account + :param account: The name for the new account + :param user: The name for the new user + :param password: The password for the new account :param admin: If true, the user will be granted full access to the account; otherwise, another user will have to add the user to the ACLs for containers to grant access. @@ -275,21 +272,21 @@ class AuthController(object): already exists, or storage url if successful """ begin = time() - if not all((new_account, new_user, new_password)): + if not all((account, user, password)): return False with self.get_conn() as conn: row = conn.execute( 'SELECT url FROM account WHERE account = ? AND user = ?', - (new_account, new_user)).fetchone() + (account, user)).fetchone() if row: self.logger.info( - 'ALREADY EXISTS create_account(%s, %s, _, %s) [%.02f]' % - (repr(new_account), repr(new_user), repr(admin), + 'ALREADY EXISTS create_user(%s, %s, _, %s) [%.02f]' % + (repr(account), repr(user), repr(admin), time() - begin)) return 'already exists' row = conn.execute( 'SELECT url, cfaccount FROM account WHERE account = ?', - (new_account,)).fetchone() + (account,)).fetchone() if row: url = row[0] account_hash = row[1] @@ -297,20 +294,20 @@ class AuthController(object): account_hash = self.add_storage_account() if not account_hash: self.logger.info( - 'FAILED create_account(%s, %s, _, %s) [%.02f]' % - (repr(new_account), repr(new_user), repr(admin), + 'FAILED create_user(%s, %s, _, %s) [%.02f]' % + (repr(account), repr(user), repr(admin), time() - begin)) return False url = self.default_cluster_url.rstrip('/') + '/' + account_hash conn.execute('''INSERT INTO account (account, url, cfaccount, user, password, admin) VALUES (?, ?, ?, ?, ?, ?)''', - (new_account, url, account_hash, new_user, new_password, + (account, url, account_hash, user, password, admin and 't' or '')) conn.commit() self.logger.info( - 'SUCCESS create_account(%s, %s, _, %s) = %s [%.02f]' % - (repr(new_account), repr(new_user), repr(admin), repr(url), + 'SUCCESS create_user(%s, %s, _, %s) = %s [%.02f]' % + (repr(account), repr(user), repr(admin), repr(url), time() - begin)) return url @@ -342,8 +339,10 @@ class AuthController(object): Valid URL paths: * GET /token/ - If the HTTP equest returns with a 204, then the token is valid, - and the TTL of the token will be available in the X-Auth-Ttl header. + If the HTTP request returns with a 204, then the token is valid, the + TTL of the token will be available in the X-Auth-Ttl header, and a + comma separated list of the "groups" the user belongs to will be in the + X-Auth-Groups header. :param request: webob.Request object """ @@ -359,7 +358,7 @@ class AuthController(object): if validation[3]: # admin access to a cfaccount groups.append(validation[3]) return HTTPNoContent(headers={'X-Auth-TTL': validation[0], - 'X-Auth-User': ','.join(groups)}) + 'X-Auth-Groups': ','.join(groups)}) def handle_add_user(self, request): """ @@ -387,7 +386,7 @@ class AuthController(object): if 'X-Auth-User-Key' not in request.headers: return HTTPBadRequest('X-Auth-User-Key is required') password = request.headers['x-auth-user-key'] - storage_url = self.create_account(account_name, user_name, password, + storage_url = self.create_user(account_name, user_name, password, request.headers.get('x-auth-user-admin') == 'true') if storage_url == 'already exists': return HTTPBadRequest(storage_url) diff --git a/swift/common/middleware/auth.py b/swift/common/middleware/auth.py index e433c7fda0..421770a241 100644 --- a/swift/common/middleware/auth.py +++ b/swift/common/middleware/auth.py @@ -39,19 +39,20 @@ class DevAuth(object): """ Accepts a standard WSGI application call, authenticating the request and installing callback hooks for authorization and ACL header - validation. + validation. For an authenticated request, REMOTE_USER will be set to a + comma separated list of the user's groups. """ - user = None + groups = None token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN')) if token: memcache_client = cache_from_env(env) key = 'devauth/%s' % token cached_auth_data = memcache_client.get(key) if cached_auth_data: - start, expiration, user = cached_auth_data + start, expiration, groups = cached_auth_data if time() - start > expiration: - user = None - if not user: + groups = None + if not groups: with Timeout(self.timeout): conn = http_connect(self.auth_host, self.auth_port, 'GET', '/token/%s' % token, ssl=self.ssl) @@ -61,10 +62,10 @@ class DevAuth(object): if resp.status // 100 != 2: return HTTPUnauthorized()(env, start_response) expiration = float(resp.getheader('x-auth-ttl')) - user = resp.getheader('x-auth-user') - memcache_client.set(key, (time(), expiration, user), + groups = resp.getheader('x-auth-groups') + memcache_client.set(key, (time(), expiration, groups), timeout=expiration) - env['REMOTE_USER'] = user + env['REMOTE_USER'] = groups env['swift.authorize'] = self.authorize env['swift.clean_acl'] = clean_acl return self.app(env, start_response) diff --git a/test/unit/auth/test_server.py b/test/unit/auth/test_server.py index cdaffc33e5..da737c0da8 100644 --- a/test/unit/auth/test_server.py +++ b/test/unit/auth/test_server.py @@ -106,7 +106,7 @@ class TestAuthServer(unittest.TestCase): def test_validate_token_non_existant_token(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - cfaccount = self.controller.create_account( + cfaccount = self.controller.create_user( 'test', 'tester', 'testing',).split('/')[-1] res = self.controller.handle_auth(Request.blank('/v1/test/auth', environ={'REQUEST_METHOD': 'GET'}, @@ -117,7 +117,7 @@ class TestAuthServer(unittest.TestCase): def test_validate_token_good(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - cfaccount = self.controller.create_account( + cfaccount = self.controller.create_user( 'test', 'tester', 'testing',).split('/')[-1] res = self.controller.handle_auth(Request.blank('/v1/test/auth', environ={'REQUEST_METHOD': 'GET'}, @@ -132,7 +132,7 @@ class TestAuthServer(unittest.TestCase): try: auth_server.time = lambda: 1 auth_server.http_connect = fake_http_connect(201, 201, 201) - cfaccount = self.controller.create_account('test', 'tester', + cfaccount = self.controller.create_user('test', 'tester', 'testing').split('/')[-1] res = self.controller.handle_auth(Request.blank('/v1/test/auth', environ={'REQUEST_METHOD': 'GET'}, @@ -146,24 +146,24 @@ class TestAuthServer(unittest.TestCase): finally: auth_server.time = orig_time - def test_create_account_no_new_account(self): + def test_create_user_no_new_account(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - result = self.controller.create_account('', 'tester', 'testing') + result = self.controller.create_user('', 'tester', 'testing') self.assertFalse(result) - def test_create_account_no_new_user(self): + def test_create_user_no_new_user(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - result = self.controller.create_account('test', '', 'testing') + result = self.controller.create_user('test', '', 'testing') self.assertFalse(result) - def test_create_account_no_new_password(self): + def test_create_user_no_new_password(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - result = self.controller.create_account('test', 'tester', '') + result = self.controller.create_user('test', 'tester', '') self.assertFalse(result) - def test_create_account_good(self): + def test_create_user_good(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - url = self.controller.create_account('test', 'tester', 'testing') + url = self.controller.create_user('test', 'tester', 'testing') self.assert_(url) self.assertEquals('/'.join(url.split('/')[:-1]), self.controller.default_cluster_url.rstrip('/'), repr(url)) @@ -176,7 +176,7 @@ class TestAuthServer(unittest.TestCase): def test_recreate_accounts_one(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - self.controller.create_account('test', 'tester', 'testing') + self.controller.create_user('test', 'tester', 'testing') auth_server.http_connect = fake_http_connect(201, 201, 201) rv = self.controller.recreate_accounts() self.assertEquals(rv.split()[0], '1', repr(rv)) @@ -184,13 +184,13 @@ class TestAuthServer(unittest.TestCase): def test_recreate_accounts_several(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - self.controller.create_account('test1', 'tester', 'testing') + self.controller.create_user('test1', 'tester', 'testing') auth_server.http_connect = fake_http_connect(201, 201, 201) - self.controller.create_account('test2', 'tester', 'testing') + self.controller.create_user('test2', 'tester', 'testing') auth_server.http_connect = fake_http_connect(201, 201, 201) - self.controller.create_account('test3', 'tester', 'testing') + self.controller.create_user('test3', 'tester', 'testing') auth_server.http_connect = fake_http_connect(201, 201, 201) - self.controller.create_account('test4', 'tester', 'testing') + self.controller.create_user('test4', 'tester', 'testing') auth_server.http_connect = fake_http_connect(201, 201, 201, 201, 201, 201, 201, 201, 201, @@ -201,7 +201,7 @@ class TestAuthServer(unittest.TestCase): def test_recreate_accounts_one_fail(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - url = self.controller.create_account('test', 'tester', 'testing') + url = self.controller.create_user('test', 'tester', 'testing') cfaccount = url.split('/')[-1] auth_server.http_connect = fake_http_connect(500, 500, 500) rv = self.controller.recreate_accounts() @@ -211,16 +211,16 @@ class TestAuthServer(unittest.TestCase): def test_recreate_accounts_several_fail(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - url = self.controller.create_account('test1', 'tester', 'testing') + url = self.controller.create_user('test1', 'tester', 'testing') cfaccounts = [url.split('/')[-1]] auth_server.http_connect = fake_http_connect(201, 201, 201) - url = self.controller.create_account('test2', 'tester', 'testing') + url = self.controller.create_user('test2', 'tester', 'testing') cfaccounts.append(url.split('/')[-1]) auth_server.http_connect = fake_http_connect(201, 201, 201) - url = self.controller.create_account('test3', 'tester', 'testing') + url = self.controller.create_user('test3', 'tester', 'testing') cfaccounts.append(url.split('/')[-1]) auth_server.http_connect = fake_http_connect(201, 201, 201) - url = self.controller.create_account('test4', 'tester', 'testing') + url = self.controller.create_user('test4', 'tester', 'testing') cfaccounts.append(url.split('/')[-1]) auth_server.http_connect = fake_http_connect(500, 500, 500, 500, 500, 500, @@ -233,16 +233,16 @@ class TestAuthServer(unittest.TestCase): def test_recreate_accounts_several_fail_some(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - url = self.controller.create_account('test1', 'tester', 'testing') + url = self.controller.create_user('test1', 'tester', 'testing') cfaccounts = [url.split('/')[-1]] auth_server.http_connect = fake_http_connect(201, 201, 201) - url = self.controller.create_account('test2', 'tester', 'testing') + url = self.controller.create_user('test2', 'tester', 'testing') cfaccounts.append(url.split('/')[-1]) auth_server.http_connect = fake_http_connect(201, 201, 201) - url = self.controller.create_account('test3', 'tester', 'testing') + url = self.controller.create_user('test3', 'tester', 'testing') cfaccounts.append(url.split('/')[-1]) auth_server.http_connect = fake_http_connect(201, 201, 201) - url = self.controller.create_account('test4', 'tester', 'testing') + url = self.controller.create_user('test4', 'tester', 'testing') cfaccounts.append(url.split('/')[-1]) auth_server.http_connect = fake_http_connect(500, 500, 500, 201, 201, 201, @@ -263,7 +263,7 @@ class TestAuthServer(unittest.TestCase): def test_auth_SOSO_missing_headers(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - cfaccount = self.controller.create_account( + cfaccount = self.controller.create_user( 'test', 'tester', 'testing').split('/')[-1] res = self.controller.handle_auth(Request.blank('/v1/test/auth', environ={'REQUEST_METHOD': 'GET'}, @@ -279,7 +279,7 @@ class TestAuthServer(unittest.TestCase): def test_auth_SOSO_bad_account(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - cfaccount = self.controller.create_account( + cfaccount = self.controller.create_user( 'test', 'tester', 'testing').split('/')[-1] res = self.controller.handle_auth(Request.blank('/v1/testbad/auth', environ={'REQUEST_METHOD': 'GET'}, @@ -294,7 +294,7 @@ class TestAuthServer(unittest.TestCase): def test_auth_SOSO_bad_user(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - cfaccount = self.controller.create_account( + cfaccount = self.controller.create_user( 'test', 'tester', 'testing').split('/')[-1] res = self.controller.handle_auth(Request.blank('/v1/test/auth', environ={'REQUEST_METHOD': 'GET'}, @@ -309,7 +309,7 @@ class TestAuthServer(unittest.TestCase): def test_auth_SOSO_bad_password(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - cfaccount = self.controller.create_account( + cfaccount = self.controller.create_user( 'test', 'tester', 'testing').split('/')[-1] res = self.controller.handle_auth(Request.blank('/v1/test/auth', environ={'REQUEST_METHOD': 'GET'}, @@ -324,7 +324,7 @@ class TestAuthServer(unittest.TestCase): def test_auth_SOSO_good(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - cfaccount = self.controller.create_account( + cfaccount = self.controller.create_user( 'test', 'tester', 'testing').split('/')[-1] res = self.controller.handle_auth(Request.blank('/v1/test/auth', environ={'REQUEST_METHOD': 'GET'}, @@ -336,7 +336,7 @@ class TestAuthServer(unittest.TestCase): def test_auth_SOSO_good_Mosso_headers(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - cfaccount = self.controller.create_account( + cfaccount = self.controller.create_user( 'test', 'tester', 'testing').split('/')[-1] res = self.controller.handle_auth(Request.blank('/v1/test/auth', environ={'REQUEST_METHOD': 'GET'}, @@ -348,7 +348,7 @@ class TestAuthServer(unittest.TestCase): def test_auth_SOSO_bad_Mosso_headers(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - cfaccount = self.controller.create_account( + cfaccount = self.controller.create_user( 'test', 'tester', 'testing',).split('/')[-1] res = self.controller.handle_auth(Request.blank('/v1/test/auth', environ={'REQUEST_METHOD': 'GET'}, @@ -368,7 +368,7 @@ class TestAuthServer(unittest.TestCase): def test_auth_Mosso_missing_headers(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - cfaccount = self.controller.create_account( + cfaccount = self.controller.create_user( 'test', 'tester', 'testing').split('/')[-1] res = self.controller.handle_auth(Request.blank('/auth', environ={'REQUEST_METHOD': 'GET'})) @@ -384,7 +384,7 @@ class TestAuthServer(unittest.TestCase): def test_auth_Mosso_bad_header_format(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - cfaccount = self.controller.create_account( + cfaccount = self.controller.create_user( 'test', 'tester', 'testing').split('/')[-1] res = self.controller.handle_auth(Request.blank('/auth', environ={'REQUEST_METHOD': 'GET'}, @@ -399,7 +399,7 @@ class TestAuthServer(unittest.TestCase): def test_auth_Mosso_bad_account(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - cfaccount = self.controller.create_account( + cfaccount = self.controller.create_user( 'test', 'tester', 'testing').split('/')[-1] res = self.controller.handle_auth(Request.blank('/auth', environ={'REQUEST_METHOD': 'GET'}, @@ -414,7 +414,7 @@ class TestAuthServer(unittest.TestCase): def test_auth_Mosso_bad_user(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - cfaccount = self.controller.create_account( + cfaccount = self.controller.create_user( 'test', 'tester', 'testing').split('/')[-1] res = self.controller.handle_auth(Request.blank('/auth', environ={'REQUEST_METHOD': 'GET'}, @@ -429,7 +429,7 @@ class TestAuthServer(unittest.TestCase): def test_auth_Mosso_bad_password(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - cfaccount = self.controller.create_account( + cfaccount = self.controller.create_user( 'test', 'tester', 'testing').split('/')[-1] res = self.controller.handle_auth(Request.blank('/auth', environ={'REQUEST_METHOD': 'GET'}, @@ -444,7 +444,7 @@ class TestAuthServer(unittest.TestCase): def test_auth_Mosso_good(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - cfaccount = self.controller.create_account( + cfaccount = self.controller.create_user( 'test', 'tester', 'testing').split('/')[-1] res = self.controller.handle_auth(Request.blank('/auth', environ={'REQUEST_METHOD': 'GET'}, @@ -456,7 +456,7 @@ class TestAuthServer(unittest.TestCase): def test_auth_Mosso_good_SOSO_header_names(self): auth_server.http_connect = fake_http_connect(201, 201, 201) - cfaccount = self.controller.create_account( + cfaccount = self.controller.create_user( 'test', 'tester', 'testing').split('/')[-1] res = self.controller.handle_auth(Request.blank('/auth', environ={'REQUEST_METHOD': 'GET'}, @@ -473,9 +473,9 @@ class TestAuthServer(unittest.TestCase): logger.logger.addHandler(log_handler) try: auth_server.http_connect = fake_http_connect(201, 201, 201) - url = self.controller.create_account('test', 'tester', 'testing') + url = self.controller.create_user('test', 'tester', 'testing') self.assertEquals(log.getvalue().rsplit(' ', 1)[0], - "auth SUCCESS create_account('test', 'tester', _, False) = %s" + "auth SUCCESS create_user('test', 'tester', _, False) = %s" % repr(url)) log.truncate(0) def start_response(*args):