def handle_get_token(self, req): """ 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:: 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 set to the token to use with Swift. :param req: The swob.Request to process. :returns: swob.Response, 2xx on success with data set as explained above. """ # Validate the request info try: pathsegs = split_path(req.path_info, 1, 3, True) except ValueError: self.logger.increment('errors') return HTTPNotFound(request=req) if not ((pathsegs[0] == 'v1' and pathsegs[2] == 'auth') or pathsegs[0] in ('auth', 'v1.0')): return HTTPBadRequest(request=req) return HTTPSeeOther(location=self.ext_authentication_url)
def denied_response(self, req): """ Returns a standard WSGI response callable with the status of 403 or 401 depending on whether the REMOTE_USER is set or not. """ if req.remote_user: self.logger.increment('forbidden') return HTTPForbidden(request=req) else: return HTTPSeeOther(location=self.ext_authentication_url)
def __call__(self, env, start_response): """ Accepts a standard WSGI application call, authenticating the request and installing callback hooks for authorization and ACL header validation. For an authenticated request, REMOTE_USER will be set to a comma separated list of the user's groups. If the request matches the self.auth_prefix, the request will be routed through the internal auth request handler (self.handle). This is to handle granting tokens, etc. """ if self.allow_overrides and env.get('swift.authorize_override', False): return self.app(env, start_response) if env.get('PATH_INFO', '').startswith(self.auth_prefix): return self.handle(env, start_response) token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN')) if token and token.startswith(self.reseller_prefix): groups = self.get_groups(env, token) if groups: user = groups and groups.split(',', 1)[0] or '' trans_id = env.get('swift.trans_id') self.logger.debug('User: %s uses token %s (trans_id %s)' % (user, token, trans_id)) env['REMOTE_USER'] = groups env['swift.authorize'] = self.authorize env['swift.clean_acl'] = clean_acl if '.reseller_admin' in groups: env['reseller_request'] = True else: # Invalid token (may be expired) 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 # definitive auth for. try: version, rest = split_path(env.get('PATH_INFO', ''), 1, 2, True) except ValueError: version, rest = None, None self.logger.increment('errors') # Not my token, not my account, I can't authorize this request, # deny all is a good idea if not already set... if 'swift.authorize' not in env: env['swift.authorize'] = self.denied_response return self.app(env, start_response)
def handle_get_token(self, req): """ 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. "Active Mode" usage: All formats require GSS (Kerberos) authentication. GET <auth-prefix>/v1/<act>/auth GET <auth-prefix>/auth GET <auth-prefix>/v1.0 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:: 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 above. """ # Validate the request info try: pathsegs = split_path(req.path_info, 1, 3, True) except ValueError: self.logger.increment('errors') return HTTPNotFound(request=req) if not ((pathsegs[0] == 'v1' and pathsegs[2] == 'auth') or pathsegs[0] in ('auth', 'v1.0')): return HTTPBadRequest(request=req) # 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