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