Per Chuck's suggestion, changed noaccess to admin access, where admin access is not the default. Also, changed swift-auth-create-account to swift-auth-add-user with changes to use optparse

This commit is contained in:
gholt 2010-09-05 19:21:08 -07:00
parent 65eb19f103
commit 0066ed02d7
7 changed files with 76 additions and 74 deletions

View File

@ -15,6 +15,7 @@
# limitations under the License. # limitations under the License.
from ConfigParser import ConfigParser from ConfigParser import ConfigParser
from optparse import OptionParser
from os.path import basename from os.path import basename
from sys import argv, exit from sys import argv, exit
@ -22,45 +23,37 @@ from swift.common.bufferedhttp import http_connect_raw as http_connect
if __name__ == '__main__': if __name__ == '__main__':
f = '/etc/swift/auth-server.conf' default_conf = '/etc/swift/auth-server.conf'
good = False parser = OptionParser(
noaccess = False usage='Usage: %prog [options] <account> <user> <password>')
if len(argv) == 6 and argv[4] == 'noaccess': parser.add_option('-c', '--conf', dest='conf', default=default_conf,
good = True help='Configuration file to determine how to connect to the local '
noaccess = True 'auth server (default: %s).' % default_conf)
f = argv[5] parser.add_option('-a', '--admin', dest='admin', action='store_true',
elif len(argv) == 5: default=False, help='Give the user administrator access; otherwise '
good = True 'the user will only have access to container specifically allowed '
if argv[4] == 'noaccess': 'with ACLs.')
noaccess = True args = argv[1:]
else: if not args:
f = argv[4] args.append('-h')
elif len(argv) == 4: (options, args) = parser.parse_args(args)
good = True if len(args) != 3:
if not good: parser.parse_args(['-h'])
exit(''' account, user, password = args
Syntax: %s <new_account> <new_user> <new_password> [noaccess] [conf_file]
The noaccess keyword will create a user with no access to the account; another
user for the account will have to add the user to the ACLs for a container to
grant some access.
'''.strip() % basename(argv[0]))
new_account = argv[1]
new_user = argv[2]
new_password = argv[3]
c = ConfigParser() c = ConfigParser()
if not c.read(f): if not c.read(options.conf):
exit('Unable to read conf file: %s' % f) exit('Unable to read conf file: %s' % options.conf)
conf = dict(c.items('app:auth-server')) conf = dict(c.items('app:auth-server'))
host = conf.get('bind_ip', '127.0.0.1') host = conf.get('bind_ip', '127.0.0.1')
port = int(conf.get('bind_port', 11000)) port = int(conf.get('bind_port', 11000))
ssl = conf.get('cert_file') is not None ssl = conf.get('cert_file') is not None
path = '/account/%s/%s' % (new_account, new_user) path = '/account/%s/%s' % (account, user)
headers = {'X-Auth-Key': new_password} headers = {'X-Auth-User-Key': password}
if noaccess: if options.admin:
headers['X-User-No-Access'] = 'true' headers['X-Auth-User-Admin'] = 'true'
conn = http_connect(host, port, 'PUT', path, headers, ssl=ssl) conn = http_connect(host, port, 'PUT', path, headers, ssl=ssl)
resp = conn.getresponse() resp = conn.getresponse()
if resp.status == 204: if resp.status == 204:
print resp.getheader('x-storage-url') print resp.getheader('x-storage-url')
else: else:
print 'Account creation failed. (%d)' % resp.status print 'Update failed: %s %s' % (resp.status, resp.reason)

View File

@ -526,19 +526,20 @@ good idea what to do on other environments.
#. `remakerings` #. `remakerings`
#. `cd ~/swift/trunk; ./.unittests` #. `cd ~/swift/trunk; ./.unittests`
#. `startmain` (The ``Unable to increase file descriptor limit. Running as non-root?`` warnings are expected and ok.) #. `startmain` (The ``Unable to increase file descriptor limit. Running as non-root?`` warnings are expected and ok.)
#. `swift-auth-create-account test tester testing` #. `swift-auth-add-user --admin test tester testing`
#. 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`` #. 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``
#. Check that you can GET account: ``curl -v -H 'X-Auth-Token: <token-from-x-auth-token-above>' <url-from-x-storage-url-above>`` #. Check that you can GET account: ``curl -v -H 'X-Auth-Token: <token-from-x-auth-token-above>' <url-from-x-storage-url-above>``
#. Check that `st` works: `st -A http://127.0.0.1:11000/v1.0 -U test:tester -K testing stat` #. Check that `st` works: `st -A http://127.0.0.1:11000/v1.0 -U test:tester -K testing stat`
#. `swift-auth-create-account test2 tester2 testing2` #. `swift-auth-add-user --admin test2 tester2 testing2`
#. `swift-auth-create-account test tester3 testing3 noaccess` #. `swift-auth-add-user test tester3 testing3`
#. Create `/etc/swift/func_test.conf`:: #. 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` #. `cd ~/swift/trunk; ./.functests` (Note: functional tests will first delete
#. `cd ~/swift/trunk; ./.probetests` (Note for future reference: probe tests everything in the configured account.)
will reset your environment) #. `cd ~/swift/trunk; ./.probetests` (Note: probe tests will reset your
environment as they call `resetswift` for each test.)
If you plan to work on documentation (and who doesn't?!): If you plan to work on documentation (and who doesn't?!):

