summaryrefslogtreecommitdiffstats
path: root/gluster/swift/common/middleware/swiftkerbauth/kerbauth.py
diff options
context:
space:
mode:
authorChetan Risbud <crisbud@redhat.com>2014-01-23 12:42:53 +0530
committerChetan Risbud <crisbud@redhat.com>2014-01-27 22:55:55 -0800
commit03128e172e0242eba396e8487bdd8d6b0da52db3 (patch)
tree2f03823a9ea37877cbb9d0687a1ca669466777c3 /gluster/swift/common/middleware/swiftkerbauth/kerbauth.py
parent1f432663badbee97543d92dd68fc196b169a938d (diff)
Rebase swiftkerbauth imported code with upstream
Few changes have been merged to upstream swiftkerbauth repo. This commit brings it down to gluster-swift repo. Bringing below changes to gluster-swift repo in one go. http://review.gluster.org/#/c/6296/ http://review.gluster.org/#/c/6370/ http://review.gluster.org/#/c/6595/ http://review.gluster.org/#/c/6713/ http://review.gluster.org/#/c/6732/ Change-Id: I10dc12d75ec63fca313339fbc71e4f18071af552 Signed-off-by: Chetan Risbud <crisbud@redhat.com> Reviewed-on: http://review.gluster.org/6764 Reviewed-by: Prashanth Pai <ppai@redhat.com>
Diffstat (limited to 'gluster/swift/common/middleware/swiftkerbauth/kerbauth.py')
-rw-r--r--gluster/swift/common/middleware/swiftkerbauth/kerbauth.py157
1 files changed, 146 insertions, 11 deletions
diff --git a/gluster/swift/common/middleware/swiftkerbauth/kerbauth.py b/gluster/swift/common/middleware/swiftkerbauth/kerbauth.py
index a1ba091..1a63a40 100644
--- a/gluster/swift/common/middleware/swiftkerbauth/kerbauth.py
+++ b/gluster/swift/common/middleware/swiftkerbauth/kerbauth.py
@@ -12,17 +12,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from time import time
+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
+from swift.common.swob import Request, Response
from swift.common.swob import HTTPBadRequest, HTTPForbidden, HTTPNotFound, \
- HTTPSeeOther
+ HTTPSeeOther, HTTPUnauthorized, HTTPServerError
from swift.common.middleware.acl import clean_acl, parse_acl, referrer_allowed
from swift.common.utils import cache_from_env, get_logger, \
split_path, config_true_value
+from gluster.swift.common.middleware.swiftkerbauth.kerbauth_utils import \
+ get_auth_data, generate_token, \
+ set_auth_data, run_kinit, get_groups_from_username
class KerbAuth(object):
@@ -71,6 +76,10 @@ 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', 'passive')
+ self.debug_headers = config_true_value(
+ conf.get('debug_headers', 'yes'))
+ self.realm_name = conf.get('realm_name', None)
self.allow_overrides = config_true_value(
conf.get('allow_overrides', 't'))
self.storage_url_scheme = conf.get('storage_url_scheme', 'default')
@@ -109,8 +118,13 @@ class KerbAuth(object):
env['reseller_request'] = True
else:
# Invalid token (may be expired)
- return HTTPSeeOther(
- location=self.ext_authentication_url)(env, start_response)
+ if self.auth_method == "active":
+ return HTTPSeeOther(
+ location=self.ext_authentication_url)(env,
+ start_response)
+ elif self.auth_method == "passive":
+ self.logger.increment('unauthorized')
+ return HTTPUnauthorized()(env, start_response)
else:
# With a non-empty reseller_prefix, I would like to be called
# back for anonymous access to accounts I know I'm the
@@ -234,7 +248,11 @@ class KerbAuth(object):
self.logger.increment('forbidden')
return HTTPForbidden(request=req)
else:
- return HTTPSeeOther(location=self.ext_authentication_url)
+ if self.auth_method == "active":
+ return HTTPSeeOther(location=self.ext_authentication_url)
+ elif self.auth_method == "passive":
+ self.logger.increment('unauthorized')
+ return HTTPUnauthorized(request=req)
def handle(self, env, start_response):
"""
@@ -290,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
@@ -315,7 +354,103 @@ class KerbAuth(object):
or pathsegs[0] in ('auth', 'v1.0')):
return HTTPBadRequest(request=req)
- return HTTPSeeOther(location=self.ext_authentication_url)
+ # Client is inside the domain
+ if self.auth_method == "active":
+ return HTTPSeeOther(location=self.ext_authentication_url)
+
+ # Client is outside the domain
+ elif self.auth_method == "passive":
+ 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, account):
+ # If only one or two of them is given, but not all
+ return HTTPUnauthorized(request=req)
+
+ # Run kinit on the user
+ if self.realm_name and "@" not in user:
+ user = user + "@" + self.realm_name
+ try:
+ ret = run_kinit(user, key)
+ except OSError as e:
+ if e.errno == errno.ENOENT:
+ return HTTPServerError("kinit command not found\n")
+ if ret != 0:
+ self.logger.warning("Failed: kinit %s", user)
+ if ret == -1:
+ self.logger.warning("Failed: kinit: Password has probably "
+ "expired.")
+ return HTTPServerError("Kinit is taking too long.\n")
+ return HTTPUnauthorized(request=req)
+ self.logger.debug("kinit succeeded")
+
+ 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')
+ token, expires, groups = get_auth_data(mc, user)
+ if not token:
+ token = generate_token()
+ expires = time() + self.token_life
+ groups = get_groups_from_username(user)
+ set_auth_data(mc, user, token, expires, groups)
+
+ headers = {'X-Auth-Token': token,
+ 'X-Storage-Token': token}
+
+ if self.debug_headers:
+ headers.update({'X-Debug-Remote-User': user,
+ 'X-Debug-Groups:': groups,
+ 'X-Debug-Token-Life': self.token_life,
+ '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
def filter_factory(global_conf, **local_conf):