summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPrashanth Pai <ppai@redhat.com>2013-12-26 14:24:19 +0530
committerLuis Pabon <lpabon@redhat.com>2014-01-10 07:45:51 -0800
commitf952c756ad024e100953a43b1f297f82b5c8f3e2 (patch)
treef30f155a1fd1c1929370af1094cb83567b5aed81
parent2f9e3120bbd7ef6b7459fccb5b740b6542b13c57 (diff)
Return X-Storage-Url in passive mode
When auth_mode is set to 'passive', client can authenticate itself using account, user and key. This enables swiftkerbauth to return X-Storage-Url response header to client. X-Storage-Url contains account name provided in the request. This required a change in X-Storage-User header format from X-Storage-User: user to X-Storage-User: account:user This makes swiftkerbauth(passive mode) handle_get_token APIs to be more consistent with that of swauth and tempauth. Change-Id: Ic1d1520bb8afbc80cca443d92d659436f2f7cd0e Signed-off-by: Prashanth Pai <ppai@redhat.com> Reviewed-on: http://review.gluster.org/6595 Reviewed-by: Chetan Risbud <crisbud@redhat.com> Tested-by: Chetan Risbud <crisbud@redhat.com>
-rwxr-xr-xapachekerbauth/var/www/cgi-bin/swift-auth4
-rw-r--r--doc/AD_server.md12
-rw-r--r--doc/ipa_server.md12
-rw-r--r--doc/swiftkerbauth_guide.md6
-rw-r--r--swiftkerbauth/kerbauth.py98
-rw-r--r--swiftkerbauth/kerbauth_utils.py2
-rw-r--r--test/unit/test_kerbauth.py83
-rw-r--r--test/unit/test_kerbauth_utils.py8
8 files changed, 179 insertions, 46 deletions
diff --git a/apachekerbauth/var/www/cgi-bin/swift-auth b/apachekerbauth/var/www/cgi-bin/swift-auth
index 45df45c..11fe0e2 100755
--- a/apachekerbauth/var/www/cgi-bin/swift-auth
+++ b/apachekerbauth/var/www/cgi-bin/swift-auth
@@ -24,7 +24,7 @@ from swift.common.memcached import MemcacheRing
from time import time, ctime
from swiftkerbauth import MEMCACHE_SERVERS, TOKEN_LIFE, DEBUG_HEADERS
from swiftkerbauth.kerbauth_utils import get_remote_user, get_auth_data, \
- generate_token, set_auth_data, get_groups
+ generate_token, set_auth_data, get_groups_from_username
def main():
@@ -48,7 +48,7 @@ def main():
if not token:
token = generate_token()
expires = time() + TOKEN_LIFE
- groups = get_groups(username)
+ groups = get_groups_from_username(username)
set_auth_data(mc, username, token, expires, groups)
print "X-Auth-Token: %s" % token
diff --git a/doc/AD_server.md b/doc/AD_server.md
index c34f0f1..66d90f2 100644
--- a/doc/AD_server.md
+++ b/doc/AD_server.md
@@ -98,6 +98,18 @@ On client:
<a name="users-groups" />
###Adding users and groups
+The following convention is to be followed in creating group names:
+
+ <reseller-prefix>\_<volume-name>
+
+ <reseller-prefix>\_<account-name>
+
+As of now, account=volume=group
+
+For example:
+
+ AUTH\_test
+
Adding groups and users to the Windows domain is easy task.
- Start -> Administrative Tools -> Active Directory Users & Computers
diff --git a/doc/ipa_server.md b/doc/ipa_server.md
index ef12b53..55e654e 100644
--- a/doc/ipa_server.md
+++ b/doc/ipa_server.md
@@ -107,6 +107,18 @@ Check if reverse resolution works :
<a name="users-groups" />
## Adding users and groups
+The following convention is to be followed in creating group names:
+
+ <reseller-prefix>\_<volume-name>
+
+ <reseller-prefix>\_<account-name>
+
+As of now, account=volume=group
+
+For example:
+
+ AUTH\_test
+
Create *auth_reseller_admin* user group
> ipa group-add auth_reseller_admin --desc="Full access to all Swift accounts"
diff --git a/doc/swiftkerbauth_guide.md b/doc/swiftkerbauth_guide.md
index e18c7ef..12845a6 100644
--- a/doc/swiftkerbauth_guide.md
+++ b/doc/swiftkerbauth_guide.md
@@ -103,6 +103,7 @@ Edit */etc/swift/proxy-server.conf* and add a new filter section as follows:
[filter:kerbauth]
use = egg:swiftkerbauth#kerbauth
ext_authentication_url = http://client.rhelbox.com/cgi-bin/swift-auth
+ auth_mode=passive
Add kerbauth to pipeline
@@ -438,8 +439,9 @@ The --negotiate option is for curl to perform Kerberos authentication and
#### Get an authentication token when auth_mode=passive:
-> curl -v -H 'X-Auth-User: auth_admin' -H 'X-Auth-Key: Redhat*123' http://127.0.0.1:8080/auth/v1.0
+> curl -v -H 'X-Auth-User: test:auth_admin' -H 'X-Auth-Key: Redhat*123' http://127.0.0.1:8080/auth/v1.0
+**NOTE**: X-Storage-Url response header can be returned only in passive mode.
<a name="config-swiftkerbauth" />
##Configurable Parameters
@@ -481,7 +483,7 @@ Set this to **"passive"** when you want to allow access to clients residing
outside the domain. In this mode, authentication is performed by gleaning
username and password from request headers (X-Auth-User and X-Auth-Key) and
running kinit command against it.
-Default value: active
+Default value: passive
#### realm_name
This is applicable only when the auth_method=passive. This option specifies
diff --git a/swiftkerbauth/kerbauth.py b/swiftkerbauth/kerbauth.py
index c51dc14..c8b51fb 100644
--- a/swiftkerbauth/kerbauth.py
+++ b/swiftkerbauth/kerbauth.py
@@ -16,6 +16,7 @@ import errno
from time import time, ctime
from traceback import format_exc
from eventlet import Timeout
+from urllib import unquote
from swift.common.swob import Request, Response
from swift.common.swob import HTTPBadRequest, HTTPForbidden, HTTPNotFound, \
@@ -26,9 +27,7 @@ from swift.common.utils import cache_from_env, get_logger, \
split_path, config_true_value
from swiftkerbauth.kerbauth_utils import get_auth_data, generate_token, \
- set_auth_data, run_kinit
-from swiftkerbauth.kerbauth_utils import get_groups as \
- get_groups_from_username
+ set_auth_data, run_kinit, get_groups_from_username
class KerbAuth(object):
@@ -77,7 +76,7 @@ class KerbAuth(object):
if self.auth_prefix[-1] != '/':
self.auth_prefix += '/'
self.token_life = int(conf.get('token_life', 86400))
- self.auth_method = conf.get('auth_method', 'active')
+ self.auth_method = conf.get('auth_method', 'passive')
self.debug_headers = config_true_value(
conf.get('debug_headers', 'yes'))
self.realm_name = conf.get('realm_name', None)
@@ -309,16 +308,37 @@ class KerbAuth(object):
"""
Handles the various `request for token and service end point(s)` calls.
There are various formats to support the various auth servers in the
- past. Examples::
+ past.
+
+ "Active Mode" usage:
+ All formats require GSS (Kerberos) authentication.
GET <auth-prefix>/v1/<act>/auth
GET <auth-prefix>/auth
GET <auth-prefix>/v1.0
- All formats require GSS (Kerberos) authentication.
+ On successful authentication, the response will have X-Auth-Token
+ and X-Storage-Token set to the token to use with Swift.
+
+ "Passive Mode" usage::
- On successful authentication, the response will have X-Auth-Token
- set to the token to use with Swift.
+ GET <auth-prefix>/v1/<act>/auth
+ X-Auth-User: <act>:<usr> or X-Storage-User: <usr>
+ X-Auth-Key: <key> or X-Storage-Pass: <key>
+ GET <auth-prefix>/auth
+ X-Auth-User: <act>:<usr> or X-Storage-User: <act>:<usr>
+ X-Auth-Key: <key> or X-Storage-Pass: <key>
+ GET <auth-prefix>/v1.0
+ X-Auth-User: <act>:<usr> or X-Storage-User: <act>:<usr>
+ X-Auth-Key: <key> or X-Storage-Pass: <key>
+
+ Values should be url encoded, "act%3Ausr" instead of "act:usr" for
+ example; however, for backwards compatibility the colon may be
+ included unencoded.
+
+ On successful authentication, the response will have X-Auth-Token
+ and X-Storage-Token set to the token to use with Swift and
+ X-Storage-URL set to the URL to the default Swift cluster to use.
:param req: The swob.Request to process.
:returns: swob.Response, 2xx on success with data set as explained
@@ -340,21 +360,41 @@ class KerbAuth(object):
# Client is outside the domain
elif self.auth_method == "passive":
- user = None
- key = None
- # Extract username and password from request
- user = req.headers.get('x-storage-user')
- if not user:
- user = req.headers.get('x-auth-user')
- key = req.headers.get('x-storage-pass')
- if not key:
- key = req.headers.get('x-auth-key')
-
- if (not user) and (not key):
- # If both are not given, client may be part of the domain
+ account, user, key = None, None, None
+ # Extract user, account and key from request
+ if pathsegs[0] == 'v1' and pathsegs[2] == 'auth':
+ account = pathsegs[1]
+ user = req.headers.get('x-storage-user')
+ if not user:
+ user = unquote(req.headers.get('x-auth-user', ''))
+ if user:
+ if ':' not in user:
+ return HTTPUnauthorized(request=req)
+ else:
+ account2, user = user.split(':', 1)
+ if account != account2:
+ return HTTPUnauthorized(request=req)
+ key = req.headers.get('x-storage-pass')
+ if not key:
+ key = unquote(req.headers.get('x-auth-key', ''))
+ elif pathsegs[0] in ('auth', 'v1.0'):
+ user = unquote(req.headers.get('x-auth-user', ''))
+ if not user:
+ user = req.headers.get('x-storage-user')
+ if user:
+ if ':' not in user:
+ return HTTPUnauthorized(request=req)
+ else:
+ account, user = user.split(':', 1)
+ key = unquote(req.headers.get('x-auth-key', ''))
+ if not key:
+ key = req.headers.get('x-storage-pass')
+
+ if not (account or user or key):
+ # If all are not given, client may be part of the domain
return HTTPSeeOther(location=self.ext_authentication_url)
- elif None in (key, user):
- # If either user OR key is given, but not both
+ elif None in (key, user, account):
+ # If only one or two of them is given, but not all
return HTTPUnauthorized(request=req)
# Run kinit on the user
@@ -372,6 +412,18 @@ class KerbAuth(object):
if "@" in user:
user = user.split("@")[0]
+
+ # Check if user really belongs to the account
+ groups_list = get_groups_from_username(user).strip().split(",")
+ user_group = ("%s%s" % (self.reseller_prefix, account)).lower()
+ reseller_admin_group = \
+ ("%sreseller_admin" % self.reseller_prefix).lower()
+ if user_group not in groups_list:
+ # Check if user is reseller_admin. If not, return Unauthorized.
+ # On AD/IdM server, auth_reseller_admin is a separate group
+ if reseller_admin_group not in groups_list:
+ return HTTPUnauthorized(request=req)
+
mc = cache_from_env(req.environ)
if not mc:
raise Exception('Memcache required')
@@ -392,6 +444,8 @@ class KerbAuth(object):
'X-Debug-Token-Expires': ctime(expires)})
resp = Response(request=req, headers=headers)
+ resp.headers['X-Storage-Url'] = \
+ '%s/v1/%s%s' % (resp.host_url, self.reseller_prefix, account)
return resp
diff --git a/swiftkerbauth/kerbauth_utils.py b/swiftkerbauth/kerbauth_utils.py
index 8490d83..683cdea 100644
--- a/swiftkerbauth/kerbauth_utils.py
+++ b/swiftkerbauth/kerbauth_utils.py
@@ -81,7 +81,7 @@ def generate_token():
return token
-def get_groups(username):
+def get_groups_from_username(username):
"""Return a set of groups to which the user belongs to."""
# Retrieve the numerical group IDs. We cannot list the group names
# because group names from Active Directory may contain spaces, and
diff --git a/test/unit/test_kerbauth.py b/test/unit/test_kerbauth.py
index 471ff58..207558f 100644
--- a/test/unit/test_kerbauth.py
+++ b/test/unit/test_kerbauth.py
@@ -80,7 +80,8 @@ class TestAuth(unittest.TestCase):
patch_filter_factory()
def setUp(self):
- self.test_auth = auth.filter_factory({})(FakeApp())
+ self.test_auth = \
+ auth.filter_factory({'auth_method': 'active'})(FakeApp())
self.test_auth_passive = \
auth.filter_factory({'auth_method': 'passive'})(FakeApp())
@@ -273,13 +274,46 @@ class TestAuth(unittest.TestCase):
self.assertEquals(resp.status_int, REDIRECT_STATUS)
#User given but no key
req = self._make_request('/auth/v1.0',
- headers={'X-Auth-User': 'blah'})
+ headers={'X-Auth-User': 'test:user'})
+ resp = self.test_auth_passive.handle_get_token(req)
+ self.assertEquals(resp.status_int, 401)
+
+ def test_passive_handle_get_token_account_in_req_path(self):
+ req = self._make_request('/v1/test/auth',
+ headers={'X-Auth-User': 'test:user',
+ 'X-Auth-Key': 'password'})
+ _mock_run_kinit = Mock(return_value=0)
+ _mock_get_groups = Mock(return_value="user,auth_test")
+ with patch('swiftkerbauth.kerbauth.run_kinit', _mock_run_kinit):
+ with patch('swiftkerbauth.kerbauth.get_groups_from_username',
+ _mock_get_groups):
+ resp = self.test_auth_passive.handle_get_token(req)
+ _mock_run_kinit.assert_called_once_with('user', 'password')
+ self.assertEquals(_mock_get_groups.call_count, 2)
+ self.assertEquals(resp.status_int, 200)
+ self.assertIsNotNone(resp.headers['X-Auth-Token'])
+ self.assertIsNotNone(resp.headers['X-Storage-Token'])
+ self.assertIsNotNone(resp.headers['X-Storage-Url'])
+
+ def test_passive_handle_get_token_user_invalid_or_no__account(self):
+ #X-Auth-User not in acc:user format
+ req = self._make_request('/auth/v1.0',
+ headers={'X-Auth-User': 'user'})
+ resp = self.test_auth_passive.handle_get_token(req)
+ self.assertEquals(resp.status_int, 401)
+ req = self._make_request('/v1/test/auth',
+ headers={'X-Auth-User': 'user'})
+ resp = self.test_auth_passive.handle_get_token(req)
+ self.assertEquals(resp.status_int, 401)
+ # Account name mismatch
+ req = self._make_request('/v1/test/auth',
+ headers={'X-Auth-User': 'wrongacc:user'})
resp = self.test_auth_passive.handle_get_token(req)
self.assertEquals(resp.status_int, 401)
def test_passive_handle_get_token_no_kinit(self):
req = self._make_request('/auth/v1.0',
- headers={'X-Auth-User': 'user',
+ headers={'X-Auth-User': 'test:user',
'X-Auth-Key': 'password'})
_mock_run_kinit = Mock(side_effect=OSError(errno.ENOENT,
os.strerror(errno.ENOENT)))
@@ -291,7 +325,7 @@ class TestAuth(unittest.TestCase):
def test_passive_handle_get_token_kinit_fail(self):
req = self._make_request('/auth/v1.0',
- headers={'X-Auth-User': 'user',
+ headers={'X-Auth-User': 'test:user',
'X-Auth-Key': 'password'})
_mock_run_kinit = Mock(return_value=1)
with patch('swiftkerbauth.kerbauth.run_kinit', _mock_run_kinit):
@@ -301,38 +335,57 @@ class TestAuth(unittest.TestCase):
def test_passive_handle_get_token_kinit_success_token_not_present(self):
req = self._make_request('/auth/v1.0',
- headers={'X-Auth-User': 'user',
+ headers={'X-Auth-User': 'test:user',
'X-Auth-Key': 'password'})
_mock_run_kinit = Mock(return_value=0)
- _mock_get_groups = Mock(return_value="user,admins")
+ _mock_get_groups = Mock(return_value="user,auth_test")
with patch('swiftkerbauth.kerbauth.run_kinit', _mock_run_kinit):
with patch('swiftkerbauth.kerbauth.get_groups_from_username',
_mock_get_groups):
resp = self.test_auth_passive.handle_get_token(req)
- _mock_run_kinit.assert_called_once_with('user', 'password')
_mock_run_kinit.assert_called_once_with('user', 'password')
- _mock_get_groups.assert_called_once_with('user')
+ self.assertEquals(_mock_get_groups.call_count, 2)
self.assertEquals(resp.status_int, 200)
self.assertIsNotNone(resp.headers['X-Auth-Token'])
self.assertIsNotNone(resp.headers['X-Storage-Token'])
+ self.assertIsNotNone(resp.headers['X-Storage-Url'])
def test_passive_handle_get_token_kinit_realm_and_memcache(self):
req = self._make_request('/auth/v1.0',
- headers={'X-Auth-User': 'user',
+ headers={'X-Auth-User': 'test:user',
'X-Auth-Key': 'password'})
req.environ['swift.cache'] = None
_auth_passive = \
auth.filter_factory({'auth_method': 'passive',
'realm_name': 'EXAMPLE.COM'})(FakeApp())
_mock_run_kinit = Mock(return_value=0)
+ _mock_get_groups = Mock(return_value="user,auth_test")
with patch('swiftkerbauth.kerbauth.run_kinit', _mock_run_kinit):
- try:
- _auth_passive.handle_get_token(req)
- except Exception as e:
- self.assertTrue(e.args[0].startswith("Memcache required"))
- else:
- self.fail("Expected Exception - Memcache required")
+ with patch('swiftkerbauth.kerbauth.get_groups_from_username',
+ _mock_get_groups):
+ try:
+ _auth_passive.handle_get_token(req)
+ except Exception as e:
+ self.assertTrue(e.args[0].startswith("Memcache "
+ "required"))
+ else:
+ self.fail("Expected Exception - Memcache required")
_mock_run_kinit.assert_called_once_with('user@EXAMPLE.COM', 'password')
+ _mock_get_groups.assert_called_once_with('user')
+
+ def test_passive_handle_get_token_user_in_any__account(self):
+ req = self._make_request('/auth/v1.0',
+ headers={'X-Auth-User': 'test:user',
+ 'X-Auth-Key': 'password'})
+ _mock_run_kinit = Mock(return_value=0)
+ _mock_get_groups = Mock(return_value="user,auth_blah")
+ with patch('swiftkerbauth.kerbauth.run_kinit', _mock_run_kinit):
+ with patch('swiftkerbauth.kerbauth.get_groups_from_username',
+ _mock_get_groups):
+ resp = self.test_auth_passive.handle_get_token(req)
+ self.assertEquals(resp.status_int, 401)
+ _mock_run_kinit.assert_called_once_with('user', 'password')
+ _mock_get_groups.assert_called_once_with('user')
def test_handle(self):
req = self._make_request('/auth/v1.0')
diff --git a/test/unit/test_kerbauth_utils.py b/test/unit/test_kerbauth_utils.py
index abf8ad0..cb135cf 100644
--- a/test/unit/test_kerbauth_utils.py
+++ b/test/unit/test_kerbauth_utils.py
@@ -64,13 +64,13 @@ class TestKerbUtils(unittest.TestCase):
matches = re.match('AUTH_tk[a-f0-9]{32}', token)
self.assertIsNotNone(matches)
- def test_get_groups(self):
- groups = ku.get_groups("root")
+ def test_get_groups_from_username(self):
+ groups = ku.get_groups_from_username("root")
self.assertIn("root", groups)
- def test_get_groups_err(self):
+ def test_get_groups_from_username_err(self):
try:
- ku.get_groups("Zroot")
+ ku.get_groups_from_username("Zroot")
except RuntimeError as err:
self.assertTrue(err.args[0].startswith("Failure running id -G"))
else: