swauth-list work; new swauth-set-account-service; .clusters is now .services; doc updates

This commit is contained in:
gholt
2010年12月01日 21:28:41 -08:00
parent 35f3487879
commit 3d2985201c

View File

@@ -25,8 +25,11 @@ from swift.common.bufferedhttp import http_connect_raw as http_connect
if __name__ == '__main__':
parser = OptionParser(usage='Usage: %prog [options] <account>')
parser.add_option('-s', '--suffix', dest='suffix',
default='', help='The suffix to use as the storage account name '
'(default: <randomly-generated-uuid4>)')
default='', help='The suffix to use with the reseller prefix as the '
'storage account name (default: <randomly-generated-uuid4>) Note: If '
'the account already exists, this will have no effect on existing '
'service URLs. Those will need to be updated with '
'swauth-set-account-service')
parser.add_option('-A', '--admin-url', dest='admin_url',
default='http://127.0.0.1:8080/auth/', help='The URL to the auth '
'subsystem (default: http://127.0.0.1:8080/auth/)')

View File

@@ -35,8 +35,11 @@ if __name__ == '__main__':
'the reseller, including the ability to create new accounts. Creating '
'a new reseller admin requires super_admin rights.')
parser.add_option('-s', '--suffix', dest='suffix',
default='', help='The suffix to use as the storage account name '
'(default: <randomly-generated-uuid4>)')
default='', help='The suffix to use with the reseller prefix as the '
'storage account name (default: <randomly-generated-uuid4>) Note: If '
'the account already exists, this will have no effect on existing '
'service URLs. Those will need to be updated with '
'swauth-set-account-service')
parser.add_option('-A', '--admin-url', dest='admin_url',
default='http://127.0.0.1:8080/auth/', help='The URL to the auth '
'subsystem (default: http://127.0.0.1:8080/auth/')

View File

@@ -27,7 +27,23 @@ from swift.common.bufferedhttp import http_connect_raw as http_connect
if __name__ == '__main__':
parser = OptionParser(usage='Usage: %prog [options] [account] [user]')
parser = OptionParser(usage='''
Usage: %prog [options] [account] [user]
If [account] and [user] are omitted, a list of accounts will be output.
If [account] is included but not [user], an account's information will be
output, including a list of users within the account.
If [account] and [user] are included, the user's information will be output,
including a list of groups the user belongs to.
If the [user] is '.groups', the active groups for the account will be listed.
'''.strip())
parser.add_option('-p', '--plain-text', dest='plain_text',
action='store_true', default=False, help='Changes the output from '
'JSON to plain text. This will cause an account to list only the '
'users and a user to list only the groups.')
parser.add_option('-A', '--admin-url', dest='admin_url',
default='http://127.0.0.1:8080/auth/', help='The URL to the auth '
'subsystem (default: http://127.0.0.1:8080/auth/')
@@ -58,8 +74,10 @@ if __name__ == '__main__':
resp = conn.getresponse()
if resp.status // 100 != 2:
print 'List failed: %s %s' % (resp.status, resp.reason)
if len(args) == 2 and args[1] != '.groups':
print resp.read()
body = resp.read()
if options.plain_text:
info = json.loads(body)
for group in info[['accounts', 'users', 'groups'][len(args)]]:
print group['name']
else:
for item in json.loads(resp.read()):
print item['name']
print body

View File

@@ -441,7 +441,15 @@ See :ref:`config-proxy` for the initial setup, and then follow these additional
# Highly recommended to change this key to something else!
super_admin_key = swauthkey
#. For DevAuth, after you change the default_cluster_url setting, you have to delete the auth database and recreate the Swift users, or manually update the auth database with the correct URL for each account. For Swauth, changing the cluster URLs for the accounts is not yet supported (you'd have to hack the .cluster objects manually; not recommended).
#. For DevAuth, after you change the default_cluster_url setting, you have to delete the auth database and recreate the Swift users, or manually update the auth database with the correct URL for each account.
For Swauth, you can change a service URL with::
swauth-set-account-service -K swauthkey <account> storage local <new_url_for_the_account>
You can obtain old service URLs with::
swauth-list -K swauthkey <account>
#. Next, copy all the ring information to all the nodes, including your new proxy nodes, and ensure the ring info gets to all the storage nodes as well.

