GSWauth authentication to be based on SWauth
We are planning on creating a GlusterFS aware authentication system for gluster-swift based on SWauth. We forked from SWauth commit 41d36ebe160aa3346f6f45197fff0c80f38fde58 Change-Id: Ia28730d21e04fc8d9ce0cb317fc04d0d97583fca Signed-off-by: Luis Pabon <lpabon@redhat.com> Reviewed-on: http://review.gluster.org/6069
This commit is contained in:
parent
46cd43fdf4
commit
ac1ac5bd21
3
gluster/swift/common/middleware/gswauth/.gitignore
vendored
Normal file
3
gluster/swift/common/middleware/gswauth/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*.egg-info
|
||||
*.py[co]
|
||||
.DS_Store
|
4
gluster/swift/common/middleware/gswauth/.unittests
Executable file
4
gluster/swift/common/middleware/gswauth/.unittests
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
nosetests test_swauth/unit --exe --with-coverage --cover-package swauth --cover-erase
|
||||
rm -f .coverage
|
39
gluster/swift/common/middleware/gswauth/AUTHORS
Normal file
39
gluster/swift/common/middleware/gswauth/AUTHORS
Normal file
@ -0,0 +1,39 @@
|
||||
Maintainer
|
||||
----------
|
||||
Greg Holt
|
||||
|
||||
Original Authors
|
||||
----------------
|
||||
Chuck Thier
|
||||
Greg Holt
|
||||
Greg Lange
|
||||
Jay Payne
|
||||
John Dickinson
|
||||
Michael Barton
|
||||
Will Reese
|
||||
|
||||
Contributors
|
||||
------------
|
||||
Andrew Clay Shafer
|
||||
Anne Gentle
|
||||
Brian K. Jones
|
||||
Caleb Tennis
|
||||
Chmouel Boudjnah
|
||||
Christian Schwede
|
||||
Chris Wedgwood
|
||||
Clay Gerrard
|
||||
Colin Nicholson
|
||||
Conrad Weidenkeller
|
||||
Cory Wright
|
||||
David Goetz
|
||||
Ed Leafe
|
||||
Fujita Tomonori
|
||||
Kapil Thangavelu
|
||||
Monty Taylor
|
||||
Pablo Llopis
|
||||
Paul Jimenez
|
||||
Pete Zaitcev
|
||||
Russ Nelson
|
||||
Scott Simpson
|
||||
Soren Hansen
|
||||
Stephen Milton
|
62
gluster/swift/common/middleware/gswauth/CHANGELOG
Normal file
62
gluster/swift/common/middleware/gswauth/CHANGELOG
Normal file
@ -0,0 +1,62 @@
|
||||
swauth (1.0.8)
|
||||
|
||||
Added request.environ[reseller_request] = True if request is coming from an
|
||||
user in .reseller_admin group
|
||||
|
||||
Fixed to work with newer Swift versions whose memcache clients require a
|
||||
time keyword argument when the older versions required a timeout keyword
|
||||
argument.
|
||||
|
||||
swauth (1.0.7)
|
||||
|
||||
New X-Auth-Token-Lifetime header a user can set to how long they'd like
|
||||
their token to be good for.
|
||||
|
||||
New max_token_life config value for capping the above.
|
||||
|
||||
New X-Auth-Token-Expires header returned with the get token request.
|
||||
|
||||
Switchover to swift.common.swob instead of WebOb; requires Swift >= 1.7.6
|
||||
now.
|
||||
|
||||
swauth (1.0.6)
|
||||
|
||||
Apparently I haven't been keeping up with this CHANGELOG. I'll try to be
|
||||
better onward.
|
||||
|
||||
This release added passing OPTIONS requests through untouched, needed for
|
||||
CORS support in Swift.
|
||||
|
||||
Also, Swauth is a bit more restrictive in deciding when it's the definitive
|
||||
auth for a request.
|
||||
|
||||
swauth (1.0.3-dev)
|
||||
|
||||
This release is still under development. A full change log will be made at
|
||||
release. Until then, you can see what has changed with:
|
||||
|
||||
git log 1.0.2..HEAD
|
||||
|
||||
swauth (1.0.2)
|
||||
|
||||
Fixed bug rejecting requests when using multiple instances of Swauth or
|
||||
Swauth with other auth services.
|
||||
|
||||
Fixed bug interpreting URL-encoded user names and keys.
|
||||
|
||||
Added support for the Swift container sync feature.
|
||||
|
||||
Allowed /not/ setting super_admin_key to disable Swauth administration
|
||||
features.
|
||||
|
||||
Added swauth_remote mode so the Swauth middleware for one Swift cluster
|
||||
could be pointing to the Swauth service on another Swift cluster, sharing
|
||||
account/user data sets.
|
||||
|
||||
Added ability to purge stored tokens.
|
||||
|
||||
Added API documentation for internal Swauth API.
|
||||
|
||||
swauth (1.0.1)
|
||||
|
||||
Initial release after separation from Swift.
|
202
gluster/swift/common/middleware/gswauth/LICENSE
Normal file
202
gluster/swift/common/middleware/gswauth/LICENSE
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
4
gluster/swift/common/middleware/gswauth/MANIFEST.in
Normal file
4
gluster/swift/common/middleware/gswauth/MANIFEST.in
Normal file
@ -0,0 +1,4 @@
|
||||
include AUTHORS LICENSE README.md .unittests test_swauth/__init__.py
|
||||
include CHANGELOG
|
||||
graft doc
|
||||
graft etc
|
71
gluster/swift/common/middleware/gswauth/README.md
Normal file
71
gluster/swift/common/middleware/gswauth/README.md
Normal file
@ -0,0 +1,71 @@
|
||||
Swauth
|
||||
------
|
||||
|
||||
An Auth Service for Swift as WSGI Middleware that uses Swift itself as a
|
||||
backing store. Sphinx-built docs at: <http://gholt.github.com/swauth/>
|
||||
|
||||
See also <https://github.com/openstack/keystone> for the standard OpenStack
|
||||
auth service.
|
||||
|
||||
|
||||
NOTE
|
||||
----
|
||||
|
||||
**Be sure to review the Sphinx-built docs at:
|
||||
<http://gholt.github.com/swauth/>**
|
||||
|
||||
|
||||
Quick Install
|
||||
-------------
|
||||
|
||||
1) Install Swauth with ``sudo python setup.py install`` or ``sudo python
|
||||
setup.py develop`` or via whatever packaging system you may be using.
|
||||
|
||||
2) Alter your proxy-server.conf pipeline to have swauth instead of tempauth:
|
||||
|
||||
Was:
|
||||
|
||||
[pipeline:main]
|
||||
pipeline = catch_errors cache tempauth proxy-server
|
||||
|
||||
Change To:
|
||||
|
||||
[pipeline:main]
|
||||
pipeline = catch_errors cache swauth proxy-server
|
||||
|
||||
3) Add to your proxy-server.conf the section for the Swauth WSGI filter:
|
||||
|
||||
[filter:swauth]
|
||||
use = egg:swauth#swauth
|
||||
set log_name = swauth
|
||||
super_admin_key = swauthkey
|
||||
|
||||
4) Be sure your proxy server allows account management:
|
||||
|
||||
[app:proxy-server]
|
||||
...
|
||||
allow_account_management = true
|
||||
|
||||
5) Restart your proxy server ``swift-init proxy reload``
|
||||
|
||||
6) Initialize the Swauth backing store in Swift ``swauth-prep -K swauthkey``
|
||||
|
||||
7) Add an account/user ``swauth-add-user -A http://127.0.0.1:8080/auth/ -K
|
||||
swauthkey -a test tester testing``
|
||||
|
||||
8) Ensure it works ``swift -A http://127.0.0.1:8080/auth/v1.0 -U test:tester -K
|
||||
testing stat -v``
|
||||
|
||||
|
||||
Web Admin Install
|
||||
-----------------
|
||||
|
||||
1) If you installed from packages, you'll need to cd to the webadmin directory
|
||||
the package installed. This is ``/usr/share/doc/python-swauth/webadmin``
|
||||
with the Lucid packages. If you installed from source, you'll need to cd to
|
||||
the webadmin directory in the source directory.
|
||||
|
||||
2) Upload the Web Admin files with ``swift -A http://127.0.0.1:8080/auth/v1.0
|
||||
-U .super_admin:.super_admin -K swauthkey upload .webadmin .``
|
||||
|
||||
3) Open ``http://127.0.0.1:8080/auth/`` in your browser.
|
2
gluster/swift/common/middleware/gswauth/babel.cfg
Normal file
2
gluster/swift/common/middleware/gswauth/babel.cfg
Normal file
@ -0,0 +1,2 @@
|
||||
[python: **.py]
|
||||
|
69
gluster/swift/common/middleware/gswauth/bin/swauth-add-account
Executable file
69
gluster/swift/common/middleware/gswauth/bin/swauth-add-account
Executable file
@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import gettext
|
||||
from optparse import OptionParser
|
||||
from os.path import basename
|
||||
from sys import argv, exit
|
||||
|
||||
from swift.common.bufferedhttp import http_connect_raw as http_connect
|
||||
from swift.common.utils import urlparse
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
gettext.install('swauth', unicode=1)
|
||||
parser = OptionParser(usage='Usage: %prog [options] <account>')
|
||||
parser.add_option('-s', '--suffix', dest='suffix',
|
||||
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/)')
|
||||
parser.add_option('-U', '--admin-user', dest='admin_user',
|
||||
default='.super_admin', help='The user with admin rights to add users '
|
||||
'(default: .super_admin).')
|
||||
parser.add_option('-K', '--admin-key', dest='admin_key',
|
||||
help='The key for the user with admin rights to add users.')
|
||||
args = argv[1:]
|
||||
if not args:
|
||||
args.append('-h')
|
||||
(options, args) = parser.parse_args(args)
|
||||
if len(args) != 1:
|
||||
parser.parse_args(['-h'])
|
||||
account = args[0]
|
||||
parsed = urlparse(options.admin_url)
|
||||
if parsed.scheme not in ('http', 'https'):
|
||||
raise Exception('Cannot handle protocol scheme %s for url %s' %
|
||||
(parsed.scheme, repr(options.admin_url)))
|
||||
parsed_path = parsed.path
|
||||
if not parsed_path:
|
||||
parsed_path = '/'
|
||||
elif parsed_path[-1] != '/':
|
||||
parsed_path += '/'
|
||||
path = '%sv2/%s' % (parsed_path, account)
|
||||
headers = {'X-Auth-Admin-User': options.admin_user,
|
||||
'X-Auth-Admin-Key': options.admin_key,
|
||||
'Content-Length': '0'}
|
||||
if options.suffix:
|
||||
headers['X-Account-Suffix'] = options.suffix
|
||||
conn = http_connect(parsed.hostname, parsed.port, 'PUT', path, headers,
|
||||
ssl=(parsed.scheme == 'https'))
|
||||
resp = conn.getresponse()
|
||||
if resp.status // 100 != 2:
|
||||
exit('Account creation failed: %s %s' % (resp.status, resp.reason))
|
99
gluster/swift/common/middleware/gswauth/bin/swauth-add-user
Executable file
99
gluster/swift/common/middleware/gswauth/bin/swauth-add-user
Executable file
@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import gettext
|
||||
from optparse import OptionParser
|
||||
from os.path import basename
|
||||
from sys import argv, exit
|
||||
|
||||
from swift.common.bufferedhttp import http_connect_raw as http_connect
|
||||
from swift.common.utils import urlparse
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
gettext.install('swauth', unicode=1)
|
||||
parser = OptionParser(
|
||||
usage='Usage: %prog [options] <account> <user> <password>')
|
||||
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 containers specifically allowed '
|
||||
'with ACLs.')
|
||||
parser.add_option('-r', '--reseller-admin', dest='reseller_admin',
|
||||
action='store_true', default=False, help='Give the user full reseller '
|
||||
'administrator access, giving them full access to all accounts within '
|
||||
'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 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/')
|
||||
parser.add_option('-U', '--admin-user', dest='admin_user',
|
||||
default='.super_admin', help='The user with admin rights to add users '
|
||||
'(default: .super_admin).')
|
||||
parser.add_option('-K', '--admin-key', dest='admin_key',
|
||||
help='The key for the user with admin rights to add users.')
|
||||
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
|
||||
parsed = urlparse(options.admin_url)
|
||||
if parsed.scheme not in ('http', 'https'):
|
||||
raise Exception('Cannot handle protocol scheme %s for url %s' %
|
||||
(parsed.scheme, repr(options.admin_url)))
|
||||
parsed_path = parsed.path
|
||||
if not parsed_path:
|
||||
parsed_path = '/'
|
||||
elif parsed_path[-1] != '/':
|
||||
parsed_path += '/'
|
||||
# Ensure the account exists
|
||||
path = '%sv2/%s' % (parsed_path, account)
|
||||
headers = {'X-Auth-Admin-User': options.admin_user,
|
||||
'X-Auth-Admin-Key': options.admin_key}
|
||||
if options.suffix:
|
||||
headers['X-Account-Suffix'] = options.suffix
|
||||
conn = http_connect(parsed.hostname, parsed.port, 'GET', path, headers,
|
||||
ssl=(parsed.scheme == 'https'))
|
||||
resp = conn.getresponse()
|
||||
if resp.status // 100 != 2:
|
||||
headers['Content-Length'] = '0'
|
||||
conn = http_connect(parsed.hostname, parsed.port, 'PUT', path, headers,
|
||||
ssl=(parsed.scheme == 'https'))
|
||||
resp = conn.getresponse()
|
||||
if resp.status // 100 != 2:
|
||||
print 'Account creation failed: %s %s' % (resp.status, resp.reason)
|
||||
# Add the user
|
||||
path = '%sv2/%s/%s' % (parsed_path, account, user)
|
||||
headers = {'X-Auth-Admin-User': options.admin_user,
|
||||
'X-Auth-Admin-Key': options.admin_key,
|
||||
'X-Auth-User-Key': password,
|
||||
'Content-Length': '0'}
|
||||
if options.admin:
|
||||
headers['X-Auth-User-Admin'] = 'true'
|
||||
if options.reseller_admin:
|
||||
headers['X-Auth-User-Reseller-Admin'] = 'true'
|
||||
conn = http_connect(parsed.hostname, parsed.port, 'PUT', path, headers,
|
||||
ssl=(parsed.scheme == 'https'))
|
||||
resp = conn.getresponse()
|
||||
if resp.status // 100 != 2:
|
||||
exit('User creation failed: %s %s' % (resp.status, resp.reason))
|
165
gluster/swift/common/middleware/gswauth/bin/swauth-cleanup-tokens
Executable file
165
gluster/swift/common/middleware/gswauth/bin/swauth-cleanup-tokens
Executable file
@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
import gettext
|
||||
import re
|
||||
from datetime import datetime, timedelta
|
||||
from optparse import OptionParser
|
||||
from sys import argv, exit
|
||||
from time import sleep, time
|
||||
|
||||
from swiftclient.client import Connection, ClientException
|
||||
|
||||
if __name__ == '__main__':
|
||||
gettext.install('swauth', unicode=1)
|
||||
parser = OptionParser(usage='Usage: %prog [options]')
|
||||
parser.add_option('-t', '--token-life', dest='token_life',
|
||||
default='86400', help='The expected life of tokens; token objects '
|
||||
'modified more than this number of seconds ago will be checked for '
|
||||
'expiration (default: 86400).')
|
||||
parser.add_option('-s', '--sleep', dest='sleep',
|
||||
default='0.1', help='The number of seconds to sleep between token '
|
||||
'checks (default: 0.1)')
|
||||
parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
|
||||
default=False, help='Outputs everything done instead of just the '
|
||||
'deletions.')
|
||||
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/)')
|
||||
parser.add_option('-K', '--admin-key', dest='admin_key',
|
||||
help='The key for .super_admin.')
|
||||
parser.add_option('', '--purge', dest='purge_account', help='Purges all '
|
||||
'tokens for a given account whether the tokens have expired or not.')
|
||||
parser.add_option('', '--purge-all', dest='purge_all', action='store_true',
|
||||
default=False, help='Purges all tokens for all accounts and users '
|
||||
'whether the tokens have expired or not.')
|
||||
args = argv[1:]
|
||||
if not args:
|
||||
args.append('-h')
|
||||
(options, args) = parser.parse_args(args)
|
||||
if len(args) != 0:
|
||||
parser.parse_args(['-h'])
|
||||
options.admin_url = options.admin_url.rstrip('/')
|
||||
if not options.admin_url.endswith('/v1.0'):
|
||||
options.admin_url += '/v1.0'
|
||||
options.admin_user = '.super_admin:.super_admin'
|
||||
options.token_life = timedelta(0, float(options.token_life))
|
||||
options.sleep = float(options.sleep)
|
||||
conn = Connection(options.admin_url, options.admin_user, options.admin_key)
|
||||
if options.purge_account:
|
||||
marker = None
|
||||
while True:
|
||||
if options.verbose:
|
||||
print 'GET %s?marker=%s' % (options.purge_account, marker)
|
||||
objs = conn.get_container(options.purge_account, marker=marker)[1]
|
||||
if objs:
|
||||
marker = objs[-1]['name']
|
||||
else:
|
||||
if options.verbose:
|
||||
print 'No more objects in %s' % options.purge_account
|
||||
break
|
||||
for obj in objs:
|
||||
if options.verbose:
|
||||
print 'HEAD %s/%s' % (options.purge_account, obj['name'])
|
||||
headers = conn.head_object(options.purge_account, obj['name'])
|
||||
if 'x-object-meta-auth-token' in headers:
|
||||
token = headers['x-object-meta-auth-token']
|
||||
container = '.token_%s' % token[-1]
|
||||
if options.verbose:
|
||||
print '%s/%s purge account %r; deleting' % \
|
||||
(container, token, options.purge_account)
|
||||
print 'DELETE %s/%s' % (container, token)
|
||||
try:
|
||||
conn.delete_object(container, token)
|
||||
except ClientException, err:
|
||||
if err.http_status != 404:
|
||||
raise
|
||||
continue
|
||||
if options.verbose:
|
||||
print 'Done.'
|
||||
exit(0)
|
||||
for x in xrange(16):
|
||||
container = '.token_%x' % x
|
||||
marker = None
|
||||
while True:
|
||||
if options.verbose:
|
||||
print 'GET %s?marker=%s' % (container, marker)
|
||||
try:
|
||||
objs = conn.get_container(container, marker=marker)[1]
|
||||
except ClientException, e:
|
||||
if e.http_status == 404:
|
||||
exit('Container %s not found. swauth-prep needs to be '
|
||||
'rerun' % (container))
|
||||
else:
|
||||
exit('Object listing on container %s failed with status '
|
||||
'code %d' % (container, e.http_status))
|
||||
if objs:
|
||||
marker = objs[-1]['name']
|
||||
else:
|
||||
if options.verbose:
|
||||
print 'No more objects in %s' % container
|
||||
break
|
||||
for obj in objs:
|
||||
if options.purge_all:
|
||||
if options.verbose:
|
||||
print '%s/%s purge all; deleting' % \
|
||||
(container, obj['name'])
|
||||
print 'DELETE %s/%s' % (container, obj['name'])
|
||||
try:
|
||||
conn.delete_object(container, obj['name'])
|
||||
except ClientException, err:
|
||||
if err.http_status != 404:
|
||||
raise
|
||||
continue
|
||||
last_modified = datetime(*map(int, re.split('[^\d]',
|
||||
obj['last_modified'])[:-1]))
|
||||
ago = datetime.utcnow() - last_modified
|
||||
if ago > options.token_life:
|
||||
if options.verbose:
|
||||
print '%s/%s last modified %ss ago; investigating' % \
|
||||
(container, obj['name'],
|
||||
ago.days * 86400 + ago.seconds)
|
||||
print 'GET %s/%s' % (container, obj['name'])
|
||||
detail = conn.get_object(container, obj['name'])[1]
|
||||
detail = json.loads(detail)
|
||||
if detail['expires'] < time():
|
||||
if options.verbose:
|
||||
print '%s/%s expired %ds ago; deleting' % \
|
||||
(container, obj['name'],
|
||||
time() - detail['expires'])
|
||||
print 'DELETE %s/%s' % (container, obj['name'])
|
||||
try:
|
||||
conn.delete_object(container, obj['name'])
|
||||
except ClientException, e:
|
||||
if e.http_status != 404:
|
||||
print 'DELETE of %s/%s failed with status ' \
|
||||
'code %d' % (container, obj['name'],
|
||||
e.http_status)
|
||||
elif options.verbose:
|
||||
print "%s/%s won't expire for %ds; skipping" % \
|
||||
(container, obj['name'],
|
||||
detail['expires'] - time())
|
||||
elif options.verbose:
|
||||
print '%s/%s last modified %ss ago; skipping' % \
|
||||
(container, obj['name'],
|
||||
ago.days * 86400 + ago.seconds)
|
||||
sleep(options.sleep)
|
||||
if options.verbose:
|
||||
print 'Done.'
|
60
gluster/swift/common/middleware/gswauth/bin/swauth-delete-account
Executable file
60
gluster/swift/common/middleware/gswauth/bin/swauth-delete-account
Executable file
@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import gettext
|
||||
from optparse import OptionParser
|
||||
from os.path import basename
|
||||
from sys import argv, exit
|
||||
|
||||
from swift.common.bufferedhttp import http_connect_raw as http_connect
|
||||
from swift.common.utils import urlparse
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
gettext.install('swauth', unicode=1)
|
||||
parser = OptionParser(usage='Usage: %prog [options] <account>')
|
||||
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/')
|
||||
parser.add_option('-U', '--admin-user', dest='admin_user',
|
||||
default='.super_admin', help='The user with admin rights to add users '
|
||||
'(default: .super_admin).')
|
||||
parser.add_option('-K', '--admin-key', dest='admin_key',
|
||||
help='The key for the user with admin rights to add users.')
|
||||
args = argv[1:]
|
||||
if not args:
|
||||
args.append('-h')
|
||||
(options, args) = parser.parse_args(args)
|
||||
if len(args) != 1:
|
||||
parser.parse_args(['-h'])
|
||||
account = args[0]
|
||||
parsed = urlparse(options.admin_url)
|
||||
if parsed.scheme not in ('http', 'https'):
|
||||
raise Exception('Cannot handle protocol scheme %s for url %s' %
|
||||
(parsed.scheme, repr(options.admin_url)))
|
||||
parsed_path = parsed.path
|
||||
if not parsed_path:
|
||||
parsed_path = '/'
|
||||
elif parsed_path[-1] != '/':
|
||||
parsed_path += '/'
|
||||
path = '%sv2/%s' % (parsed_path, account)
|
||||
headers = {'X-Auth-Admin-User': options.admin_user,
|
||||
'X-Auth-Admin-Key': options.admin_key}
|
||||
conn = http_connect(parsed.hostname, parsed.port, 'DELETE', path, headers,
|
||||
ssl=(parsed.scheme == 'https'))
|
||||
resp = conn.getresponse()
|
||||
if resp.status // 100 != 2:
|
||||
exit('Account deletion failed: %s %s' % (resp.status, resp.reason))
|
60
gluster/swift/common/middleware/gswauth/bin/swauth-delete-user
Executable file
60
gluster/swift/common/middleware/gswauth/bin/swauth-delete-user
Executable file
@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import gettext
|
||||
from optparse import OptionParser
|
||||
from os.path import basename
|
||||
from sys import argv, exit
|
||||
|
||||
from swift.common.bufferedhttp import http_connect_raw as http_connect
|
||||
from swift.common.utils import urlparse
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
gettext.install('swauth', unicode=1)
|
||||
parser = OptionParser(usage='Usage: %prog [options] <account> <user>')
|
||||
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/')
|
||||
parser.add_option('-U', '--admin-user', dest='admin_user',
|
||||
default='.super_admin', help='The user with admin rights to add users '
|
||||
'(default: .super_admin).')
|
||||
parser.add_option('-K', '--admin-key', dest='admin_key',
|
||||
help='The key for the user with admin rights to add users.')
|
||||
args = argv[1:]
|
||||
if not args:
|
||||
args.append('-h')
|
||||
(options, args) = parser.parse_args(args)
|
||||
if len(args) != 2:
|
||||
parser.parse_args(['-h'])
|
||||
account, user = args
|
||||
parsed = urlparse(options.admin_url)
|
||||
if parsed.scheme not in ('http', 'https'):
|
||||
raise Exception('Cannot handle protocol scheme %s for url %s' %
|
||||
(parsed.scheme, repr(options.admin_url)))
|
||||
parsed_path = parsed.path
|
||||
if not parsed_path:
|
||||
parsed_path = '/'
|
||||
elif parsed_path[-1] != '/':
|
||||
parsed_path += '/'
|
||||
path = '%sv2/%s/%s' % (parsed_path, account, user)
|
||||
headers = {'X-Auth-Admin-User': options.admin_user,
|
||||
'X-Auth-Admin-Key': options.admin_key}
|
||||
conn = http_connect(parsed.hostname, parsed.port, 'DELETE', path, headers,
|
||||
ssl=(parsed.scheme == 'https'))
|
||||
resp = conn.getresponse()
|
||||
if resp.status // 100 != 2:
|
||||
exit('User deletion failed: %s %s' % (resp.status, resp.reason))
|
86
gluster/swift/common/middleware/gswauth/bin/swauth-list
Executable file
86
gluster/swift/common/middleware/gswauth/bin/swauth-list
Executable file
@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
import gettext
|
||||
from optparse import OptionParser
|
||||
from os.path import basename
|
||||
from sys import argv, exit
|
||||
|
||||
from swift.common.bufferedhttp import http_connect_raw as http_connect
|
||||
from swift.common.utils import urlparse
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
gettext.install('swauth', unicode=1)
|
||||
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/')
|
||||
parser.add_option('-U', '--admin-user', dest='admin_user',
|
||||
default='.super_admin', help='The user with admin rights to add users '
|
||||
'(default: .super_admin).')
|
||||
parser.add_option('-K', '--admin-key', dest='admin_key',
|
||||
help='The key for the user with admin rights to add users.')
|
||||
args = argv[1:]
|
||||
if not args:
|
||||
args.append('-h')
|
||||
(options, args) = parser.parse_args(args)
|
||||
if len(args) > 2:
|
||||
parser.parse_args(['-h'])
|
||||
parsed = urlparse(options.admin_url)
|
||||
if parsed.scheme not in ('http', 'https'):
|
||||
raise Exception('Cannot handle protocol scheme %s for url %s' %
|
||||
(parsed.scheme, repr(options.admin_url)))
|
||||
parsed_path = parsed.path
|
||||
if not parsed_path:
|
||||
parsed_path = '/'
|
||||
elif parsed_path[-1] != '/':
|
||||
parsed_path += '/'
|
||||
path = '%sv2/%s' % (parsed_path, '/'.join(args))
|
||||
headers = {'X-Auth-Admin-User': options.admin_user,
|
||||
'X-Auth-Admin-Key': options.admin_key}
|
||||
conn = http_connect(parsed.hostname, parsed.port, 'GET', path, headers,
|
||||
ssl=(parsed.scheme == 'https'))
|
||||
resp = conn.getresponse()
|
||||
body = resp.read()
|
||||
if resp.status // 100 != 2:
|
||||
exit('List failed: %s %s' % (resp.status, resp.reason))
|
||||
if options.plain_text:
|
||||
info = json.loads(body)
|
||||
for group in info[['accounts', 'users', 'groups'][len(args)]]:
|
||||
print group['name']
|
||||
else:
|
||||
print body
|
59
gluster/swift/common/middleware/gswauth/bin/swauth-prep
Executable file
59
gluster/swift/common/middleware/gswauth/bin/swauth-prep
Executable file
@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import gettext
|
||||
from optparse import OptionParser
|
||||
from os.path import basename
|
||||
from sys import argv, exit
|
||||
|
||||
from swift.common.bufferedhttp import http_connect_raw as http_connect
|
||||
from swift.common.utils import urlparse
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
gettext.install('swauth', unicode=1)
|
||||
parser = OptionParser(usage='Usage: %prog [options]')
|
||||
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/')
|
||||
parser.add_option('-U', '--admin-user', dest='admin_user',
|
||||
default='.super_admin', help='The user with admin rights to add users '
|
||||
'(default: .super_admin).')
|
||||
parser.add_option('-K', '--admin-key', dest='admin_key',
|
||||
help='The key for the user with admin rights to add users.')
|
||||
args = argv[1:]
|
||||
if not args:
|
||||
args.append('-h')
|
||||
(options, args) = parser.parse_args(args)
|
||||
if args:
|
||||
parser.parse_args(['-h'])
|
||||
parsed = urlparse(options.admin_url)
|
||||
if parsed.scheme not in ('http', 'https'):
|
||||
raise Exception('Cannot handle protocol scheme %s for url %s' %
|
||||
(parsed.scheme, repr(options.admin_url)))
|
||||
parsed_path = parsed.path
|
||||
if not parsed_path:
|
||||
parsed_path = '/'
|
||||
elif parsed_path[-1] != '/':
|
||||
parsed_path += '/'
|
||||
path = '%sv2/.prep' % parsed_path
|
||||
headers = {'X-Auth-Admin-User': options.admin_user,
|
||||
'X-Auth-Admin-Key': options.admin_key}
|
||||
conn = http_connect(parsed.hostname, parsed.port, 'POST', path, headers,
|
||||
ssl=(parsed.scheme == 'https'))
|
||||
resp = conn.getresponse()
|
||||
if resp.status // 100 != 2:
|
||||
exit('Auth subsystem prep failed: %s %s' % (resp.status, resp.reason))
|
73
gluster/swift/common/middleware/gswauth/bin/swauth-set-account-service
Executable file
73
gluster/swift/common/middleware/gswauth/bin/swauth-set-account-service
Executable file
@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
import gettext
|
||||
from optparse import OptionParser
|
||||
from os.path import basename
|
||||
from sys import argv, exit
|
||||
|
||||
from swift.common.bufferedhttp import http_connect_raw as http_connect
|
||||
from swift.common.utils import urlparse
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
gettext.install('swauth', unicode=1)
|
||||
parser = OptionParser(usage='''
|
||||
Usage: %prog [options] <account> <service> <name> <value>
|
||||
|
||||
Sets a service URL for an account. Can only be set by a reseller admin.
|
||||
|
||||
Example: %prog -K swauthkey test storage local http://127.0.0.1:8080/v1/AUTH_018c3946-23f8-4efb-a8fb-b67aae8e4162
|
||||
'''.strip())
|
||||
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/)')
|
||||
parser.add_option('-U', '--admin-user', dest='admin_user',
|
||||
default='.super_admin', help='The user with admin rights to add users '
|
||||
'(default: .super_admin).')
|
||||
parser.add_option('-K', '--admin-key', dest='admin_key',
|
||||
help='The key for the user with admin rights to add users.')
|
||||
args = argv[1:]
|
||||
if not args:
|
||||
args.append('-h')
|
||||
(options, args) = parser.parse_args(args)
|
||||
if len(args) != 4:
|
||||
parser.parse_args(['-h'])
|
||||
account, service, name, url = args
|
||||
parsed = urlparse(options.admin_url)
|
||||
if parsed.scheme not in ('http', 'https'):
|
||||
raise Exception('Cannot handle protocol scheme %s for url %s' %
|
||||
(parsed.scheme, repr(options.admin_url)))
|
||||
parsed_path = parsed.path
|
||||
if not parsed_path:
|
||||
parsed_path = '/'
|
||||
elif parsed_path[-1] != '/':
|
||||
parsed_path += '/'
|
||||
path = '%sv2/%s/.services' % (parsed_path, account)
|
||||
body = json.dumps({service: {name: url}})
|
||||
headers = {'Content-Length': str(len(body)),
|
||||
'X-Auth-Admin-User': options.admin_user,
|
||||
'X-Auth-Admin-Key': options.admin_key}
|
||||
conn = http_connect(parsed.hostname, parsed.port, 'POST', path, headers,
|
||||
ssl=(parsed.scheme == 'https'))
|
||||
conn.send(body)
|
||||
resp = conn.getresponse()
|
||||
if resp.status // 100 != 2:
|
||||
exit('Service set failed: %s %s' % (resp.status, resp.reason))
|
466
gluster/swift/common/middleware/gswauth/doc/source/api.rst
Normal file
466
gluster/swift/common/middleware/gswauth/doc/source/api.rst
Normal file
@ -0,0 +1,466 @@
|
||||
.. _api_top:
|
||||
|
||||
----------
|
||||
Swauth API
|
||||
----------
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
Swauth has its own internal versioned REST API for adding, removing,
|
||||
and editing accounts. This document explains the v2 API.
|
||||
|
||||
Authentication
|
||||
--------------
|
||||
|
||||
Each REST request against the swauth API requires the inclusion of a
|
||||
specific authorization user and key to be passed in a specific HTTP
|
||||
header. These headers are defined as ``X-Auth-Admin-User`` and
|
||||
``X-Auth-Admin-Key``.
|
||||
|
||||
Typically, these values are ``.super_admin`` (the site super admin
|
||||
user) with the key being specified in the swauth middleware
|
||||
configuration as ``super_admin_key``.
|
||||
|
||||
This could also be a reseller admin with the appropriate rights to
|
||||
perform actions on reseller accounts.
|
||||
|
||||
Endpoints
|
||||
---------
|
||||
|
||||
The swauth API endpoint is presented on the proxy servers, in the
|
||||
"/auth" namespace. In addition, the API is versioned, and the version
|
||||
documented is version 2. API versions subdivide the auth namespace by
|
||||
version, specified as a version identifier like "v2".
|
||||
|
||||
The auth endpoint described herein is therefore located at "/auth/v2/"
|
||||
as presented by the proxy servers.
|
||||
|
||||
Bear in mind that in order for the auth management API to be
|
||||
presented, it must be enabled in the proxy server config by setting
|
||||
``allow_account_managment`` to ``true`` in the ``[app:proxy-server]``
|
||||
stanza of your proxy-server.conf.
|
||||
|
||||
Responses
|
||||
---------
|
||||
|
||||
Responses from the auth APIs are returned as a JSON structure.
|
||||
Example return values in this document are edited for readability.
|
||||
|
||||
|
||||
Reseller/Admin Services
|
||||
=======================
|
||||
|
||||
Operations can be performed against the endpoint itself to perform
|
||||
general administrative operations. Currently, the only operations
|
||||
that can be performed is a GET operation to get reseller or site admin
|
||||
information.
|
||||
|
||||
Get Admin Info
|
||||
--------------
|
||||
|
||||
A GET request at the swauth endpoint will return reseller information
|
||||
for the account specified in the ``X-Auth-Admin-User`` header.
|
||||
Currently, the information returned is limited to a list of accounts
|
||||
for the reseller or site admin.
|
||||
|
||||
Valid return codes:
|
||||
* 200: Success
|
||||
* 403: Invalid X-Auth-Admin-User/X-Auth-Admin-Key
|
||||
* 5xx: Internal error
|
||||
|
||||
Example Request::
|
||||
|
||||
GET /auth/<api version>/ HTTP/1.1
|
||||
X-Auth-Admin-User: .super_admin
|
||||
X-Auth-Admin-Key: swauthkey
|
||||
|
||||
Example Curl Request::
|
||||
|
||||
curl -D - https://<endpoint>/auth/v2/ \
|
||||
-H "X-Auth-Admin-User: .super_admin" \
|
||||
-H "X-Auth-Admin-Key: swauthkey"
|
||||
|
||||
Example Result::
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
|
||||
{ "accounts":
|
||||
[
|
||||
{ "name": "account1" },
|
||||
{ "name": "account2" },
|
||||
{ "name": "account3" }
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
Account Services
|
||||
================
|
||||
|
||||
There are API request to get account details, create, and delete
|
||||
accounts, mapping logically to the REST verbs GET, PUT, and DELETE.
|
||||
These actions are performed against an account URI, in the following
|
||||
general request structure::
|
||||
|
||||
METHOD /auth/<version>/<account> HTTP/1.1
|
||||
|
||||
The methods that can be used are detailed below.
|
||||
|
||||
Get Account Details
|
||||
-------------------
|
||||
|
||||
Account details can be retrieved by performing a GET request against
|
||||
an account URI. 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 that represents valid storage cluster
|
||||
endpoints, and which endpoint is the default. The 'users' value is a
|
||||
list of dicts, each dict representing a user and currently only
|
||||
containing the single key 'name'.
|
||||
|
||||
Valid Responses:
|
||||
* 200: Success
|
||||
* 403: Invalid X-Auth-Admin-User/X-Auth-Admin-Key
|
||||
* 5xx: Internal error
|
||||
|
||||
Example Request::
|
||||
|
||||
GET /auth/<api version>/<account> HTTP/1.1
|
||||
X-Auth-Admin-User: .super_admin
|
||||
X-Auth-Admin-Key: swauthkey
|
||||
|
||||
Example Curl Request::
|
||||
|
||||
curl -D - https://<endpoint>/auth/v2/<account> \
|
||||
-H "X-Auth-Admin-User: .super_admin" \
|
||||
-H "X-Auth-Admin-Key: swauthkey"
|
||||
|
||||
Example Response::
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
|
||||
{ "services":
|
||||
{ "storage":
|
||||
{ "default": "local",
|
||||
"local": "https://<storage endpoint>/v1/<account_id>" },
|
||||
},
|
||||
"account_id": "<account_id>",
|
||||
"users": [ { "name": "user1" },
|
||||
{ "name": "user2" } ]
|
||||
}
|
||||
|
||||
Create Account
|
||||
--------------
|
||||
|
||||
An account can be created with a PUT request against a non-existent
|
||||
account. By default, a newly created UUID4 will be used with the
|
||||
reseller prefix as the account ID used when creating corresponding
|
||||
service accounts. However, you can provide an X-Account-Suffix header
|
||||
to replace the UUDI4 part.
|
||||
|
||||
Valid return codes:
|
||||
* 200: Success
|
||||
* 403: Invalid X-Auth-Admin-User/X-Auth-Admin-Key
|
||||
* 5xx: Internal error
|
||||
|
||||
Example Request::
|
||||
|
||||
GET /auth/<api version>/<new_account> HTTP/1.1
|
||||
X-Auth-Admin-User: .super_admin
|
||||
X-Auth-Admin-Key: swauthkey
|
||||
|
||||
Example Curl Request::
|
||||
|
||||
curl -D - https://<endpoint>/auth/v2/<new_account> \
|
||||
-H "X-Auth-Admin-User: .super_admin" \
|
||||
-H "X-Auth-Admin-Key: swauthkey"
|
||||
|
||||
Example Response::
|
||||
|
||||
HTTP/1.1 201 Created
|
||||
|
||||
|
||||
Delete Account
|
||||
--------------
|
||||
|
||||
An account can be deleted with a DELETE request against an existing
|
||||
account.
|
||||
|
||||
Valid Responses:
|
||||
* 204: Success
|
||||
* 403: Invalid X-Auth-Admin-User/X-Auth-Admin-Key
|
||||
* 404: Account not found
|
||||
* 5xx: Internal error
|
||||
|
||||
Example Request::
|
||||
|
||||
DELETE /auth/<api version>/<account> HTTP/1.1
|
||||
X-Auth-Admin-User: .super_admin
|
||||
X-Auth-Admin-Key: swauthkey
|
||||
|
||||
Example Curl Request::
|
||||
|
||||
curl -XDELETE -D - https://<endpoint>/auth/v2/<account> \
|
||||
-H "X-Auth-Admin-User: .super_admin" \
|
||||
-H "X-Auth-Admin-Key: swauthkey"
|
||||
|
||||
Example Response::
|
||||
|
||||
HTTP/1.1 204 No Content
|
||||
|
||||
|
||||
User Services
|
||||
=============
|
||||
|
||||
Each account in swauth contains zero or more users. These users can
|
||||
be determined with the 'Get Account Details' API request against an
|
||||
account.
|
||||
|
||||
Users in an account can be created, modified, and detailed as
|
||||
described below by apply the appropriate REST verbs to a user URI, in
|
||||
the following general request structure::
|
||||
|
||||
METHOD /auth/<version>/<account>/<user> HTTP/1.1
|
||||
|
||||
The methods that can be used are detailed below.
|
||||
|
||||
Get User Details
|
||||
----------------
|
||||
|
||||
User details can be retrieved by performing a GET request against
|
||||
a user URI. On success, a JSON dictionary will be returned as
|
||||
described::
|
||||
|
||||
{"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": "<auth-type>:<key>"
|
||||
# The auth-type and key for the user; currently only
|
||||
# plaintext and sha1 are implemented as auth types.
|
||||
}
|
||||
|
||||
For example::
|
||||
|
||||
{"groups": [{"name": "test:tester"}, {"name": "test"},
|
||||
{"name": ".admin"}],
|
||||
"auth": "plaintext:testing"}
|
||||
|
||||
Valid Responses:
|
||||
* 200: Success
|
||||
* 403: Invalid X-Auth-Admin-User/X-Auth-Admin-Key
|
||||
* 404: Unknown account
|
||||
* 5xx: Internal error
|
||||
|
||||
Example Request::
|
||||
|
||||
GET /auth/<api version>/<account>/<user> HTTP/1.1
|
||||
X-Auth-Admin-User: .super_admin
|
||||
X-Auth-Admin-Key: swauthkey
|
||||
|
||||
Example Curl Request::
|
||||
|
||||
curl -D - https://<endpoint>/auth/v2/<account>/<user> \
|
||||
-H "X-Auth-Admin-User: .super_admin" \
|
||||
-H "X-Auth-Admin-Key: swauthkey"
|
||||
|
||||
Example Response::
|
||||
|
||||
HTTP/1.1 200 Ok
|
||||
|
||||
{ "groups": [ { "name": "<account>:<user>" },
|
||||
{ "name": "<user>" },
|
||||
{ "name": ".admin" } ],
|
||||
"auth" : "plaintext:password" }
|
||||
|
||||
|
||||
Create User
|
||||
-----------
|
||||
|
||||
A user can be created with a PUT request against a non-existent
|
||||
user URI. The new user's password must be set using the
|
||||
``X-Auth-User-Key`` header. The user name MUST NOT start with a
|
||||
period ('.'). This requirement is enforced by the API, and will
|
||||
result in a 400 error.
|
||||
|
||||
Optional Headers:
|
||||
|
||||
* ``X-Auth-User-Admin: true``: create the user as an account admin
|
||||
* ``X-Auth-User-Reseller-Admin: true``: create the user as a reseller
|
||||
admin
|
||||
|
||||
Reseller admin accounts can only be created by the site admin, while
|
||||
regular accounts (or account admin accounts) can be created by an
|
||||
account admin, an appropriate reseller admin, or the site admin.
|
||||
|
||||
Note that PUT requests are idempotent, and the PUT request serves as
|
||||
both a request and modify action.
|
||||
|
||||
Valid Responses:
|
||||
* 200: Success
|
||||
* 400: Invalid request (missing required headers)
|
||||
* 403: Invalid X-Auth-Admin-User/X-Auth-Admin-Key, or insufficient priv
|
||||
* 404: Unknown account
|
||||
* 5xx: Internal error
|
||||
|
||||
Example Request::
|
||||
|
||||
PUT /auth/<api version>/<account>/<user> HTTP/1.1
|
||||
X-Auth-Admin-User: .super_admin
|
||||
X-Auth-Admin-Key: swauthkey
|
||||
X-Auth-User-Admin: true
|
||||
X-Auth-User-Key: secret
|
||||
|
||||
Example Curl Request::
|
||||
|
||||
curl -XPUT -D - https://<endpoint>/auth/v2/<account>/<user> \
|
||||
-H "X-Auth-Admin-User: .super_admin" \
|
||||
-H "X-Auth-Admin-Key: swauthkey" \
|
||||
-H "X-Auth-User-Admin: true" \
|
||||
-H "X-Auth-User-Key: secret"
|
||||
|
||||
Example Response::
|
||||
|
||||
HTTP/1.1 201 Created
|
||||
|
||||
Delete User
|
||||
-----------
|
||||
|
||||
A user can be deleted by performing a DELETE request against a user
|
||||
URI. This action can only be performed by an account admin,
|
||||
appropriate reseller admin, or site admin.
|
||||
|
||||
Valid Responses:
|
||||
* 200: Success
|
||||
* 403: Invalid X-Auth-Admin-User/X-Auth-Admin-Key, or insufficient priv
|
||||
* 404: Unknown account or user
|
||||
* 5xx: Internal error
|
||||
|
||||
Example Request::
|
||||
|
||||
DELETE /auth/<api version>/<account>/<user> HTTP/1.1
|
||||
X-Auth-Admin-User: .super_admin
|
||||
X-Auth-Admin-Key: swauthkey
|
||||
|
||||
Example Curl Request::
|
||||
|
||||
curl -XDELETE -D - https://<endpoint>/auth/v2/<account>/<user> \
|
||||
-H "X-Auth-Admin-User: .super_admin" \
|
||||
-H "X-Auth-Admin-Key: swauthkey"
|
||||
|
||||
Example Response::
|
||||
|
||||
HTTP/1.1 204 No Content
|
||||
|
||||
|
||||
Other Services
|
||||
==============
|
||||
|
||||
There are several other swauth functions that can be performed, mostly
|
||||
done via "pseudo-user" accounts. These are well-known user names that
|
||||
are unable to be actually provisioned. These pseudo-users are
|
||||
described below.
|
||||
|
||||
.. _api_set_service_endpoints:
|
||||
|
||||
Set Service Endpoints
|
||||
---------------------
|
||||
|
||||
Service endpoint information can be retrived using the _`Get Account
|
||||
Details` API method.
|
||||
|
||||
This function allows setting values within this section for
|
||||
the <account>, allowing the addition of new service end points
|
||||
or updating existing ones by performing a POST to the URI
|
||||
corresponding to the pseudo-user ".services".
|
||||
|
||||
The body of the POST request should contain a JSON dict with
|
||||
the following format::
|
||||
|
||||
{"service_name": {"end_point_name": "end_point_value"}}
|
||||
|
||||
There can be multiple services and multiple end points in the
|
||||
same call.
|
||||
|
||||
Any new services or end points will be added to the existing
|
||||
set of services and end points. Any existing services with the
|
||||
same service name will be merged with the new end points. Any
|
||||
existing end points with the same end point name will have
|
||||
their values updated.
|
||||
|
||||
The updated services dictionary will be returned on success.
|
||||
|
||||
Valid Responses:
|
||||
|
||||
* 200: Success
|
||||
* 403: Invalid X-Auth-Admin-User/X-Auth-Admin-Key
|
||||
* 404: Account not found
|
||||
* 5xx: Internal error
|
||||
|
||||
Example Request::
|
||||
|
||||
POST /auth/<api version>/<account>/.services HTTP/1.0
|
||||
X-Auth-Admin-User: .super_admin
|
||||
X-Auth-Admin-Key: swauthkey
|
||||
|
||||
{"storage": { "local": "<new endpoint>" }}
|
||||
|
||||
Example Curl Request::
|
||||
|
||||
curl -XPOST -D - https://<endpoint>/auth/v2/<account>/.services \
|
||||
-H "X-Auth-Admin-User: .super_admin" \
|
||||
-H "X-Auth-Admin-Key: swauthkey" --data-binary \
|
||||
'{ "storage": { "local": "<new endpoint>" }}'
|
||||
|
||||
Example Response::
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
|
||||
{"storage": {"default": "local", "local": "<new endpoint>" }}
|
||||
|
||||
Get Account Groups
|
||||
------------------
|
||||
|
||||
Individual user group information can be retrieved using the `Get User Details`_ API method.
|
||||
|
||||
This function allows retrieving all group information for all users in
|
||||
an existing account. This can be achieved using a GET action against
|
||||
a user URI with the pseudo-user ".groups".
|
||||
|
||||
The JSON dictionary returned will be a "groups" dictionary similar to
|
||||
that documented in the `Get User Details`_ method, but representing
|
||||
the summary of all groups utilized by all active users in the account.
|
||||
|
||||
Valid Responses:
|
||||
* 200: Success
|
||||
* 403: Invalid X-Auth-Admin-User/X-Auth-Admin-Key
|
||||
* 404: Account not found
|
||||
* 5xx: Internal error
|
||||
|
||||
Example Request::
|
||||
|
||||
GET /auth/<api version>/<account>/.groups
|
||||
X-Auth-Admin-User: .super_admin
|
||||
X-Auth-Admin-Key: swauthkey
|
||||
|
||||
Example Curl Request::
|
||||
|
||||
curl -D - https://<endpoint>/auth/v2/<account>/.groups \
|
||||
-H "X-Auth-Admin-User: .super_admin" \
|
||||
-H "X-Auth-Admin-Key: swauthkey"
|
||||
|
||||
Example Response::
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
|
||||
{ "groups": [ { "name": ".admin" },
|
||||
{ "name": "<account>" },
|
||||
{ "name": "<account>:user1" },
|
||||
{ "name": "<account>:user2" } ] }
|
||||
|
@ -0,0 +1,10 @@
|
||||
.. _swauth_authtypes_module:
|
||||
|
||||
swauth.authtypes
|
||||
=================
|
||||
|
||||
.. automodule:: swauth.authtypes
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:noindex:
|
233
gluster/swift/common/middleware/gswauth/doc/source/conf.py
Normal file
233
gluster/swift/common/middleware/gswauth/doc/source/conf.py
Normal file
@ -0,0 +1,233 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
#
|
||||
# Swauth documentation build configuration file, created by
|
||||
# sphinx-quickstart on Mon Feb 14 19:34:51 2011.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
|
||||
import swauth
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Swauth'
|
||||
copyright = u'2010-2011, OpenStack, LLC'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '.'.join(str(v) for v in swauth.version_info[:2])
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = swauth.version
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = []
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'default'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'Swauthdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
# The paper size ('letter' or 'a4').
|
||||
#latex_paper_size = 'letter'
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#latex_font_size = '10pt'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'Swauth.tex', u'Swauth Documentation',
|
||||
u'OpenStack, LLC', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'swauth', u'Swauth Documentation',
|
||||
[u'OpenStack, LLC'], 1)
|
||||
]
|
159
gluster/swift/common/middleware/gswauth/doc/source/details.rst
Normal file
159
gluster/swift/common/middleware/gswauth/doc/source/details.rst
Normal file
@ -0,0 +1,159 @@
|
||||
----------------------
|
||||
Implementation Details
|
||||
----------------------
|
||||
|
||||
The Swauth system is a scalable authentication and authorization system that
|
||||
uses Swift itself as its backing store. This section will describe how it
|
||||
stores its data.
|
||||
|
||||
.. note::
|
||||
|
||||
You can access Swauth's internal .auth account by using the account:user of
|
||||
.super_admin:.super_admin and the super admin key you have set in your
|
||||
configuration. Here's an example using `st` on a standard SAIO: ``st -A
|
||||
http://127.0.0.1:8080/auth/v1.0 -U .super_admin:.super_admin -K swauthkey
|
||||
stat``
|
||||
|
||||
At the topmost level, the auth system has its own Swift account it stores its
|
||||
own account information within. This Swift account is known as
|
||||
self.auth_account in the code and its name is in the format
|
||||
self.reseller_prefix + ".auth". In this text, we'll refer to this account as
|
||||
<auth_account>.
|
||||
|
||||
The containers whose names do not begin with a period represent the accounts
|
||||
within the auth service. For example, the <auth_account>/test container would
|
||||
represent the "test" account.
|
||||
|
||||
The objects within each container represent the users for that auth service
|
||||
account. For example, the <auth_account>/test/bob object would represent the
|
||||
user "bob" within the auth service account of "test". Each of these user
|
||||
objects contain a JSON dictionary of the format::
|
||||
|
||||
{"auth": "<auth_type>:<auth_value>", "groups": <groups_array>}
|
||||
|
||||
The `<auth_type>` specifies how the user key is encoded. The default is `plaintext`,
|
||||
which saves the user's key in plaintext in the `<auth_value>` field.
|
||||
The value `sha1` is supported as well, which stores the user's key as a salted
|
||||
SHA1 hash. Note that using a one-way hash like SHA1 will likely inhibit future use of key-signing request types, assuming such support is added. The `<auth_type>` can be specified in the swauth section of the proxy server's
|
||||
config file, along with the salt value in the following way::
|
||||
|
||||
auth_type = <auth_type>
|
||||
auth_type_salt = <salt-value>
|
||||
|
||||
Both fields are optional. auth_type defaults to `plaintext` and auth_type_salt defaults to "swauthsalt". Additional auth types can be implemented along with existing ones in the authtypes.py module.
|
||||
|
||||
The `<groups_array>` contains at least two groups. The first is a unique group
|
||||
identifying that user and it's name is of the format `<user>:<account>`. The
|
||||
second group is the `<account>` itself. Additional groups of `.admin` for
|
||||
account administrators and `.reseller_admin` for reseller administrators may
|
||||
exist. Here's an example user JSON dictionary::
|
||||
|
||||
{"auth": "plaintext:testing",
|
||||
"groups": ["name": "test:tester", "name": "test", "name": ".admin"]}
|
||||
|
||||
To map an auth service account to a Swift storage account, the Service Account
|
||||
Id string is stored in the `X-Container-Meta-Account-Id` header for the
|
||||
<auth_account>/<account> container. To map back the other way, an
|
||||
<auth_account>/.account_id/<account_id> object is created with the contents of
|
||||
the corresponding auth service's account name.
|
||||
|
||||
Also, to support a future where the auth service will support multiple Swift
|
||||
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>}}
|
||||
|
||||
The "default" is always "local" right now, and "local" is always the single
|
||||
Swift cluster URL; but in the future there can be more than one cluster with
|
||||
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 .services dictionary at the moment::
|
||||
|
||||
{"storage":
|
||||
{"default": "local",
|
||||
"local": "http://127.0.0.1:8080/v1/AUTH_8980f74b1cda41e483cbe0a925f448a9"}}
|
||||
|
||||
But, here's an example of what the dictionary may look like in the future::
|
||||
|
||||
{"storage":
|
||||
{"default": "dfw",
|
||||
"dfw": "http://dfw.storage.com:8080/v1/AUTH_8980f74b1cda41e483cbe0a925f448a9",
|
||||
"ord": "http://ord.storage.com:8080/v1/AUTH_8980f74b1cda41e483cbe0a925f448a9",
|
||||
"sat": "http://ord.storage.com:8080/v1/AUTH_8980f74b1cda41e483cbe0a925f448a9"},
|
||||
"servers":
|
||||
{"default": "dfw",
|
||||
"dfw": "http://dfw.servers.com:8080/v1/AUTH_8980f74b1cda41e483cbe0a925f448a9",
|
||||
"ord": "http://ord.servers.com:8080/v1/AUTH_8980f74b1cda41e483cbe0a925f448a9",
|
||||
"sat": "http://ord.servers.com:8080/v1/AUTH_8980f74b1cda41e483cbe0a925f448a9"}}
|
||||
|
||||
Lastly, the tokens themselves are stored as objects in the
|
||||
`<auth_account>/.token_[0-f]` containers. The names of the objects are the
|
||||
token strings themselves, such as `AUTH_tked86bbd01864458aa2bd746879438d5a`.
|
||||
The exact `.token_[0-f]` container chosen is based on the final digit of the
|
||||
token name, such as `.token_a` for the token
|
||||
`AUTH_tked86bbd01864458aa2bd746879438d5a`. The contents of the token objects
|
||||
are JSON dictionaries of the format::
|
||||
|
||||
{"account": <account>,
|
||||
"user": <user>,
|
||||
"account_id": <account_id>,
|
||||
"groups": <groups_array>,
|
||||
"expires": <time.time() value>}
|
||||
|
||||
The `<account>` is the auth service account's name for that token. The `<user>`
|
||||
is the user within the account for that token. The `<account_id>` is the
|
||||
same as the `X-Container-Meta-Account-Id` for the auth service's account,
|
||||
as described above. The `<groups_array>` is the user's groups, as described
|
||||
above with the user object. The "expires" value indicates when the token is no
|
||||
longer valid, as compared to Python's time.time() value.
|
||||
|
||||
Here's an example token object's JSON dictionary::
|
||||
|
||||
{"account": "test",
|
||||
"user": "tester",
|
||||
"account_id": "AUTH_8980f74b1cda41e483cbe0a925f448a9",
|
||||
"groups": ["name": "test:tester", "name": "test", "name": ".admin"],
|
||||
"expires": 1291273147.1624689}
|
||||
|
||||
To easily map a user to an already issued token, the token name is stored in
|
||||
the user object's `X-Object-Meta-Auth-Token` header.
|
||||
|
||||
Here is an example full listing of an <auth_account>::
|
||||
|
||||
.account_id
|
||||
AUTH_2282f516-559f-4966-b239-b5c88829e927
|
||||
AUTH_f6f57a3c-33b5-4e85-95a5-a801e67505c8
|
||||
AUTH_fea96a36-c177-4ca4-8c7e-b8c715d9d37b
|
||||
.token_0
|
||||
.token_1
|
||||
.token_2
|
||||
.token_3
|
||||
.token_4
|
||||
.token_5
|
||||
.token_6
|
||||
AUTH_tk9d2941b13d524b268367116ef956dee6
|
||||
.token_7
|
||||
.token_8
|
||||
AUTH_tk93627c6324c64f78be746f1e6a4e3f98
|
||||
.token_9
|
||||
.token_a
|
||||
.token_b
|
||||
.token_c
|
||||
.token_d
|
||||
.token_e
|
||||
AUTH_tk0d37d286af2c43ffad06e99112b3ec4e
|
||||
.token_f
|
||||
AUTH_tk766bbde93771489982d8dc76979d11cf
|
||||
reseller
|
||||
.services
|
||||
reseller
|
||||
test
|
||||
.services
|
||||
tester
|
||||
tester3
|
||||
test2
|
||||
.services
|
||||
tester2
|
142
gluster/swift/common/middleware/gswauth/doc/source/index.rst
Normal file
142
gluster/swift/common/middleware/gswauth/doc/source/index.rst
Normal file
@ -0,0 +1,142 @@
|
||||
.. Swauth documentation master file, created by
|
||||
sphinx-quickstart on Mon Feb 14 19:34:51 2011.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Swauth
|
||||
======
|
||||
|
||||
Copyright (c) 2010-2012 OpenStack, LLC
|
||||
|
||||
An Auth Service for Swift as WSGI Middleware that uses Swift itself as a
|
||||
backing store. Sphinx-built docs at: http://gholt.github.com/swauth/
|
||||
Source available at: https://github.com/gholt/swauth
|
||||
|
||||
See also https://github.com/openstack/keystone for the standard OpenStack
|
||||
auth service.
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Before discussing how to install Swauth within a Swift system, it might help to understand how Swauth does it work first.
|
||||
|
||||
1. Swauth is middleware installed in the Swift Proxy's WSGI pipeline.
|
||||
|
||||
2. It intercepts requests to ``/auth/`` (by default).
|
||||
|
||||
3. It also uses Swift's `authorize callback <http://swift.openstack.org/development_auth.html>`_ and `acl callback <http://swift.openstack.org/misc.html#module-swift.common.middleware.acl>`_ features to authorize Swift requests.
|
||||
|
||||
4. Swauth will also make various internal calls to the Swift WSGI pipeline it's installed in to manipulate containers and objects within an ``AUTH_.auth`` (by default) Swift account. These containers and objects are what store account and user information.
|
||||
|
||||
5. Instead of #4, Swauth can be configured to call out to another remote Swauth to perform #4 on its behalf (using the swauth_remote config value).
|
||||
|
||||
6. When managing accounts and users with the various ``swauth-`` command line tools, these tools are actually just performing HTTP requests against the ``/auth/`` end point referenced in #2. You can make your own tools that use the same :ref:`API <api_top>`.
|
||||
|
||||
7. In the special case of creating a new account, Swauth will do its usual WSGI-internal requests as per #4 but will also call out to the Swift cluster to create the actual Swift account.
|
||||
|
||||
a. This Swift cluster callout is an account PUT request to the URL defined by the ``swift_default_cluster`` config value.
|
||||
|
||||
b. This callout end point is also saved when the account is created so that it can be given to the users of that account in the future.
|
||||
|
||||
c. Sometimes, due to public/private network routing or firewalling, the URL Swauth should use should be different than the URL Swauth should give the users later. That is why the ``default_swift_cluster`` config value can accept two URLs (first is the one for users, second is the one for Swauth).
|
||||
|
||||
d. Once an account is created, the URL given to users for that account will not change, even if the ``default_swift_cluster`` config value changes. This is so that you can use multiple clusters with the same Swauth system; ``default_swift_cluster`` just points to the one where you want new users to go.
|
||||
|
||||
f. You can change the stored URL for an account if need be with the ``swauth-set-account-service`` command line tool or a POST request (see :ref:`API <api_set_service_endpoints>`).
|
||||
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
1) Install Swauth with ``sudo python setup.py install`` or ``sudo python
|
||||
setup.py develop`` or via whatever packaging system you may be using.
|
||||
|
||||
2) Alter your ``proxy-server.conf`` pipeline to have ``swauth`` instead of ``tempauth``:
|
||||
|
||||
Was::
|
||||
|
||||
[pipeline:main]
|
||||
pipeline = catch_errors cache tempauth proxy-server
|
||||
|
||||
Change To::
|
||||
|
||||
[pipeline:main]
|
||||
pipeline = catch_errors cache swauth proxy-server
|
||||
|
||||
3) Add to your ``proxy-server.conf`` the section for the Swauth WSGI filter::
|
||||
|
||||
[filter:swauth]
|
||||
use = egg:swauth#swauth
|
||||
set log_name = swauth
|
||||
super_admin_key = swauthkey
|
||||
default_swift_cluster = <your setting as discussed below>
|
||||
|
||||
The ``default_swift_cluster`` setting can be confusing.
|
||||
|
||||
a. If you're using an all-in-one type configuration where everything will be run on the local host on port 8080, you can omit the ``default_swift_cluster`` completely and it will default to ``local#http://127.0.0.1:8080/v1``.
|
||||
|
||||
b. If you're using a single Swift proxy you can just set the ``default_swift_cluster = cluster_name#https://<public_ip>:<port>/v1`` and that URL will be given to users as well as used by Swauth internally. (Quick note: be sure the ``http`` vs. ``https`` is set right depending on if you're using SSL.)
|
||||
|
||||
c. If you're using multiple Swift proxies behind a load balancer, you'll probably want ``default_swift_cluster = cluster_name#https://<load_balancer_ip>:<port>/v1#http://127.0.0.1:<port>/v1`` so that Swauth gives out the first URL but uses the second URL internally. Remember to double-check the ``http`` vs. ``https`` settings for each of the URLs; they might be different if you're terminating SSL at the load balancer.
|
||||
|
||||
Also see the ``proxy-server.conf-sample`` for more config options, such as the ability to have a remote Swauth in a multiple Swift cluster configuration.
|
||||
|
||||
4) Be sure your Swift proxy allows account management in the ``proxy-server.conf``::
|
||||
|
||||
[app:proxy-server]
|
||||
...
|
||||
allow_account_management = true
|
||||
|
||||
For greater security, you can leave this off any public proxies and just have one or two private proxies with it turned on.
|
||||
|
||||
5) Restart your proxy server ``swift-init proxy reload``
|
||||
|
||||
6) Initialize the Swauth backing store in Swift ``swauth-prep -K swauthkey``
|
||||
|
||||
7) Add an account/user ``swauth-add-user -A http[s]://<host>:<port>/auth/ -K
|
||||
swauthkey -a test tester testing``
|
||||
|
||||
8) Ensure it works ``swift -A http[s]://<host>:<port>/auth/v1.0 -U test:tester -K testing stat -v``
|
||||
|
||||
|
||||
If anything goes wrong, it's best to start checking the proxy server logs. The client command line utilities often don't get enough information to help. I will often just ``tail -F`` the appropriate proxy log (``/var/log/syslog`` or however you have it configured) and then run the Swauth command to see exactly what requests are happening to try to determine where things fail.
|
||||
|
||||
General note, I find I occasionally just forget to reload the proxies after a config change; so that's the first thing you might try. Or, if you suspect the proxies aren't reloading properly, you might try ``swift-init proxy stop``, ensure all the processes died, then ``swift-init proxy start``.
|
||||
|
||||
Also, it's quite common to get the ``/auth/v1.0`` vs. just ``/auth/`` URL paths confused. Usual rule is: Swauth tools use just ``/auth/`` and Swift tools use ``/auth/v1.0``.
|
||||
|
||||
|
||||
Web Admin Install
|
||||
-----------------
|
||||
|
||||
1) If you installed from packages, you'll need to cd to the webadmin directory
|
||||
the package installed. This is ``/usr/share/doc/python-swauth/webadmin``
|
||||
with the Lucid packages. If you installed from source, you'll need to cd to
|
||||
the webadmin directory in the source directory.
|
||||
|
||||
2) Upload the Web Admin files with ``swift -A http[s]://<host>:<port>/auth/v1.0
|
||||
-U .super_admin:.super_admin -K swauthkey upload .webadmin .``
|
||||
|
||||
3) Open ``http[s]://<host>:<port>/auth/`` in your browser.
|
||||
|
||||
|
||||
Contents
|
||||
--------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
license
|
||||
details
|
||||
swauth
|
||||
middleware
|
||||
api
|
||||
authtypes
|
||||
|
||||
|
||||
Indices and tables
|
||||
------------------
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
225
gluster/swift/common/middleware/gswauth/doc/source/license.rst
Normal file
225
gluster/swift/common/middleware/gswauth/doc/source/license.rst
Normal file
@ -0,0 +1,225 @@
|
||||
.. _license:
|
||||
|
||||
*******
|
||||
LICENSE
|
||||
*******
|
||||
|
||||
::
|
||||
|
||||
Copyright (c) 2010-2011 OpenStack, LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@ -0,0 +1,9 @@
|
||||
.. _swauth_middleware_module:
|
||||
|
||||
swauth.middleware
|
||||
=================
|
||||
|
||||
.. automodule:: swauth.middleware
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
@ -0,0 +1,9 @@
|
||||
.. _swauth_module:
|
||||
|
||||
swauth
|
||||
======
|
||||
|
||||
.. automodule:: swauth
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
@ -0,0 +1,78 @@
|
||||
[DEFAULT]
|
||||
# Standard from Swift
|
||||
|
||||
[pipeline:main]
|
||||
# Standard from Swift, this is just an example of where to put swauth
|
||||
pipeline = catch_errors healthcheck cache ratelimit swauth proxy-server
|
||||
|
||||
[app:proxy-server]
|
||||
# Standard from Swift, main point to note is the inclusion of
|
||||
# allow_account_management = true (only for the proxy servers where you want to
|
||||
# be able to create/delete accounts).
|
||||
use = egg:swift#proxy
|
||||
allow_account_management = true
|
||||
|
||||
[filter:swauth]
|
||||
use = egg:swauth#swauth
|
||||
# You can override the default log routing for this filter here:
|
||||
# set log_name = swauth
|
||||
# set log_facility = LOG_LOCAL0
|
||||
# set log_level = INFO
|
||||
# set log_headers = False
|
||||
# The reseller prefix will verify a token begins with this prefix before even
|
||||
# attempting to validate it. Also, with authorization, only Swift storage
|
||||
# accounts with this prefix will be authorized by this middleware. Useful if
|
||||
# multiple auth systems are in use for one Swift cluster.
|
||||
# reseller_prefix = AUTH
|
||||
# If you wish to use a Swauth service on a remote cluster with this cluster:
|
||||
# swauth_remote = http://remotehost:port/auth
|
||||
# swauth_remote_timeout = 10
|
||||
# When using swauth_remote, the rest of these settings have no effect.
|
||||
#
|
||||
# The auth prefix will cause requests beginning with this prefix to be routed
|
||||
# to the auth subsystem, for granting tokens, creating accounts, users, etc.
|
||||
# auth_prefix = /auth/
|
||||
# Cluster strings are of the format name#url where name is a short name for the
|
||||
# Swift cluster and url is the url to the proxy server(s) for the cluster.
|
||||
# default_swift_cluster = local#http://127.0.0.1:8080/v1
|
||||
# You may also use the format name#url#url where the first url is the one
|
||||
# given to users to access their account (public url) and the second is the one
|
||||
# used by swauth itself to create and delete accounts (private url). This is
|
||||
# useful when a load balancer url should be used by users, but swauth itself is
|
||||
# behind the load balancer. Example:
|
||||
# default_swift_cluster = local#https://public.com:8080/v1#http://private.com:8080/v1
|
||||
# Number of seconds a newly issued token should be valid for, by default.
|
||||
# token_life = 86400
|
||||
# Maximum number of seconds a newly issued token can be valid for.
|
||||
# max_token_life = <same as token_life>
|
||||
# Specifies how the user key is stored. The default is 'plaintext', leaving the
|
||||
# key unsecured but available for key-signing features if such are ever added.
|
||||
# An alternative is 'sha1' which stores only a one-way hash of the key leaving
|
||||
# it secure but unavailable for key-signing.
|
||||
# auth_type = plaintext
|
||||
# Used if the auth_type is sha1 or another method that can make use of a salt.
|
||||
# auth_type_salt = swauthsalt
|
||||
# This allows middleware higher in the WSGI pipeline to override auth
|
||||
# processing, useful for middleware such as tempurl and formpost. If you know
|
||||
# you're not going to use such middleware and you want a bit of extra security,
|
||||
# you can set this to false.
|
||||
# allow_overrides = true
|
||||
# Highly recommended to change this. If you comment this out, the Swauth
|
||||
# administration features will be disabled for this proxy.
|
||||
super_admin_key = swauthkey
|
||||
|
||||
[filter:ratelimit]
|
||||
# Standard from Swift
|
||||
use = egg:swift#ratelimit
|
||||
|
||||
[filter:cache]
|
||||
# Standard from Swift
|
||||
use = egg:swift#memcache
|
||||
|
||||
[filter:healthcheck]
|
||||
# Standard from Swift
|
||||
use = egg:swift#healthcheck
|
||||
|
||||
[filter:catch_errors]
|
||||
# Standard from Swift
|
||||
use = egg:swift#catch_errors
|
30
gluster/swift/common/middleware/gswauth/locale/swauth.pot
Normal file
30
gluster/swift/common/middleware/gswauth/locale/swauth.pot
Normal file
@ -0,0 +1,30 @@
|
||||
# Translations template for swauth.
|
||||
# Copyright (C) 2011 ORGANIZATION
|
||||
# This file is distributed under the same license as the swauth project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: swauth 1.0.1.dev\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2011-05-26 10:35+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 0.9.4\n"
|
||||
|
||||
#: swauth/middleware.py:94
|
||||
msgid "No super_admin_key set in conf file! Exiting."
|
||||
msgstr ""
|
||||
|
||||
#: swauth/middleware.py:637
|
||||
#, python-format
|
||||
msgid ""
|
||||
"ERROR: Exception while trying to communicate with "
|
||||
"%(scheme)s://%(host)s:%(port)s/%(path)s"
|
||||
msgstr ""
|
||||
|
23
gluster/swift/common/middleware/gswauth/setup.cfg
Normal file
23
gluster/swift/common/middleware/gswauth/setup.cfg
Normal file
@ -0,0 +1,23 @@
|
||||
[build_sphinx]
|
||||
all_files = 1
|
||||
build-dir = doc/build
|
||||
source-dir = doc/source
|
||||
|
||||
[egg_info]
|
||||
tag_build =
|
||||
tag_date = 0
|
||||
tag_svn_revision = 0
|
||||
|
||||
[compile_catalog]
|
||||
directory = locale
|
||||
domain = swauth
|
||||
|
||||
[update_catalog]
|
||||
domain = swauth
|
||||
output_dir = locale
|
||||
input_file = locale/swauth.pot
|
||||
|
||||
[extract_messages]
|
||||
keywords = _ l_ lazy_gettext
|
||||
mapping_file = babel.cfg
|
||||
output_file = locale/swauth.pot
|
89
gluster/swift/common/middleware/gswauth/setup.py
Normal file
89
gluster/swift/common/middleware/gswauth/setup.py
Normal file
@ -0,0 +1,89 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
from setuptools.command.sdist import sdist
|
||||
import os
|
||||
import subprocess
|
||||
try:
|
||||
from babel.messages import frontend
|
||||
except ImportError:
|
||||
frontend = None
|
||||
|
||||
from swauth import __version__ as version
|
||||
|
||||
|
||||
class local_sdist(sdist):
|
||||
"""Customized sdist hook - builds the ChangeLog file from VC first"""
|
||||
|
||||
def run(self):
|
||||
if os.path.isdir('.bzr'):
|
||||
# We're in a bzr branch
|
||||
|
||||
log_cmd = subprocess.Popen(["bzr", "log", "--gnu"],
|
||||
stdout=subprocess.PIPE)
|
||||
changelog = log_cmd.communicate()[0]
|
||||
with open("ChangeLog", "w") as changelog_file:
|
||||
changelog_file.write(changelog)
|
||||
sdist.run(self)
|
||||
|
||||
|
||||
name = 'swauth'
|
||||
|
||||
|
||||
cmdclass = {'sdist': local_sdist}
|
||||
|
||||
|
||||
if frontend:
|
||||
cmdclass.update({
|
||||
'compile_catalog': frontend.compile_catalog,
|
||||
'extract_messages': frontend.extract_messages,
|
||||
'init_catalog': frontend.init_catalog,
|
||||
'update_catalog': frontend.update_catalog,
|
||||
})
|
||||
|
||||
|
||||
setup(
|
||||
name=name,
|
||||
version=version,
|
||||
description='Swauth',
|
||||
license='Apache License (2.0)',
|
||||
author='OpenStack, LLC.',
|
||||
author_email='swauth@brim.net',
|
||||
url='https://github.com/gholt/swauth',
|
||||
packages=find_packages(exclude=['test_swauth', 'bin']),
|
||||
test_suite='nose.collector',
|
||||
cmdclass=cmdclass,
|
||||
classifiers=[
|
||||
'Development Status :: 4 - Beta',
|
||||
'License :: OSI Approved :: Apache Software License',
|
||||
'Operating System :: POSIX :: Linux',
|
||||
'Programming Language :: Python :: 2.6',
|
||||
'Environment :: No Input/Output (Daemon)',
|
||||
],
|
||||
install_requires=[], # removed for better compat
|
||||
scripts=[
|
||||
'bin/swauth-add-account', 'bin/swauth-add-user',
|
||||
'bin/swauth-cleanup-tokens', 'bin/swauth-delete-account',
|
||||
'bin/swauth-delete-user', 'bin/swauth-list', 'bin/swauth-prep',
|
||||
'bin/swauth-set-account-service',
|
||||
],
|
||||
entry_points={
|
||||
'paste.filter_factory': [
|
||||
'swauth=swauth.middleware:filter_factory',
|
||||
],
|
||||
},
|
||||
)
|
23
gluster/swift/common/middleware/gswauth/swauth/__init__.py
Normal file
23
gluster/swift/common/middleware/gswauth/swauth/__init__.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Copyright (c) 2010-2013 OpenStack, LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import gettext
|
||||
|
||||
|
||||
#: Version information (major, minor, revision[, 'dev']).
|
||||
version_info = (1, 0, 9, 'dev')
|
||||
#: Version string 'major.minor.revision'.
|
||||
version = __version__ = ".".join(map(str, version_info))
|
||||
gettext.install('swauth')
|
103
gluster/swift/common/middleware/gswauth/swauth/authtypes.py
Normal file
103
gluster/swift/common/middleware/gswauth/swauth/authtypes.py
Normal file
@ -0,0 +1,103 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# Pablo Llopis 2011
|
||||
|
||||
|
||||
"""
|
||||
This module hosts available auth types for encoding and matching user keys.
|
||||
For adding a new auth type, simply write a class that satisfies the following
|
||||
conditions:
|
||||
|
||||
- For the class name, capitalize first letter only. This makes sure the user
|
||||
can specify an all-lowercase config option such as "plaintext" or "sha1".
|
||||
Swauth takes care of capitalizing the first letter before instantiating it.
|
||||
- Write an encode(key) method that will take a single argument, the user's key,
|
||||
and returns the encoded string. For plaintext, this would be
|
||||
"plaintext:<key>"
|
||||
- Write a match(key, creds) method that will take two arguments: the user's
|
||||
key, and the user's retrieved credentials. Return a boolean value that
|
||||
indicates whether the match is True or False.
|
||||
|
||||
Note that, since some of the encodings will be hashes, swauth supports the
|
||||
notion of salts. Thus, self.salt will be set to either a user-specified salt
|
||||
value or to a default value.
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
|
||||
|
||||
#: Maximum length any valid token should ever be.
|
||||
MAX_TOKEN_LENGTH = 5000
|
||||
|
||||
|
||||
class Plaintext(object):
|
||||
"""
|
||||
Provides a particular auth type for encoding format for encoding and
|
||||
matching user keys.
|
||||
|
||||
This class must be all lowercase except for the first character, which
|
||||
must be capitalized. encode and match methods must be provided and are
|
||||
the only ones that will be used by swauth.
|
||||
"""
|
||||
def encode(self, key):
|
||||
"""
|
||||
Encodes a user key into a particular format. The result of this method
|
||||
will be used by swauth for storing user credentials.
|
||||
|
||||
:param key: User's secret key
|
||||
:returns: A string representing user credentials
|
||||
"""
|
||||
return "plaintext:%s" % key
|
||||
|
||||
def match(self, key, creds):
|
||||
"""
|
||||
Checks whether the user-provided key matches the user's credentials
|
||||
|
||||
:param key: User-supplied key
|
||||
:param creds: User's stored credentials
|
||||
:returns: True if the supplied key is valid, False otherwise
|
||||
"""
|
||||
return self.encode(key) == creds
|
||||
|
||||
|
||||
class Sha1(object):
|
||||
"""
|
||||
Provides a particular auth type for encoding format for encoding and
|
||||
matching user keys.
|
||||
|
||||
This class must be all lowercase except for the first character, which
|
||||
must be capitalized. encode and match methods must be provided and are
|
||||
the only ones that will be used by swauth.
|
||||
"""
|
||||
def encode(self, key):
|
||||
"""
|
||||
Encodes a user key into a particular format. The result of this method
|
||||
will be used by swauth for storing user credentials.
|
||||
|
||||
:param key: User's secret key
|
||||
:returns: A string representing user credentials
|
||||
"""
|
||||
enc_key = '%s%s' % (self.salt, key)
|
||||
enc_val = hashlib.sha1(enc_key).hexdigest()
|
||||
return "sha1:%s$%s" % (self.salt, enc_val)
|
||||
|
||||
def match(self, key, creds):
|
||||
"""
|
||||
Checks whether the user-provided key matches the user's credentials
|
||||
|
||||
:param key: User-supplied key
|
||||
:param creds: User's stored credentials
|
||||
:returns: True if the supplied key is valid, False otherwise
|
||||
"""
|
||||
return self.encode(key) == creds
|
1546
gluster/swift/common/middleware/gswauth/swauth/middleware.py
Normal file
1546
gluster/swift/common/middleware/gswauth/swauth/middleware.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,71 @@
|
||||
import swift
|
||||
|
||||
|
||||
MAJOR = None
|
||||
MINOR = None
|
||||
REVISION = None
|
||||
FINAL = None
|
||||
|
||||
|
||||
def parse(value):
|
||||
parts = value.split('.')
|
||||
if parts[-1].endswith('-dev'):
|
||||
final = False
|
||||
parts[-1] = parts[-1][:-4]
|
||||
else:
|
||||
final = True
|
||||
major = int(parts.pop(0))
|
||||
minor = int(parts.pop(0))
|
||||
if parts:
|
||||
revision = int(parts.pop(0))
|
||||
else:
|
||||
revision = 0
|
||||
return major, minor, revision, final
|
||||
|
||||
|
||||
def newer_than(value):
|
||||
global MAJOR, MINOR, REVISION, FINAL
|
||||
major, minor, revision, final = parse(value)
|
||||
if MAJOR is None:
|
||||
MAJOR, MINOR, REVISION, FINAL = parse(swift.__version__)
|
||||
if MAJOR < major:
|
||||
return False
|
||||
elif MAJOR == major:
|
||||
if MINOR < minor:
|
||||
return False
|
||||
elif MINOR == minor:
|
||||
if REVISION < revision:
|
||||
return False
|
||||
elif REVISION == revision:
|
||||
if not FINAL or final:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def run_tests():
|
||||
global MAJOR, MINOR, REVISION, FINAL
|
||||
MAJOR, MINOR, REVISION, FINAL = parse('1.3')
|
||||
assert(newer_than('1.2'))
|
||||
assert(newer_than('1.2.9'))
|
||||
assert(newer_than('1.3-dev'))
|
||||
assert(newer_than('1.3.0-dev'))
|
||||
assert(not newer_than('1.3'))
|
||||
assert(not newer_than('1.3.0'))
|
||||
assert(not newer_than('1.3.1-dev'))
|
||||
assert(not newer_than('1.3.1'))
|
||||
assert(not newer_than('1.4'))
|
||||
assert(not newer_than('2.0'))
|
||||
MAJOR, MINOR, REVISION, FINAL = parse('1.7.7-dev')
|
||||
assert(newer_than('1.6'))
|
||||
assert(newer_than('1.7'))
|
||||
assert(newer_than('1.7.6-dev'))
|
||||
assert(newer_than('1.7.6'))
|
||||
assert(not newer_than('1.7.7'))
|
||||
assert(not newer_than('1.7.8-dev'))
|
||||
assert(not newer_than('1.7.8'))
|
||||
assert(not newer_than('1.8.0'))
|
||||
assert(not newer_than('2.0'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_tests()
|
@ -0,0 +1,10 @@
|
||||
# See http://code.google.com/p/python-nose/issues/detail?id=373
|
||||
# The code below enables nosetests to work with i18n _() blocks
|
||||
|
||||
import __builtin__
|
||||
import sys
|
||||
import os
|
||||
from ConfigParser import MissingSectionHeaderError
|
||||
from StringIO import StringIO
|
||||
|
||||
setattr(__builtin__, '_', lambda x: x)
|
@ -0,0 +1,64 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# Pablo Llopis 2011
|
||||
|
||||
import unittest
|
||||
from contextlib import contextmanager
|
||||
from swauth import authtypes
|
||||
|
||||
|
||||
class TestPlaintext(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.auth_encoder = authtypes.Plaintext()
|
||||
|
||||
def test_plaintext_encode(self):
|
||||
enc_key = self.auth_encoder.encode('keystring')
|
||||
self.assertEquals('plaintext:keystring', enc_key)
|
||||
|
||||
def test_plaintext_valid_match(self):
|
||||
creds = 'plaintext:keystring'
|
||||
match = self.auth_encoder.match('keystring', creds)
|
||||
self.assertEquals(match, True)
|
||||
|
||||
def test_plaintext_invalid_match(self):
|
||||
creds = 'plaintext:other-keystring'
|
||||
match = self.auth_encoder.match('keystring', creds)
|
||||
self.assertEquals(match, False)
|
||||
|
||||
|
||||
class TestSha1(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.auth_encoder = authtypes.Sha1()
|
||||
self.auth_encoder.salt = 'salt'
|
||||
|
||||
def test_sha1_encode(self):
|
||||
enc_key = self.auth_encoder.encode('keystring')
|
||||
self.assertEquals('sha1:salt$d50dc700c296e23ce5b41f7431a0e01f69010f06',
|
||||
enc_key)
|
||||
|
||||
def test_sha1_valid_match(self):
|
||||
creds = 'sha1:salt$d50dc700c296e23ce5b41f7431a0e01f69010f06'
|
||||
match = self.auth_encoder.match('keystring', creds)
|
||||
self.assertEquals(match, True)
|
||||
|
||||
def test_sha1_invalid_match(self):
|
||||
creds = 'sha1:salt$deadbabedeadbabedeadbabec0ffeebadc0ffeee'
|
||||
match = self.auth_encoder.match('keystring', creds)
|
||||
self.assertEquals(match, False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
File diff suppressed because it is too large
Load Diff
552
gluster/swift/common/middleware/gswauth/webadmin/index.html
Normal file
552
gluster/swift/common/middleware/gswauth/webadmin/index.html
Normal file
@ -0,0 +1,552 @@
|
||||
<html>
|
||||
<head>
|
||||
<style type="text/css">
|
||||
body {font-family: sans-serif}
|
||||
table {border-collapse: collapse}
|
||||
td {padding-left: 1ex; padding-right: 1ex}
|
||||
.account {color: #0000ff; padding-left: 3ex; cursor: pointer}
|
||||
.add_account_heading {text-align: right; padding-right: 0}
|
||||
.service {padding-left: 3ex; vertical-align: top}
|
||||
.service_detail {padding-left: 0}
|
||||
.user {color: #0000ff; padding-left: 3ex; cursor: pointer}
|
||||
.group {padding-left: 3ex}
|
||||
.add_user_heading {text-align: right; padding-right: 0}
|
||||
.shadow_delement {color: #0000ff; cursor: pointer}
|
||||
.shadow_felement {display: none}
|
||||
#swauth {font-size: 200%; font-weight: bold; font-style: italic; margin: 0px; padding: 0px}
|
||||
#creds_area {float: right}
|
||||
#logout {color: #0000ff; padding-left: 3ex; cursor: pointer}
|
||||
#refresh_accounts {color: #0000ff; padding-left: 1ex; cursor: pointer}
|
||||
#add_account {color: #0000ff; padding-left: 1ex; padding-right: 1ex; cursor: pointer}
|
||||
#add_account_title {padding-top: 1ex; padding-bottom: 1ex}
|
||||
#add_account_cancel {color: #0000ff; padding-top: 1ex; padding-left: 3ex; cursor: pointer}
|
||||
#add_account_save {color: #0000ff; text-align: right; padding-top: 1ex; padding-right: 3ex; cursor: pointer}
|
||||
#account_area {background: #ddeeff}
|
||||
#add_user {color: #0000ff; padding-left: 1ex; padding-right: 1ex; cursor: pointer}
|
||||
#add_user_title {padding-top: 1ex; padding-bottom: 1ex}
|
||||
#add_user_cancel {color: #0000ff; padding-top: 1ex; padding-left: 3ex; cursor: pointer}
|
||||
#add_user_save {color: #0000ff; text-align: right; padding-top: 1ex; padding-right: 3ex; cursor: pointer}
|
||||
#delete_account {color: #0000ff; text-align: right; margin-left: 45ex; padding-right: 1ex; cursor: pointer}
|
||||
#user_area {background: #aaccff}
|
||||
#delete_user {color: #0000ff; text-align: right; margin-left: 45ex; padding-right: 1ex; cursor: pointer}
|
||||
#auth_view {display: none}
|
||||
#auth_toggler {color: #0000ff; cursor: pointer}
|
||||
#auth_update {color: #0000ff; padding-left: 1ex; cursor: pointer}
|
||||
#auth_update_field {display: none}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
var request = null;
|
||||
var creds_user = '';
|
||||
var creds_key = '';
|
||||
var creds_logged_in = true;
|
||||
var account = '';
|
||||
var user = '';
|
||||
var account_selection = -1;
|
||||
var user_selection = -1;
|
||||
var swauth_area_selected_background = '#ddeeff';
|
||||
var account_area_selected_background = '#aaccff';
|
||||
var endpoints;
|
||||
|
||||
function get_bounds(element) {
|
||||
bounds = {};
|
||||
bounds.top = 0;
|
||||
bounds.left = 0;
|
||||
bounds.width = element.offsetWidth;
|
||||
bounds.height = element.offsetHeight;
|
||||
if (element.offsetParent) {
|
||||
do {
|
||||
bounds.top += element.offsetTop;
|
||||
bounds.left += element.offsetLeft;
|
||||
} while (element = element.offsetParent);
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
function shadow_edit(delement) {
|
||||
felement = document.getElementById('f' + delement.id.substring(1));
|
||||
felement.value = delement.innerHTML;
|
||||
delement.style.display = 'none';
|
||||
felement.style.display = 'inline';
|
||||
felement.focus();
|
||||
}
|
||||
|
||||
function shadow_submitter(felement, evnt, func) {
|
||||
keycode = 0;
|
||||
if (window.event) {
|
||||
keycode = window.event.keyCode;
|
||||
} else if (evnt) {
|
||||
keycode = evnt.which;
|
||||
}
|
||||
if (keycode == 13) {
|
||||
func(felement);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function shadow_escaper(felement, evnt) {
|
||||
keycode = 0;
|
||||
if (window.event) {
|
||||
keycode = window.event.keyCode;
|
||||
} else if (evnt) {
|
||||
keycode = evnt.which;
|
||||
}
|
||||
if (keycode == 27) {
|
||||
felement.style.display = 'none';
|
||||
document.getElementById('d' + felement.id.substring(1)).style.display = 'inline';
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function creds_clicked() {
|
||||
creds_area = document.getElementById('creds_area');
|
||||
if (creds_logged_in) {
|
||||
creds_user = '';
|
||||
creds_key = '';
|
||||
creds_area.innerHTML = 'User: <input id="creds_user" type="text" size="10" /> Key: <input id="creds_key" type="password" size="10" onkeypress="return creds_submitter(event)" />';
|
||||
document.getElementById('swauth_area').innerHTML = '';
|
||||
creds_logged_in = false;
|
||||
document.getElementById("creds_user").focus();
|
||||
} else {
|
||||
creds_user = document.getElementById('creds_user').value;
|
||||
creds_key = document.getElementById('creds_key').value;
|
||||
creds_area.innerHTML = '<div>Logged in as ' + creds_user + ' <span id="logout" onclick="creds_clicked()">Logout</span></div>';
|
||||
creds_logged_in = true;
|
||||
swauth_area_load();
|
||||
}
|
||||
}
|
||||
|
||||
function creds_submitter(e) {
|
||||
keycode = 0;
|
||||
if (window.event) {
|
||||
keycode = window.event.keyCode;
|
||||
} else if (e) {
|
||||
keycode = e.which;
|
||||
}
|
||||
if (keycode == 13) {
|
||||
creds_clicked();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function swauth_area_reset() {
|
||||
account_area_reset();
|
||||
document.getElementById('swauth_area').innerHTML = '';
|
||||
}
|
||||
|
||||
function account_area_reset() {
|
||||
user_area_reset();
|
||||
element = document.getElementById('add_account')
|
||||
if (element) {
|
||||
element.style.background = 'none';
|
||||
}
|
||||
if (account_selection != -1) {
|
||||
document.getElementById('account_' + account_selection).style.background = 'none';
|
||||
}
|
||||
account = '';
|
||||
account_selection = -1;
|
||||
document.getElementById('account_area').innerHTML = '';
|
||||
}
|
||||
|
||||
function user_area_reset() {
|
||||
element = document.getElementById('add_user')
|
||||
if (element) {
|
||||
element.style.background = 'none';
|
||||
}
|
||||
if (user_selection != -1) {
|
||||
document.getElementById('user_' + user_selection).style.background = 'none';
|
||||
}
|
||||
user = '';
|
||||
user_selection = -1;
|
||||
document.getElementById('user_area').innerHTML = '';
|
||||
}
|
||||
|
||||
function swauth_area_load() {
|
||||
swauth_area_reset();
|
||||
request = new XMLHttpRequest();
|
||||
request.onreadystatechange = swauth_area_load2;
|
||||
request.open('GET', '/auth/v2/', true);
|
||||
request.setRequestHeader('X-Auth-Admin-User', creds_user);
|
||||
request.setRequestHeader('X-Auth-Admin-Key', creds_key);
|
||||
request.send();
|
||||
}
|
||||
|
||||
function swauth_area_load2() {
|
||||
if (request.readyState == 4) {
|
||||
swauth_area = document.getElementById('swauth_area');
|
||||
if (request.status >= 200 && request.status <= 299) {
|
||||
data = JSON.parse(request.responseText);
|
||||
content = '<table><tr><td>Accounts <span id="refresh_accounts" onclick="swauth_area_load()">Refresh</span> <span id="add_account" onclick="add_account()">Add</span></td></tr>';
|
||||
for (ix = 0; ix < data.accounts.length; ix++) {
|
||||
content += '<tr><td id="account_' + ix + '" onclick="account_area_load(' + ix + ')" class="account">' + data.accounts[ix].name + '</td></tr>';
|
||||
}
|
||||
content += '</table>';
|
||||
swauth_area.innerHTML = content;
|
||||
} else {
|
||||
swauth_area.innerHTML = 'Server returned status: ' + request.status + ' ' + request.statusText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function add_account() {
|
||||
account_area_reset();
|
||||
document.getElementById('add_account').style.background = swauth_area_selected_background;
|
||||
account_area = document.getElementById('account_area');
|
||||
account_area.innerHTML = '<table><tr><td id="add_account_title" colspan="2">New Account</td></tr><tr><td class="add_account_heading">Name</td><td><input id="add_account_name" type="text" size="20" /></td></tr><tr><td class="add_account_heading">Suffix</td><td><input id="add_account_suffix" type="text" size="20" /> (Optional)</td></tr><tr><td id="add_account_cancel" onclick="swauth_area_load()">Cancel</td><td id="add_account_save" onclick="add_account_save()">Add</td></tr></table>';
|
||||
bounds = get_bounds(document.getElementById('add_account'));
|
||||
account_area.style.position = 'absolute';
|
||||
account_area.style.top = bounds.top;
|
||||
account_area.style.left = bounds.left + bounds.width;
|
||||
document.getElementById("add_account_name").focus();
|
||||
}
|
||||
|
||||
function add_account_save() {
|
||||
request = new XMLHttpRequest();
|
||||
request.onreadystatechange = add_account_save2;
|
||||
request.open('PUT', '/auth/v2/' + document.getElementById('add_account_name').value, true);
|
||||
request.setRequestHeader('X-Auth-Admin-User', creds_user);
|
||||
request.setRequestHeader('X-Auth-Admin-Key', creds_key);
|
||||
request.setRequestHeader('X-Account-Suffix', document.getElementById('add_account_suffix').value);
|
||||
request.send();
|
||||
}
|
||||
|
||||
function add_account_save2() {
|
||||
if (request.readyState == 4) {
|
||||
if (request.status >= 200 && request.status <= 299) {
|
||||
swauth_area_load();
|
||||
} else {
|
||||
alert('Server returned status: ' + request.status + ' ' + request.statusText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function account_area_load(account_index) {
|
||||
account_area_reset();
|
||||
account_element = document.getElementById('account_' + account_index);
|
||||
account_element.style.background = swauth_area_selected_background;
|
||||
account_selection = account_index;
|
||||
account = account_element.innerHTML;
|
||||
request = new XMLHttpRequest();
|
||||
request.onreadystatechange = account_area_load2;
|
||||
request.open('GET', '/auth/v2/' + account, true);
|
||||
request.setRequestHeader('X-Auth-Admin-User', creds_user);
|
||||
request.setRequestHeader('X-Auth-Admin-Key', creds_key);
|
||||
request.send();
|
||||
}
|
||||
|
||||
function account_area_load2() {
|
||||
account_area = document.getElementById('account_area');
|
||||
if (request.readyState == 4) {
|
||||
if (request.status >= 200 && request.status <= 299) {
|
||||
data = JSON.parse(request.responseText);
|
||||
content = '<div id="delete_account" onclick="delete_account()">Delete</div><table><tr><td>Account Id</td><td>' + data.account_id + '</td></tr></table><table><tr><td>Services</td></tr>';
|
||||
services = [];
|
||||
for (service in data.services) {
|
||||
services.push(service);
|
||||
}
|
||||
services.sort();
|
||||
for (ix = 0; ix < services.length; ix++) {
|
||||
content += '<tr><td class="service">' + services[ix] + '</td><td class="service_detail"><table>';
|
||||
if (data.services[services[ix]]['default']) {
|
||||
content += '<tr><td>default</td><td><span id="d-' + services[ix] + '" class="shadow_delement" onclick="shadow_edit(this)">' + data.services[services[ix]]['default'] + '</span><input id="f-' + services[ix] + '" class="shadow_felement" type="text" size="40" onkeypress="return shadow_submitter(this, event, endpoint_save)" onkeydown="return shadow_escaper(this, event)" /></td></tr>';
|
||||
}
|
||||
endpoints = [];
|
||||
for (name in data.services[services[ix]]) {
|
||||
if (name != 'default') {
|
||||
endpoints.push(name);
|
||||
}
|
||||
}
|
||||
endpoints.sort();
|
||||
for (iy = 0; iy < endpoints.length; iy++) {
|
||||
content += '<tr><td>' + endpoints[iy] + '</td><td><span id="d' + iy + '-' + services[ix] + '" class="shadow_delement" onclick="shadow_edit(this)">' + data.services[services[ix]][endpoints[iy]] + '</span><input id="f' + iy + '-' + services[ix] + '" class="shadow_felement" type="text" size="40" onkeypress="return shadow_submitter(this, event, endpoint_save)" onkeydown="return shadow_escaper(this, event)" /></td></tr>';
|
||||
}
|
||||
content += '</table></td></tr>';
|
||||
}
|
||||
content += '</table><table><tr><td>Users <span id="add_user" onclick="add_user()">Add</span></td></tr>';
|
||||
for (ix = 0; ix < data.users.length; ix++) {
|
||||
content += '<tr><td id="user_' + ix + '" onclick="user_area_load(' + ix + ')" class="user">' + data.users[ix].name + '</td></tr>';
|
||||
}
|
||||
content += '</table>';
|
||||
account_area.innerHTML = content;
|
||||
} else {
|
||||
account_area.innerHTML = 'Server returned status: ' + request.status + ' ' + request.statusText;
|
||||
}
|
||||
bounds = get_bounds(document.getElementById('account_' + account_selection));
|
||||
account_area.style.position = 'absolute';
|
||||
account_area.style.top = bounds.top;
|
||||
account_area.style.left = bounds.left + bounds.width;
|
||||
}
|
||||
}
|
||||
|
||||
function endpoint_save(field) {
|
||||
service = field.id.substring(field.id.indexOf('-') + 1)
|
||||
index = field.id.substring(1, field.id.indexOf('-'))
|
||||
if (index) {
|
||||
endpoint = endpoints[index];
|
||||
} else {
|
||||
endpoint = 'default';
|
||||
}
|
||||
services = {};
|
||||
services[service] = {};
|
||||
services[service][endpoint] = field.value;
|
||||
request = new XMLHttpRequest();
|
||||
request.onreadystatechange = endpoint_save2;
|
||||
request.open('POST', '/auth/v2/' + account + '/.services', true);
|
||||
request.setRequestHeader('X-Auth-Admin-User', creds_user);
|
||||
request.setRequestHeader('X-Auth-Admin-Key', creds_key);
|
||||
request.send(JSON.stringify(services));
|
||||
}
|
||||
|
||||
function endpoint_save2() {
|
||||
if (request.readyState == 4) {
|
||||
if (request.status >= 200 && request.status <= 299) {
|
||||
account_area_load(account_selection);
|
||||
} else {
|
||||
alert('Server returned status: ' + request.status + ' ' + request.statusText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function add_user() {
|
||||
user_area_reset();
|
||||
document.getElementById('add_user').style.background = account_area_selected_background;
|
||||
user_area = document.getElementById('user_area');
|
||||
user_area.innerHTML = '<table><tr><td id="add_user_title" colspan="2">New User</td></tr><tr><td class="add_user_heading">Name</td><td><input id="add_user_name" type="text" size="20" /></td></tr><tr><td class="add_user_heading">Auth Key</td><td><input id="add_user_key" type="password" size="20" /></td></tr><tr><td class="add_user_heading">Account Admin</td><td><input id="add_user_admin" type="checkbox" /></td></tr><tr><td class="add_user_heading">Reseller Admin</td><td><input id="add_user_reseller_admin" type="checkbox" /></td></tr><tr><td id="add_user_cancel" onclick="add_user_cancel()">Cancel</td><td id="add_user_save" onclick="add_user_save()">Add</td></tr></table>';
|
||||
bounds = get_bounds(document.getElementById('add_user'));
|
||||
user_area.style.position = 'absolute';
|
||||
user_area.style.top = bounds.top;
|
||||
user_area.style.left = bounds.left + bounds.width;
|
||||
document.getElementById("add_user_name").focus();
|
||||
}
|
||||
|
||||
function add_user_cancel() {
|
||||
document.getElementById('add_user').style.background = 'none';
|
||||
document.getElementById('user_area').innerHTML = '';
|
||||
}
|
||||
|
||||
function add_user_save() {
|
||||
request = new XMLHttpRequest();
|
||||
request.onreadystatechange = add_user_save2;
|
||||
request.open('PUT', '/auth/v2/' + account + '/' + document.getElementById('add_user_name').value, true);
|
||||
request.setRequestHeader('X-Auth-Admin-User', creds_user);
|
||||
request.setRequestHeader('X-Auth-Admin-Key', creds_key);
|
||||
request.setRequestHeader('X-Auth-User-Key', document.getElementById('add_user_key').value);
|
||||
if (document.getElementById('add_user_admin').value) {
|
||||
request.setRequestHeader('X-Auth-User-Admin', 'true');
|
||||
}
|
||||
if (document.getElementById('add_user_reseller_admin').value) {
|
||||
request.setRequestHeader('X-Auth-User-Reseller-Admin', 'true');
|
||||
}
|
||||
request.send();
|
||||
}
|
||||
|
||||
function add_user_save2() {
|
||||
if (request.readyState == 4) {
|
||||
if (request.status >= 200 && request.status <= 299) {
|
||||
account_area_load(account_selection);
|
||||
} else {
|
||||
alert('Server returned status: ' + request.status + ' ' + request.statusText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function delete_account() {
|
||||
request = new XMLHttpRequest();
|
||||
request.onreadystatechange = delete_account2;
|
||||
request.open('DELETE', '/auth/v2/' + account, true);
|
||||
request.setRequestHeader('X-Auth-Admin-User', creds_user);
|
||||
request.setRequestHeader('X-Auth-Admin-Key', creds_key);
|
||||
request.send();
|
||||
}
|
||||
|
||||
function delete_account2() {
|
||||
if (request.readyState == 4) {
|
||||
if (request.status >= 200 && request.status <= 299) {
|
||||
swauth_area_load();
|
||||
} else {
|
||||
alert('Server returned status: ' + request.status + ' ' + request.statusText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function user_area_load(account_area_user_index) {
|
||||
user_area_reset();
|
||||
user_element = document.getElementById('user_' + account_area_user_index);
|
||||
user_element.style.background = account_area_selected_background;
|
||||
user_selection = account_area_user_index;
|
||||
user = user_element.innerHTML;
|
||||
request = new XMLHttpRequest();
|
||||
request.onreadystatechange = user_area_load2;
|
||||
request.open('GET', '/auth/v2/' + account + '/' + user, true);
|
||||
request.setRequestHeader('X-Auth-Admin-User', creds_user);
|
||||
request.setRequestHeader('X-Auth-Admin-Key', creds_key);
|
||||
request.send();
|
||||
}
|
||||
|
||||
function user_area_load2() {
|
||||
user_area = document.getElementById('user_area');
|
||||
if (request.readyState == 4) {
|
||||
if (request.status >= 200 && request.status <= 299) {
|
||||
data = JSON.parse(request.responseText);
|
||||
content = '<div id="delete_user" onclick="delete_user()">Delete</div><table><tr><td>Auth</td><td><span id="auth_toggler" onclick="auth_toggle()">Show</span> <span id="auth_view">' + data.auth + '</span></td><td><input id="auth_update_field" type="password" size="20" onkeypress="return auth_submitter(event)" onkeydown="return auth_escaper(event)" /> <span id="auth_update" onclick="auth_update()">Update</span></td></tr></table><table><tr><td>Groups</td></tr>';
|
||||
groups = [];
|
||||
for (ix = 0; ix < data.groups.length; ix++) {
|
||||
groups.push(data.groups[ix].name);
|
||||
}
|
||||
groups.sort();
|
||||
for (ix = 0; ix < groups.length; ix++) {
|
||||
content += '<tr><td class="group">' + groups[ix] + '</td></tr>';
|
||||
}
|
||||
content += '</table>';
|
||||
user_area.innerHTML = content;
|
||||
} else {
|
||||
user_area.innerHTML = 'Server returned status: ' + request.status + ' ' + request.statusText;
|
||||
}
|
||||
bounds = get_bounds(document.getElementById('user_' + user_selection));
|
||||
user_area.style.position = 'absolute';
|
||||
user_area.style.top = bounds.top;
|
||||
user_area.style.left = bounds.left + bounds.width;
|
||||
}
|
||||
}
|
||||
|
||||
function delete_user() {
|
||||
request = new XMLHttpRequest();
|
||||
request.onreadystatechange = delete_user2;
|
||||
request.open('DELETE', '/auth/v2/' + account + '/' + user, true);
|
||||
request.setRequestHeader('X-Auth-Admin-User', creds_user);
|
||||
request.setRequestHeader('X-Auth-Admin-Key', creds_key);
|
||||
request.send();
|
||||
}
|
||||
|
||||
function delete_user2() {
|
||||
if (request.readyState == 4) {
|
||||
if (request.status >= 200 && request.status <= 299) {
|
||||
account_area_load(account_selection);
|
||||
} else {
|
||||
alert('Server returned status: ' + request.status + ' ' + request.statusText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function auth_toggle() {
|
||||
to_toggle = document.getElementById('auth_view');
|
||||
toggler = document.getElementById('auth_toggler');
|
||||
if (to_toggle.style.display && to_toggle.style.display != 'none') {
|
||||
toggler.innerHTML = 'Show';
|
||||
to_toggle.style.display = 'none';
|
||||
} else {
|
||||
toggler.innerHTML = 'Hide';
|
||||
to_toggle.style.display = 'inline';
|
||||
}
|
||||
}
|
||||
|
||||
function auth_update() {
|
||||
field = document.getElementById('auth_update_field');
|
||||
trigger = document.getElementById('auth_update');
|
||||
if (field.style.display && field.style.display != 'none') {
|
||||
auth_save();
|
||||
} else {
|
||||
field.style.display = 'inline';
|
||||
trigger.style.display = 'none';
|
||||
field.focus();
|
||||
}
|
||||
}
|
||||
|
||||
function auth_submitter(e) {
|
||||
keycode = 0;
|
||||
if (window.event) {
|
||||
keycode = window.event.keyCode;
|
||||
} else if (e) {
|
||||
keycode = e.which;
|
||||
}
|
||||
if (keycode == 13) {
|
||||
auth_save();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function auth_escaper(e) {
|
||||
keycode = 0;
|
||||
if (window.event) {
|
||||
keycode = window.event.keyCode;
|
||||
} else if (e) {
|
||||
keycode = e.which;
|
||||
}
|
||||
if (keycode == 27) {
|
||||
field = document.getElementById('auth_update_field');
|
||||
field.value = '';
|
||||
field.style.display ='none';
|
||||
document.getElementById('auth_update').style.display ='inline';
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function auth_save() {
|
||||
document.getElementById('auth_update_field').style.display ='none';
|
||||
if (document.getElementById('auth_update_field').value) {
|
||||
request = new XMLHttpRequest();
|
||||
request.onreadystatechange = auth_save2;
|
||||
request.open('GET', '/auth/v2/' + account + '/' + user, true);
|
||||
request.setRequestHeader('X-Auth-Admin-User', creds_user);
|
||||
request.setRequestHeader('X-Auth-Admin-Key', creds_key);
|
||||
request.send();
|
||||
}
|
||||
}
|
||||
|
||||
function auth_save2() {
|
||||
if (request.readyState == 4) {
|
||||
if (request.status >= 200 && request.status <= 299) {
|
||||
data = JSON.parse(request.responseText);
|
||||
request = new XMLHttpRequest();
|
||||
request.onreadystatechange = auth_save3;
|
||||
request.open('PUT', '/auth/v2/' + account_element.innerHTML + '/' + user_element.innerHTML, true);
|
||||
request.setRequestHeader('X-Auth-Admin-User', creds_user);
|
||||
request.setRequestHeader('X-Auth-Admin-Key', creds_key);
|
||||
request.setRequestHeader('X-Auth-User-Key', document.getElementById('auth_update_field').value);
|
||||
admin = false;
|
||||
reseller_admin = false;
|
||||
for (ix = 0; ix < data.groups.length; ix++) {
|
||||
if (data.groups[ix].name == '.admin') {
|
||||
admin = true;
|
||||
} else if (data.groups[ix].name == '.reseller_admin') {
|
||||
reseller_admin = true;
|
||||
}
|
||||
}
|
||||
if (admin) {
|
||||
request.setRequestHeader('X-Auth-User-Admin', 'true');
|
||||
}
|
||||
if (reseller_admin) {
|
||||
request.setRequestHeader('X-Auth-User-Reseller-Admin', 'true');
|
||||
}
|
||||
request.send();
|
||||
} else {
|
||||
alert('Server returned status: ' + request.status + ' ' + request.statusText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function auth_save3() {
|
||||
if (request.readyState == 4) {
|
||||
if (request.status >= 200 && request.status <= 299) {
|
||||
user_area_load(user_selection);
|
||||
} else {
|
||||
alert('Server returned status: ' + request.status + ' ' + request.statusText);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="creds_clicked()">
|
||||
<form onsubmit="return false">
|
||||
<div id="creds_area"></div>
|
||||
<div id="swauth">Swauth</div>
|
||||
<div id="swauth_area"></div>
|
||||
<div id="account_area"></div>
|
||||
<div id="user_area"></div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user