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:
parent
65eb19f103
commit
0066ed02d7
@ -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)
|
@ -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?!):
|
||||||
|
|
||||||
|
@ -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::
|
||||||
|
2
setup.py
2
setup.py
@ -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',
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user