예제 #1
0
    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)
예제 #2
0
 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)
예제 #3
0
    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)
예제 #4
0
    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