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.
from ConfigParser import ConfigParser
from optparse import OptionParser
from os.path import basename
from sys import argv, exit
@ -22,45 +23,37 @@ from swift.common.bufferedhttp import http_connect_raw as http_connect
if __name__ == '__main__':
f = '/etc/swift/auth-server.conf'
good = False
noaccess = False
if len(argv) == 6 and argv[4] == 'noaccess':
good = True
noaccess = True
f = argv[5]
elif len(argv) == 5:
good = True
if argv[4] == 'noaccess':
noaccess = True
else:
f = argv[4]
elif len(argv) == 4:
good = True
if not good:
exit('''
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]
default_conf = '/etc/swift/auth-server.conf'
parser = OptionParser(
usage='Usage: %prog [options] <account> <user> <password>')
parser.add_option('-c', '--conf', dest='conf', default=default_conf,
help='Configuration file to determine how to connect to the local '
'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 '
'with ACLs.')
args = argv[1:]
if not args:
args.append('-h')
(options, args) = parser.parse_args(args)
if len(args) != 3:
parser.parse_args(['-h'])
account, user, password = args
c = ConfigParser()
if not c.read(f):
exit('Unable to read conf file: %s' % f)
if not c.read(options.conf):
exit('Unable to read conf file: %s' % options.conf)
conf = dict(c.items('app:auth-server'))
host = conf.get('bind_ip', '127.0.0.1')
port = int(conf.get('bind_port', 11000))
ssl = conf.get('cert_file') is not None
path = '/account/%s/%s' % (new_account, new_user)
headers = {'X-Auth-Key': new_password}
if noaccess:
headers['X-User-No-Access'] = 'true'
path = '/account/%s/%s' % (account, user)
headers = {'X-Auth-User-Key': password}
if options.admin:
headers['X-Auth-User-Admin'] = 'true'
conn = http_connect(host, port, 'PUT', path, headers, ssl=ssl)
resp = conn.getresponse()
if resp.status == 204:
print resp.getheader('x-storage-url')
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`
#. `cd ~/swift/trunk; ./.unittests`
#. `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``
#. 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`
#. `swift-auth-create-account test2 tester2 testing2`
#. `swift-auth-create-account test tester3 testing3 noaccess`
#. `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
#. `cd ~/swift/trunk; ./.functests`
#. `cd ~/swift/trunk; ./.probetests` (Note for future reference: probe tests
will reset your environment)
#. `cd ~/swift/trunk; ./.functests` (Note: functional tests will first delete
everything in the configured account.)
#. `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?!):

View File

@ -107,9 +107,9 @@ Installing Swift For Use With Cyberduck
cert_file = /etc/swift/cert.crt
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
.. note::

View File

@ -61,7 +61,7 @@ setup(
'bin/st', 'bin/swift-account-auditor',
'bin/swift-account-audit', 'bin/swift-account-reaper',
'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-container-auditor',
'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.conn = get_db_connection(self.db_file, okay_to_create=True)
try:
self.conn.execute('SELECT noaccess FROM account LIMIT 1')
self.conn.execute('SELECT admin FROM account LIMIT 1')
except sqlite3.OperationalError, err:
if str(err) == 'no such column: noaccess':
if str(err) == 'no such column: admin':
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 (
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
ON account (account)''')
try:
@ -224,7 +224,8 @@ class AuthController(object):
Tests if the given token is a valid token
: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()
self.purge_old_tokens()
@ -248,7 +249,7 @@ class AuthController(object):
return rv
def create_account(self, new_account, new_user, new_password,
noaccess=False):
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
@ -266,9 +267,9 @@ class AuthController(object):
: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 noaccess: If true, the user will be granted no access to the
account by default; another user will have to add the
user to the ACLs for containers to grant access.
: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.
:returns: False if the create fails, 'already exists' if the user
already exists, or storage url if successful
@ -283,7 +284,7 @@ class AuthController(object):
if row:
self.logger.info(
'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))
return 'already exists'
row = conn.execute(
@ -297,19 +298,19 @@ class AuthController(object):
if not account_hash:
self.logger.info(
'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))
return False
url = self.default_cluster_url.rstrip('/') + '/' + account_hash
conn.execute('''INSERT INTO account
(account, url, cfaccount, user, password, noaccess)
(account, url, cfaccount, user, password, admin)
VALUES (?, ?, ?, ?, ?, ?)''',
(new_account, url, account_hash, new_user, new_password,
noaccess and 't' or ''))
admin and 't' or ''))
conn.commit()
self.logger.info(
'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))
return url
@ -350,25 +351,31 @@ class AuthController(object):
_, token = split_path(request.path, minsegs=2)
except ValueError:
return HTTPBadRequest()
# Retrieves (TTL, account, user, cfaccount) if valid, False otherwise
validation = self.validate_token(token)
if not validation:
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],
'X-Auth-User': '%s:%s,%s,%s' %
(validation[1], validation[2], validation[1], validation[3])})
'X-Auth-User': ','.join(groups)})
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:
* PUT /account/<account-name>/<user-name> - create the account
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.
:param request: webob.Request object
@ -377,11 +384,11 @@ class AuthController(object):
_, account_name, user_name = split_path(request.path, minsegs=3)
except ValueError:
return HTTPBadRequest()
if 'X-Auth-Key' not in request.headers:
return HTTPBadRequest('X-Auth-Key is required')
password = request.headers['x-auth-key']
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,
request.headers.get('x-user-no-access'))
request.headers.get('x-auth-user-admin') == 'true')
if storage_url == 'already exists':
return HTTPBadRequest(storage_url)
if not storage_url:
@ -458,13 +465,14 @@ class AuthController(object):
self.purge_old_tokens()
with self.get_conn() as conn:
row = conn.execute('''
SELECT cfaccount, url, noaccess FROM account
SELECT cfaccount, url, admin FROM account
WHERE account = ? AND user = ? AND password = ?''',
(account, user, password)).fetchone()
if row is None:
return HTTPUnauthorized()
cfaccount = row[2] and '.none' or row[0]
cfaccount = row[0]
url = row[1]
admin = row[2] == 't'
row = conn.execute('''
SELECT token FROM token WHERE account = ? AND user = ?''',
(account, user)).fetchone()
@ -476,7 +484,7 @@ class AuthController(object):
INSERT INTO token
(token, created, account, user, cfaccount)
VALUES (?, ?, ?, ?, ?)''',
(token, time(), account, user, cfaccount))
(token, time(), account, user, admin and cfaccount or ''))
conn.commit()
return HTTPNoContent(headers={'x-auth-token': token,
'x-storage-token': token,
@ -503,7 +511,7 @@ class AuthController(object):
elif req.method == 'GET' and req.path.startswith('/token/'):
handler = self.handle_token
elif req.method == 'PUT' and req.path.startswith('/account/'):
handler = self.handle_account_create
handler = self.handle_add_user
elif req.method == 'POST' and \
req.path == '/recreate_accounts':
handler = self.handle_account_recreate

View File

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

View File

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