コード例 #1
0
    def __call__(self, env, start_response):
        self.logger.debug('In mauth middleware')
        identity = None # the identity we are trying to populate
 
        # Handle s3 connections first because s3 has a unique format/use for the 'HTTP_X_AUTH_TOKEN'.
        s3 = env.get('HTTP_AUTHORIZATION', None)
        if s3 and s3.startswith('AWS'):
            s3_apikey, s3_signature = s3.split(' ')[1].rsplit(':', 1)[:]
            if s3_apikey and s3_signature:
                # check if we have cached data to validate this request instead of hitting cloudstack.
                memcache_client = cache_from_env(env)
                memcache_result = memcache_client.get('mauth_s3_apikey/%s' % s3_apikey)
                valid_cache = False
                data = None
                if memcache_result and self.cache_timeout > 0:
                    expires, data = memcache_result
                    if expires > time():
                        valid_cache = True
                if valid_cache:
                    self.logger.debug('Validating the S3 request via the cached identity')
                    s3_token = base64.urlsafe_b64decode(env.get('HTTP_X_AUTH_TOKEN', '')).encode("utf-8")
                    if s3_signature == base64.b64encode(hmac.new(data.get('secret', ''), s3_token, hashlib.sha1).digest()):
                        self.logger.debug('Using cached S3 identity')
                        identity = data.get('identity', None)
                        
                        # The swift3 middleware sets env['PATH_INFO'] to '/v1/<aws_secret_key>', we need to map it to the cloudstack account.
                        env['PATH_INFO'] = env['PATH_INFO'].replace(s3_apikey, '%s' % (identity.get('account_url').split('/v1/')[-1]))
                else: # hit cloudstack and populate memcached if valid request
                    secret_key = None
                    try:
                        identity, secret_key = self.get_s3_identity(env, start_response, s3_apikey, s3_signature)
                    except self.MauthError as e:
                        self.logger.debug(e.value)
                        env['swift.authorize'] = self.denied_response
                        return self.app(env, start_response)
                    
                    if identity:
                        # The swift3 middleware sets env['PATH_INFO'] to '/v1/<aws_secret_key>', we need to map it to the cloudstack account.
                        env['PATH_INFO'] = env['PATH_INFO'].replace(s3_apikey, '%s' % (identity.get('account_url').split('/v1/')[-1]))

                        memcache_client = cache_from_env(env)
                        if memcache_client:
                            memcache_client.set('mauth_s3_apikey/%s' % s3_apikey, (identity.get('expires', time()), dict({'secret':secret_key, 'identity':identity})), time=int(env.get('HTTP_X_AUTH_TTL', self.cache_timeout)))
                            memcache_client.set('mauth_token/%s' % token, (identity.get('expires', time()), identity), time=int(env.get('HTTP_X_AUTH_TTL', self.cache_timeout)))
                    else:
                        self.logger.debug('No identity for this request')
                        env['swift.authorize'] = self.denied_response
                        return self.app(env, start_response)
            else:
                self.logger.debug('Invalid credential format')
                env['swift.authorize'] = self.denied_response
                return self.app(env, start_response)
        
        # If it is not an S3 call, handle the request for authenication, otherwise, use the token.
        req = Request(env)
        if not s3:
            try:
                auth_url_piece, rest_of_url = split_path(req.path_info, minsegs=1, maxsegs=2, rest_with_last=True)
            except ValueError:
                return HTTPNotFound(request=req)

            # Check if the request is for authentication (to get a token).
            if auth_url_piece in ('auth', 'v1.0'): # valid auth urls
                auth_user = env.get('HTTP_X_AUTH_USER', None)
                auth_key = env.get('HTTP_X_AUTH_KEY', None)
                if auth_user and auth_key:
                    # check if we have this user and key cached.
                    memcache_client = cache_from_env(env)
                    memcache_result = memcache_client.get('mauth_creds/%s/%s' % (auth_user, auth_key))
                    valid_cache = False
                    data = None
                    if memcache_result and self.cache_timeout > 0 and int(env.get('HTTP_X_AUTH_TTL', 1)) > 0:
                        expires, data = memcache_result
                        if expires > time():
                            valid_cache = True
                    if valid_cache:
                        self.logger.debug('Using cached identity via creds')
                        identity = data
                        self.logger.debug("Using identity: %r" % (identity))
                        req.response = Response(request=req,
                                                headers={'x-auth-token':identity.get('token', None), 
                                                         'x-storage-token':identity.get('token', None),
                                                         'x-storage-url':identity.get('account_url', None)})
                        return req.response(env, start_response)
                    else: # hit cloudstack for the details.
                        try:
                            identity = self.get_identity(env, start_response, auth_user, auth_key)
                        except self.MauthError as e:
                            self.logger.debug(e.value)
                            env['swift.authorize'] = self.denied_response
                            return self.app(env, start_response)

                        if identity:
                            self.logger.debug("Using identity: %r" % (identity))
                                
                            # add to memcache so it can be referenced later
                            memcache_client = cache_from_env(env)
                            if memcache_client:
                                memcache_client.set('mauth_creds/%s/%s' % (auth_user, auth_key), (identity.get('expires', time()), identity), time=int(env.get('HTTP_X_AUTH_TTL', self.cache_timeout)))
                                memcache_client.set('mauth_token/%s' % identity.get('token', ''), (identity.get('expires', time()), identity), time=int(env.get('HTTP_X_AUTH_TTL', self.cache_timeout)))
                            req.response = Response(request=req,
                                                    headers={'x-auth-token':identity.get('token', None), 
                                                             'x-storage-token':identity.get('token', None),
                                                             'x-storage-url':identity.get('account_url', None)})
                            return req.response(env, start_response)
                        else:
                            self.logger.debug('No identity for these credentials')
                            env['swift.authorize'] = self.denied_response
                            return self.app(env, start_response)
                else:
                    self.logger.debug('Credentials missing')
                    env['swift.authorize'] = self.denied_response
                    return self.app(env, start_response)
            else:
                token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN', None))

                if not token:
                    qs = env.get('QUERY_STRING', None)
                    if qs:
                        parts = qs.split('&')
                        for part in parts:
                           param = part.split('=')
                           if 'X-AUTH-TOKEN' in param[0].upper():
                               self.logger.debug("Found token '%s' in query string." % param[1])
                               token = param[1]

        if not identity and not token:
            # this is an anonymous request.  pass it through for authorize to verify.
            self.logger.debug('Passing through anonymous request')
            env['swift.authorize'] = self.authorize
            env['swift.clean_acl'] = clean_acl
            return self.app(env, start_response)

        # setup a memcache client for the following.
        memcache_client = cache_from_env(env)
        
        if not identity:
            memcache_result = memcache_client.get('mauth_token/%s' % token)
            if memcache_result and self.cache_timeout > 0:
                expires, _identity = memcache_result
                if expires > time():
                    self.logger.debug('Using cached identity via token')
                    identity = _identity

        if not identity:
            self.logger.debug("No cached identity, validate token via the extension.")
            try:
                identity = self.validate_token(token)
            except self.MauthError as e:
                self.logger.debug(e.value)
                env['swift.authorize'] = self.denied_response
                return self.app(env, start_response)

            if identity and memcache_client:                
                memcache_client.set('mauth_token/%s' % identity.get('token', None), (identity.get('expires', time()), identity), time=int(env.get('HTTP_X_AUTH_TTL', self.cache_timeout)))
            else:  # if we didn't get identity it means there was an error.
                self.logger.debug('No identity for this token');
                env['swift.authorize'] = self.denied_response
                return self.app(env, start_response)

        if not identity:
            env['swift.authorize'] = self.denied_response
            return self.app(env, start_response)

        self.logger.debug("Using identity: %r" % (identity))
        env['mauth.identity'] = identity
        env['REMOTE_USER'] = '******'.join(identity['roles'])
        env['swift.authorize'] = self.authorize
        env['swift.clean_acl'] = clean_acl
        return self.app(env, start_response)