View File

@ -107,9 +107,9 @@ Installing Swift For Use With Cyberduck
cert_file = /etc/swift/cert.crt cert_file = /etc/swift/cert.crt
key_file = /etc/swift/cert.key key_file = /etc/swift/cert.key
#. Use swift-auth-create-account to create a new account:: #. Use swift-auth-add-user to create a new account and admin user::
ubuntu@domU-12-31-39-03-CD-06:/home/swift/swift/bin$ swift-auth-create-account a3 b3 c3 ubuntu@domU-12-31-39-03-CD-06:/home/swift/swift/bin$ swift-auth-add-user --admin a3 b3 c3
https://ec2-184-72-156-130.compute-1.amazonaws.com:8080/v1/06228ccf-6d0a-4395-889e-e971e8de8781 https://ec2-184-72-156-130.compute-1.amazonaws.com:8080/v1/06228ccf-6d0a-4395-889e-e971e8de8781
.. note:: .. note::

View File

@ -61,7 +61,7 @@ setup(
'bin/st', 'bin/swift-account-auditor', 'bin/st', 'bin/swift-account-auditor',
'bin/swift-account-audit', 'bin/swift-account-reaper', 'bin/swift-account-audit', 'bin/swift-account-reaper',
'bin/swift-account-replicator', 'bin/swift-account-server', 'bin/swift-account-replicator', 'bin/swift-account-server',
'bin/swift-auth-create-account', 'bin/swift-auth-add-user',
'bin/swift-auth-recreate-accounts', 'bin/swift-auth-server', 'bin/swift-auth-recreate-accounts', 'bin/swift-auth-server',
'bin/swift-container-auditor', 'bin/swift-container-auditor',
'bin/swift-container-replicator', 'bin/swift-container-replicator',

View File

@ -105,14 +105,14 @@ class AuthController(object):
self.db_file = os.path.join(self.swift_dir, 'auth.db') self.db_file = os.path.join(self.swift_dir, 'auth.db')
self.conn = get_db_connection(self.db_file, okay_to_create=True) self.conn = get_db_connection(self.db_file, okay_to_create=True)
try: try:
self.conn.execute('SELECT noaccess FROM account LIMIT 1') self.conn.execute('SELECT admin FROM account LIMIT 1')
except sqlite3.OperationalError, err: except sqlite3.OperationalError, err:
if str(err) == 'no such column: noaccess': if str(err) == 'no such column: admin':
self.conn.execute( self.conn.execute(
'ALTER TABLE account ADD COLUMN noaccess TEXT') "ALTER TABLE account ADD COLUMN admin TEXT DEFAULT 't'")
self.conn.execute('''CREATE TABLE IF NOT EXISTS account ( self.conn.execute('''CREATE TABLE IF NOT EXISTS account (
account TEXT, url TEXT, cfaccount TEXT, account TEXT, url TEXT, cfaccount TEXT,
user TEXT, password TEXT, noaccess TEXT)''') user TEXT, password TEXT, admin TEXT)''')
self.conn.execute('''CREATE INDEX IF NOT EXISTS ix_account_account self.conn.execute('''CREATE INDEX IF NOT EXISTS ix_account_account
ON account (account)''') ON account (account)''')
try: try:
@ -224,7 +224,8 @@ class AuthController(object):
Tests if the given token is a valid token Tests if the given token is a valid token
:param token: The token to validate :param token: The token to validate
:returns: (TTL, account, user, cfaccount) if valid, False otherwise :returns: (TTL, account, user, cfaccount) if valid, False otherwise.
cfaccount will be None for users without admin access.
""" """
begin = time() begin = time()
self.purge_old_tokens() self.purge_old_tokens()
@ -248,7 +249,7 @@ class AuthController(object):
return rv return rv
def create_account(self, new_account, new_user, new_password, def create_account(self, new_account, new_user, new_password,
noaccess=False): admin=False):
""" """
Handles the create_account call for developers, used to request Handles the create_account call for developers, used to request
an account be created both on a Swift cluster and in the auth server an account be created both on a Swift cluster and in the auth server
@ -266,9 +267,9 @@ class AuthController(object):
:param new_account: The name for the new account :param new_account: The name for the new account
:param new_user: The name for the new user :param new_user: The name for the new user
:param new_password: The password for the new account :param new_password: The password for the new account
:param noaccess: If true, the user will be granted no access to the :param admin: If true, the user will be granted full access to the
account by default; another user will have to add the account; otherwise, another user will have to add the
user to the ACLs for containers to grant access. user to the ACLs for containers to grant access.
:returns: False if the create fails, 'already exists' if the user :returns: False if the create fails, 'already exists' if the user
already exists, or storage url if successful already exists, or storage url if successful
@ -283,7 +284,7 @@ class AuthController(object):
if row: if row:
self.logger.info( self.logger.info(
'ALREADY EXISTS create_account(%s, %s, _, %s) [%.02f]' % 'ALREADY EXISTS create_account(%s, %s, _, %s) [%.02f]' %
(repr(new_account), repr(new_user), repr(noaccess), (repr(new_account), repr(new_user), repr(admin),
time() - begin)) time() - begin))
return 'already exists' return 'already exists'
row = conn.execute( row = conn.execute(
@ -297,19 +298,19 @@ class AuthController(object):
if not account_hash: if not account_hash:
self.logger.info( self.logger.info(
'FAILED create_account(%s, %s, _, %s) [%.02f]' % 'FAILED create_account(%s, %s, _, %s) [%.02f]' %
(repr(new_account), repr(new_user), repr(noaccess), (repr(new_account), repr(new_user), repr(admin),
time() - begin)) time() - begin))
return False return False
url = self.default_cluster_url.rstrip('/') + '/' + account_hash url = self.default_cluster_url.rstrip('/') + '/' + account_hash
conn.execute('''INSERT INTO account conn.execute('''INSERT INTO account
(account, url, cfaccount, user, password, noaccess) (account, url, cfaccount, user, password, admin)
VALUES (?, ?, ?, ?, ?, ?)''', VALUES (?, ?, ?, ?, ?, ?)''',
(new_account, url, account_hash, new_user, new_password, (new_account, url, account_hash, new_user, new_password,
noaccess and 't' or '')) admin and 't' or ''))
conn.commit() conn.commit()
self.logger.info( self.logger.info(
'SUCCESS create_account(%s, %s, _, %s) = %s [%.02f]' % 'SUCCESS create_account(%s, %s, _, %s) = %s [%.02f]' %
(repr(new_account), repr(new_user), repr(noaccess), repr(url), (repr(new_account), repr(new_user), repr(admin), repr(url),
time() - begin)) time() - begin))
return url return url
@ -350,25 +351,31 @@ class AuthController(object):
_, token = split_path(request.path, minsegs=2) _, token = split_path(request.path, minsegs=2)
except ValueError: except ValueError:
return HTTPBadRequest() return HTTPBadRequest()
# Retrieves (TTL, account, user, cfaccount) if valid, False otherwise
validation = self.validate_token(token) validation = self.validate_token(token)
if not validation: if not validation:
return HTTPNotFound() return HTTPNotFound()
# X-Auth-User: account:user,account,cfaccount groups = ['%s:%s' % (validation[1], validation[2]), validation[1]]
if validation[3]: # admin access to a cfaccount
groups.append(validation[3])
return HTTPNoContent(headers={'X-Auth-TTL': validation[0], return HTTPNoContent(headers={'X-Auth-TTL': validation[0],
'X-Auth-User': '%s:%s,%s,%s' % 'X-Auth-User': ','.join(groups)})
(validation[1], validation[2], validation[1], validation[3])})
def handle_account_create(self, request): def handle_add_user(self, request):
""" """
Handles Rest requests from developers to have an account created. Handles Rest requests from developers to have a user added. If the
account specified doesn't exist, it will also be added. Currently,
updating a user's information (password, admin access) must be done by
directly updating the sqlite database.
Valid URL paths: Valid URL paths:
* PUT /account/<account-name>/<user-name> - create the account * PUT /account/<account-name>/<user-name> - create the account
Valid headers: Valid headers:
* X-Auth-Key: <password> (Only required when creating an account) * X-Auth-User-Key: <password>
* X-Auth-User-Admin: <true|false>
If the HTTP request returns with a 204, then the account was created, If the HTTP request returns with a 204, then the user was added,
and the storage url will be available in the X-Storage-Url header. and the storage url will be available in the X-Storage-Url header.
:param request: webob.Request object :param request: webob.Request object
@ -377,11 +384,11 @@ class AuthController(object):
_, account_name, user_name = split_path(request.path, minsegs=3) _, account_name, user_name = split_path(request.path, minsegs=3)
except ValueError: except ValueError:
return HTTPBadRequest() return HTTPBadRequest()
if 'X-Auth-Key' not in request.headers: if 'X-Auth-User-Key' not in request.headers:
return HTTPBadRequest('X-Auth-Key is required') return HTTPBadRequest('X-Auth-User-Key is required')
password = request.headers['x-auth-key'] password = request.headers['x-auth-user-key']
storage_url = self.create_account(account_name, user_name, password, storage_url = self.create_account(account_name, user_name, password,
request.headers.get('x-user-no-access')) request.headers.get('x-auth-user-admin') == 'true')
if storage_url == 'already exists': if storage_url == 'already exists':
return HTTPBadRequest(storage_url) return HTTPBadRequest(storage_url)
if not storage_url: if not storage_url:
@ -458,13 +465,14 @@ class AuthController(object):
self.purge_old_tokens() self.purge_old_tokens()
with self.get_conn() as conn: with self.get_conn() as conn:
row = conn.execute(''' row = conn.execute('''
SELECT cfaccount, url, noaccess FROM account SELECT cfaccount, url, admin FROM account
WHERE account = ? AND user = ? AND password = ?''', WHERE account = ? AND user = ? AND password = ?''',
(account, user, password)).fetchone() (account, user, password)).fetchone()
if row is None: if row is None:
return HTTPUnauthorized() return HTTPUnauthorized()
cfaccount = row[2] and '.none' or row[0] cfaccount = row[0]
url = row[1] url = row[1]
admin = row[2] == 't'
row = conn.execute(''' row = conn.execute('''
SELECT token FROM token WHERE account = ? AND user = ?''', SELECT token FROM token WHERE account = ? AND user = ?''',
(account, user)).fetchone() (account, user)).fetchone()
@ -476,7 +484,7 @@ class AuthController(object):
INSERT INTO token INSERT INTO token
(token, created, account, user, cfaccount) (token, created, account, user, cfaccount)
VALUES (?, ?, ?, ?, ?)''', VALUES (?, ?, ?, ?, ?)''',
(token, time(), account, user, cfaccount)) (token, time(), account, user, admin and cfaccount or ''))
conn.commit() conn.commit()
return HTTPNoContent(headers={'x-auth-token': token, return HTTPNoContent(headers={'x-auth-token': token,
'x-storage-token': token, 'x-storage-token': token,
@ -503,7 +511,7 @@ class AuthController(object):
elif req.method == 'GET' and req.path.startswith('/token/'): elif req.method == 'GET' and req.path.startswith('/token/'):
handler = self.handle_token handler = self.handle_token
elif req.method == 'PUT' and req.path.startswith('/account/'): elif req.method == 'PUT' and req.path.startswith('/account/'):
handler = self.handle_account_create handler = self.handle_add_user
elif req.method == 'POST' and \ elif req.method == 'POST' and \
req.path == '/recreate_accounts': req.path == '/recreate_accounts':
handler = self.handle_account_recreate handler = self.handle_account_recreate

View File

@ -3,17 +3,17 @@ auth_host = 127.0.0.1
auth_port = 11000 auth_port = 11000
auth_ssl = no auth_ssl = no
# Primary functional test account # Primary functional test account (needs admin access to the account)
account = test account = test
username = tester username = tester
password = testing password = testing
# User on a second account # User on a second account (needs admin access to the account)
account2 = test2 account2 = test2
username2 = tester2 username2 = tester2
password2 = testing2 password2 = testing2
# User on same account as first, but with noaccess # User on same account as first, but without admin access
username3 = tester3 username3 = tester3
password3 = testing3 password3 = testing3

View File

@ -462,7 +462,7 @@ class TestContainer(unittest.TestCase):
resp.read() resp.read()
self.assertEquals(resp.status, 201) self.assertEquals(resp.status, 201)
def test_noaccess_user(self): def test_nonadmin_user(self):
if skip or skip3: if skip or skip3:
raise SkipTest raise SkipTest
# Obtain the first account's string # Obtain the first account's string