View File

@@ -87,7 +87,6 @@ Source Documentation
db
object
auth
swauth
misc

View File

@@ -54,7 +54,7 @@ Swauth
------
The Swauth system is an optional DevAuth replacement included at
swift/common/middleware/swauth.py is a scalable authentication and
swift/common/middleware/swauth.py; a scalable authentication and
authorization system that uses Swift itself as its backing store. This section
will describe how it stores its data.
@@ -93,8 +93,8 @@ Id string is stored in the `X-Container-Meta-Account-Id` header for the
the corresponding auth service's account name.
Also, to support a future where the auth service will support multiple Swift
clusters for the same auth service account, an
<auth_account>/<account>/.clusters object is created with its contents having a
clusters or even multiple services for the same auth service account, an
<auth_account>/<account>/.services object is created with its contents having a
JSON dictionary of the format::
{"storage": {"default": "local", "local": <url>}}
@@ -105,7 +105,7 @@ various names instead of just "local", and the "default" key's value will
contain the primary cluster to use for that account. Also, there may be more
services in addition to the current "storage" service right now.
Here's an example .clusters dictionary at the moment::
Here's an example .services dictionary at the moment::
{"storage":
{"default": "local",
@@ -165,12 +165,12 @@ Here is an example full listing of an <auth_account>::
AUTH_tk7594203449754c22a34ac7d910521c2e
AUTH_tk8f2ee54605dd42a8913d244de544d19e
reseller
.clusters
.services
reseller
test
.clusters
.services
tester
tester3
test2
.clusters
.services
tester2

View File

@@ -81,7 +81,8 @@ setup(
'bin/swift-account-stats-logger',
'bin/swauth-add-account', 'bin/swauth-add-user',
'bin/swauth-delete-account', 'bin/swauth-delete-user',
'bin/swauth-list', 'bin/swauth-prep', 'bin/swift-auth-to-swauth',
'bin/swauth-list', 'bin/swauth-prep', 'bin/swauth-set-account-service',
'bin/swift-auth-to-swauth',
],
entry_points={
'paste.app_factory': [

View File

@@ -181,7 +181,7 @@ class Swauth(object):
detail = json.loads(resp.body)
if detail['expires'] < time():
return None
groups = detail['groups']
groups = [g['name'] for g in detail['groups']]
if '.admin' in groups:
groups.remove('.admin')
groups.append(detail['account_id'])
@@ -283,7 +283,7 @@ class Swauth(object):
req.path_info_pop()
if req.method == 'GET':
if not account and not user:
handler = self.handle_get_accounts
handler = self.handle_get_reseller
elif account:
if not user:
handler = self.handle_get_account
@@ -305,6 +305,8 @@ class Swauth(object):
elif req.method == 'POST':
if account == '.prep':
handler = self.handle_prep
elif user == '.services':
handler = self.handle_set_services
if not handler:
req.response = HTTPBadRequest(request=req)
else:
@@ -337,17 +339,22 @@ class Swauth(object):
(path, resp.status))
return HTTPNoContent(request=req)
def handle_get_accounts(self, req):
def handle_get_reseller(self, req):
"""
Handles the GET v2 call for listing the accounts handled by this auth
system. Can only be called by a .reseller_admin.
Handles the GET v2 call for getting general reseller information
(currently just a list of accounts). Can only be called by a
.reseller_admin.
On success, a JSON list of dicts will be returned. Each dict represents
an account and currently only contains the single key `name`.
On success, a JSON dictionary will be returned with a single `accounts`
key whose value is list of dicts. Each dict represents an account and
currently only contains the single key `name`. For example::
{"accounts": [{"name": "reseller"}, {"name": "test"},
{"name": "test2"}]}
:param req: The webob.Request to process.
:returns: webob.Response, 2xx on success with a JSON list of the
accounts as explained above.
:returns: webob.Response, 2xx on success with a JSON dictionary as
explained above.
"""
if not self.is_reseller_admin(req):
return HTTPForbidden(request=req)
@@ -368,25 +375,43 @@ class Swauth(object):
if container['name'][0] != '.':
listing.append({'name': container['name']})
marker = sublisting[-1]['name']
return Response(body=json.dumps(listing))
return Response(body=json.dumps({'accounts': listing}))
def handle_get_account(self, req):
"""
Handles the GET v2/<account> call for listing the users in an account.
Handles the GET v2/<account> call for getting account information.
Can only be called by an account .admin.
On success, a JSON list of dicts will be returned. Each dict represents
a user and currently only contains the single key `name`.
On success, a JSON dictionary will be returned containing the keys
`account_id`, `services`, and `users`. The `account_id` is the value
used when creating service accounts. The `services` value is a dict as
described in the :func:`handle_get_token` call. The `users` value is a
list of dicts, each dict representing a user and currently only
containing the single key `name`. For example::
{"account_id": "AUTH_018c3946-23f8-4efb-a8fb-b67aae8e4162",
"services": {"storage": {"default": "local",
"local": "http://127.0.0.1:8080/v1/AUTH_018c3946-23f8-4efb-a8fb-b67aae8e4162"}},
"users": [{"name": "tester"}, {"name": "tester3"}]}
:param req: The webob.Request to process.
:returns: webob.Response, 2xx on success with a JSON list of the users
in the account as explained above.
:returns: webob.Response, 2xx on success with a JSON dictionary as
explained above.
"""
account = req.path_info_pop()
if req.path_info:
return HTTPBadRequest(request=req)
if not self.is_account_admin(req, account):
return HTTPForbidden(request=req)
path = quote('/v1/%s/%s/.services' % (self.auth_account, account))
resp = self.make_request(req.environ, 'GET',
path).get_response(self.app)
if resp.status_int == 404:
return HTTPNotFound(request=req)
if resp.status_int // 100 != 2:
raise Exception('Could not obtain the .services object: %s%s' %
(path, resp.status))
services = json.loads(resp.body)
listing = []
marker = ''
while True:
@@ -394,6 +419,7 @@ class Swauth(object):
(self.auth_account, account)), quote(marker))
resp = self.make_request(req.environ, 'GET',
path).get_response(self.app)
account_id = resp.headers['X-Container-Meta-Account-Id']
if resp.status_int == 404:
return HTTPNotFound(request=req)
if resp.status_int // 100 != 2:
@@ -406,7 +432,38 @@ class Swauth(object):
if obj['name'][0] != '.':
listing.append({'name': obj['name']})
marker = sublisting[-1]['name']
return Response(body=json.dumps(listing))
return Response(body=json.dumps({'account_id': account_id,
'services': services, 'users': listing}))
def handle_set_services(self, req):
if not self.is_reseller_admin(req):
return HTTPForbidden(request=req)
account = req.path_info_pop()
if req.path_info != '/.services' or not account.isalnum():
return HTTPBadRequest(request=req)
new_services = json.loads(req.body)
# Get the current services information
path = quote('/v1/%s/%s/.services' % (self.auth_account, account))
resp = self.make_request(req.environ, 'GET',
path).get_response(self.app)
if resp.status_int == 404:
return HTTPNotFound(request=req)
if resp.status_int // 100 != 2:
raise Exception('Could not obtain services info: %s%s' %
(path, resp.status))
services = json.loads(resp.body)
for new_service, value in new_services.iteritems():
if new_service in services:
services[new_service].update(value)
else:
services[new_service] = value
# Save the new services information
resp = self.make_request(req.environ, 'PUT', path,
json.dumps(services)).get_response(self.app)
if resp.status_int // 100 != 2:
raise Exception('Could not save .services object: %s%s' %
(path, resp.status))
return HTTPNoContent(request=req)
def handle_put_account(self, req):
"""
@@ -467,15 +524,15 @@ class Swauth(object):
raise Exception('Could not create account id mapping: %s%s' %
(path, resp.status))
# Record the cluster url(s) for the account
path = quote('/v1/%s/%s/.clusters' % (self.auth_account, account))
clusters = {'storage': {}}
clusters['storage'][self.dsc_name] = '%s/%s%s' % (self.dsc_url,
path = quote('/v1/%s/%s/.services' % (self.auth_account, account))
services = {'storage': {}}
services['storage'][self.dsc_name] = '%s/%s%s' % (self.dsc_url,
self.reseller_prefix, account_suffix)
clusters['storage']['default'] = self.dsc_name
services['storage']['default'] = self.dsc_name
resp = self.make_request(req.environ, 'PUT', path,
json.dumps(clusters)).get_response(self.app)
json.dumps(services)).get_response(self.app)
if resp.status_int // 100 != 2:
raise Exception('Could not create .clusters object: %s%s' %
raise Exception('Could not create .services object: %s%s' %
(path, resp.status))
# Record the mapping from account name to the account id
path = quote('/v1/%s/%s' % (self.auth_account, account))
@@ -519,16 +576,16 @@ class Swauth(object):
if obj['name'][0] != '.':
return HTTPConflict(request=req)
marker = sublisting[-1]['name']
# Obtain the listing of clusters the account is on.
path = quote('/v1/%s/%s/.clusters' % (self.auth_account, account))
# Obtain the listing of services the account is on.
path = quote('/v1/%s/%s/.services' % (self.auth_account, account))
resp = self.make_request(req.environ, 'GET',
path).get_response(self.app)
if resp.status_int == 404:
return HTTPNoContent(request=req)
elif resp.status_int // 100 == 2:
clusters = json.loads(resp.body)
services = json.loads(resp.body)
# Delete the account on each cluster it is on.
for name, url in clusters['storage'].iteritems():
for name, url in services['storage'].iteritems():
if name != 'default':
parsed = urlparse(url)
if parsed.scheme == 'http':
@@ -543,8 +600,8 @@ class Swauth(object):
raise Exception('Could not delete account on the '
'Swift cluster: %s%s%s' %
(url, resp.status, resp.reason))
# Delete the .clusters object itself.
path = quote('/v1/%s/%s/.clusters' %
# Delete the .services object itself.
path = quote('/v1/%s/%s/.services' %
(self.auth_account, account))
resp = self.make_request(req.environ, 'DELETE',
path).get_response(self.app)
@@ -577,31 +634,43 @@ class Swauth(object):
def handle_get_user(self, req):
"""
Handles the GET v2/<account>/<user> call for retrieving the user's JSON
dict. Can only be called by an account .admin.
Handles the GET v2/<account>/<user> call for getting user information.
Can only be called by an account .admin.
On success, a JSON dict will be returned as described::
{"groups": [ # List of groups the user is a member of
"<act>:<usr>", # The first group is a unique user identifier
"<account>", # The second group is the auth account name
"<additional-group>"...
# There may be additional groups, .admin being a special group
# indicating an account admin and .reseller_admin indicating a
# reseller admin.
{"groups": [ # List of groups the user is a member of
{"name": "<act>:<usr>"},
# The first group is a unique user identifier
{"name": "<account>"},
# The second group is the auth account name
{"name": "<additional-group>"}
# There may be additional groups, .admin being a special
# group indicating an account admin and .reseller_admin
# indicating a reseller admin.
],
"auth": "plaintext:<key>"
# The auth-type and key for the user; currently only plaintext is
# implemented.
}
If the <user> in the request is the special user `.groups`, a JSON list
of dicts will be returned instead, each dict representing a group in
the account currently with just the single key `name`.
For example::
{"groups": [{"name": "test:tester"}, {"name": "test"},
{"name": ".admin"}],
"auth": "plaintext:testing"}
If the <user> in the request is the special user `.groups`, the JSON
dict will contain a single key of `groups` whose value is a list of
dicts representing the active groups within the account. Each dict
currently has the single key `name`. For example::
{"groups": [{"name": ".admin"}, {"name": "test"},
{"name": "test:tester"}, {"name": "test:tester3"}]}
:param req: The webob.Request to process.
:returns: webob.Response, 2xx on success with data set as explained
above.
:returns: webob.Response, 2xx on success with a JSON dictionary as
explained above.
"""
account = req.path_info_pop()
user = req.path_info_pop()
@@ -641,9 +710,11 @@ class Swauth(object):
if resp.status_int // 100 != 2:
raise Exception('Could not retrieve user object: '
'%s%s' % (path, resp.status))
groups.update(json.loads(resp.body)['groups'])
groups.update(g['name']
for g in json.loads(resp.body)['groups'])
marker = sublisting[-1]['name']
body = json.dumps(list({'name': g} for g in sorted(groups)))
body = json.dumps({'groups':
[{'name': g} for g in sorted(groups)]})
else:
path = quote('/v1/%s/%s/%s' % (self.auth_account, account, user))
resp = self.make_request(req.environ, 'GET',
@@ -695,7 +766,8 @@ class Swauth(object):
if reseller_admin:
groups.append('.reseller_admin')
resp = self.make_request(req.environ, 'PUT', path, json.dumps({'auth':
'plaintext:%s' % key, 'groups': groups})).get_response(self.app)
'plaintext:%s' % key,
'groups': [{'name': g} for g in groups]})).get_response(self.app)
if resp.status_int // 100 != 2:
raise Exception('Could not create user object: %s%s' %
(path, resp.status))
@@ -765,7 +837,7 @@ class Swauth(object):
X-Storage-Token set to the token to use with Swift and X-Storage-URL
set to the URL to the default Swift cluster to use.
The response body will be set to the account's clusters JSON object as
The response body will be set to the account's services JSON object as
described here::
{"storage": { # Represents the Swift storage service end points
@@ -878,12 +950,12 @@ class Swauth(object):
if resp.status_int // 100 != 2:
raise Exception('Could not save new token: %s%s' %
(path, resp.status))
# Get the cluster url information
path = quote('/v1/%s/%s/.clusters' % (self.auth_account, account))
# Get the services information
path = quote('/v1/%s/%s/.services' % (self.auth_account, account))
resp = self.make_request(req.environ, 'GET',
path).get_response(self.app)
if resp.status_int // 100 != 2:
raise Exception('Could not obtain clusters info: %s%s' %
raise Exception('Could not obtain services info: %s%s' %
(path, resp.status))
detail = json.loads(resp.body)
url = detail['storage'][detail['storage']['default']]
@@ -978,7 +1050,7 @@ class Swauth(object):
def get_itoken(self, env):
"""
Returns the current internal token to use for the auth system's own
actions with other Swift clusters. Each process will create its own
actions with other services. Each process will create its own
itoken and the token will be deleted and recreated based on the
token_life configuration value. The itoken information is stored in
memcache because the auth process that is asked by Swift to validate

View File

@@ -177,7 +177,8 @@ class TestAuth(unittest.TestCase):
('200 Ok', {},
json.dumps({'account': 'act', 'user': 'act:usr',
'account_id': 'AUTH_cfa',
'groups': ['act:usr', 'act', '.admin'],
'groups': [{'name': 'act:usr'}, {'name': 'act'},
{'name': '.admin'}],
'expires': time() + 60})),
('204 No Content', {}, '')]))
local_auth = auth.filter_factory({'super_admin_key': 'supertest',
@@ -219,7 +220,8 @@ class TestAuth(unittest.TestCase):
('200 Ok', {},
json.dumps({'account': 'act', 'user': 'act:usr',
'account_id': 'AUTH_cfa',
'groups': ['act:usr', 'act', '.admin'],
'groups': [{'name': 'act:usr'}, {'name': 'act'},
{'name': '.admin'}],
'expires': time() + 60})),
('204 No Content', {}, '')]))
resp = Request.blank('/v1/AUTH_cfa',
@@ -233,13 +235,15 @@ class TestAuth(unittest.TestCase):
('200 Ok', {},
json.dumps({'account': 'act', 'user': 'act:usr',
'account_id': 'AUTH_cfa',
'groups': ['act:usr', 'act', '.admin'],
'groups': [{'name': 'act:usr'}, {'name': 'act'},
{'name': '.admin'}],
'expires': time() + 60})),
('204 No Content', {}, ''),
('200 Ok', {},
json.dumps({'account': 'act', 'user': 'act:usr',
'account_id': 'AUTH_cfa',
'groups': ['act:usr', 'act', '.admin'],
'groups': [{'name': 'act:usr'}, {'name': 'act'},
{'name': '.admin'}],
'expires': time() + 60})),
('204 No Content', {}, '')]))
resp = Request.blank('/v1/AUTH_cfa',
@@ -254,7 +258,8 @@ class TestAuth(unittest.TestCase):
('200 Ok', {},
json.dumps({'account': 'act', 'user': 'act:usr',
'account_id': 'AUTH_cfa',
'groups': ['act:usr', 'act', '.admin'],
'groups': [{'name': 'act:usr'}, {'name': 'act'},
{'name': '.admin'}],
'expires': time() + 60})),
('204 No Content', {}, ''),
# Don't need a second token object returned if memcache is used
@@ -276,7 +281,8 @@ class TestAuth(unittest.TestCase):
('200 Ok', {},
json.dumps({'account': 'act', 'user': 'act:usr',
'account_id': 'AUTH_cfa',
'groups': ['act:usr', 'act', '.admin'],
'groups': [{'name': 'act:usr'}, {'name': 'act'},
{'name': '.admin'}],
'expires': time() - 1}))]))
resp = Request.blank('/v1/AUTH_cfa',
headers={'X-Auth-Token': 'AUTH_t'}).get_response(self.test_auth)
@@ -287,7 +293,8 @@ class TestAuth(unittest.TestCase):
('200 Ok', {},
json.dumps({'account': 'act', 'user': 'act:usr',
'account_id': 'AUTH_cfa',
'groups': ['act:usr', 'act', '.admin'],
'groups': [{'name': 'act:usr'}, {'name': 'act'},
{'name': '.admin'}],
'expires': time() + 60})),
('204 No Content', {}, '')]))
resp = Request.blank('/v1/AUTH_cfa',
@@ -421,14 +428,15 @@ class TestAuth(unittest.TestCase):
# GET of user object
('200 Ok', {},
json.dumps({"auth": "plaintext:key",
"groups": ["act:usr", "act", ".admin"]})),
"groups": [{'name': "act:usr"}, {'name': "act"},
{'name': ".admin"}]})),
# GET of account
('204 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
# PUT of new token
('201 Created', {}, ''),
# POST of token to user object
('204 No Content', {}, ''),
# GET of clusters object
# GET of services object
('200 Ok', {}, json.dumps({"storage": {"default": "local",
"local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
resp = Request.blank('/auth/v1.0',
@@ -472,7 +480,8 @@ class TestAuth(unittest.TestCase):
# GET of user object
('200 Ok', {},
json.dumps({"auth": "plaintext:key",
"groups": ["act:usr", "act", ".admin"]})),
"groups": [{'name': "act:usr"}, {'name': "act"},
{'name': ".admin"}]})),
# GET of account
('503 Service Unavailable', {}, '')]))
resp = Request.blank('/auth/v1.0',
@@ -485,7 +494,8 @@ class TestAuth(unittest.TestCase):
# GET of user object
('200 Ok', {},
json.dumps({"auth": "plaintext:key",
"groups": ["act:usr", "act", ".admin"]})),
"groups": [{'name': "act:usr"}, {'name': "act"},
{'name': ".admin"}]})),
# GET of account
('204 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
# PUT of new token
@@ -500,7 +510,8 @@ class TestAuth(unittest.TestCase):
# GET of user object
('200 Ok', {},
json.dumps({"auth": "plaintext:key",
"groups": ["act:usr", "act", ".admin"]})),
"groups": [{'name': "act:usr"}, {'name': "act"},
{'name': ".admin"}]})),
# GET of account
('204 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
# PUT of new token
@@ -512,19 +523,20 @@ class TestAuth(unittest.TestCase):
'X-Auth-Key': 'key'}).get_response(self.test_auth)
self.assertEquals(resp.status_int, 500)
def test_get_token_fail_get_clusters(self):
def test_get_token_fail_get_services(self):
self.test_auth.app = FakeApp(iter([
# GET of user object
('200 Ok', {},
json.dumps({"auth": "plaintext:key",
"groups": ["act:usr", "act", ".admin"]})),
"groups": [{'name': "act:usr"}, {'name': "act"},
{'name': ".admin"}]})),
# GET of account
('204 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
# PUT of new token
('201 Created', {}, ''),
# POST of token to user object
('204 No Content', {}, ''),
# GET of clusters object
# GET of services object
('503 Service Unavailable', {}, '')]))
resp = Request.blank('/auth/v1.0',
headers={'X-Auth-User': 'act:usr',
@@ -536,7 +548,8 @@ class TestAuth(unittest.TestCase):
# GET of user object
('200 Ok', {'X-Object-Meta-Auth-Token': 'AUTH_tktest'},
json.dumps({"auth": "plaintext:key",
"groups": ["act:usr", "act", ".admin"]})),
"groups": [{'name': "act:usr"}, {'name': "act"},
{'name': ".admin"}]})),
# GET of token
('503 Service Unavailable', {}, '')]))
resp = Request.blank('/auth/v1.0',
@@ -549,14 +562,15 @@ class TestAuth(unittest.TestCase):
# GET of user object
('200 Ok', {},
json.dumps({"auth": "plaintext:key",
"groups": ["act:usr", "act", ".admin"]})),
"groups": [{'name': "act:usr"}, {'name': "act"},
{'name': ".admin"}]})),
# GET of account
('204 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
# PUT of new token
('201 Created', {}, ''),
# POST of token to user object
('204 No Content', {}, ''),
# GET of clusters object
# GET of services object
('200 Ok', {}, json.dumps({"storage": {"default": "local",
"local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
resp = Request.blank('/auth/v1.0',
@@ -578,14 +592,15 @@ class TestAuth(unittest.TestCase):
# GET of user object
('200 Ok', {},
json.dumps({"auth": "plaintext:key",
"groups": ["act:usr", "act", ".admin"]})),
"groups": [{'name': "act:usr"}, {'name': "act"},
{'name': ".admin"}]})),
# GET of account
('204 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
# PUT of new token
('201 Created', {}, ''),
# POST of token to user object
('204 No Content', {}, ''),
# GET of clusters object
# GET of services object
('200 Ok', {}, json.dumps({"storage": {"default": "local",
"local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
resp = Request.blank('/auth/v1/act/auth',
@@ -607,14 +622,15 @@ class TestAuth(unittest.TestCase):
# GET of user object
('200 Ok', {},
json.dumps({"auth": "plaintext:key",
"groups": ["act:usr", "act", ".admin"]})),
"groups": [{'name': "act:usr"}, {'name': "act"},
{'name': ".admin"}]})),
# GET of account
('204 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
# PUT of new token
('201 Created', {}, ''),
# POST of token to user object
('204 No Content', {}, ''),
# GET of clusters object
# GET of services object
('200 Ok', {}, json.dumps({"storage": {"default": "local",
"local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
resp = Request.blank('/auth/v1.0',
@@ -636,14 +652,15 @@ class TestAuth(unittest.TestCase):
# GET of user object
('200 Ok', {},
json.dumps({"auth": "plaintext:key",
"groups": ["act:usr", "act", ".admin"]})),
"groups": [{'name': "act:usr"}, {'name': "act"},
{'name': ".admin"}]})),
# GET of account
('204 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
# PUT of new token
('201 Created', {}, ''),
# POST of token to user object
('204 No Content', {}, ''),
# GET of clusters object
# GET of services object
('200 Ok', {}, json.dumps({"storage": {"default": "local",
"local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
resp = Request.blank('/auth/v1/act/auth',
@@ -665,12 +682,14 @@ class TestAuth(unittest.TestCase):
# GET of user object
('200 Ok', {'X-Object-Meta-Auth-Token': 'AUTH_tktest'},
json.dumps({"auth": "plaintext:key",
"groups": ["act:usr", "act", ".admin"]})),
"groups": [{'name': "act:usr"}, {'name': "act"},
{'name': ".admin"}]})),
# GET of token
('200 Ok', {}, json.dumps({"account": "act", "user": "usr",
"account_id": "AUTH_cfa", "groups": ["act:usr", "key", ".admin"],
"account_id": "AUTH_cfa", "groups": [{'name': "act:usr"},
{'name': "key"}, {'name': ".admin"}],
"expires": 9999999999.9999999})),
# GET of clusters object
# GET of services object
('200 Ok', {}, json.dumps({"storage": {"default": "local",
"local": "http://127.0.0.1:8080/v1/AUTH_cfa"}}))]))
resp = Request.blank('/auth/v1.0',
Reference in New Issue
openstack/swift
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.

The note is not visible to the blocked user.