Changeset - 32440c07a085
[Not reviewed]
default
0 1 0
domruf - 8 years ago 2017-09-11 21:16:49
dominikruf@gmail.com
auth: consume request body before responding 401 or 403 during authentication

In order to work correctly with reverse proxies like Apache, the application
needs to consume the whole body before returning and closing the connection.
Otherwise the reverse proxy may complain about a broken pipe.

For example, if the client sends a lot of data and kallithea doesn't read all
that data before sending 401, the connection will be closed before the reverse
proxy has sent all the data. In this case an apache reverse proxy will fail
with a broken pipe error.

This is not necessary for all wsgi servers. Waitress automatically buffers (and
therefore reads) all the data and uwsgi has a 'post-buffering' option to do the
same. But AFAIK there is no way to push to a password protected hg repository
when using gunicorn without this changeset.
1 file changed with 10 insertions and 4 deletions:
0 comments (0 inline, 0 general)
kallithea/lib/base.py
Show inline comments
 
@@ -191,8 +191,14 @@ class BasicAuth(paste.auth.basic.AuthBas
 
        self.authfunc = authfunc
 
        self._rc_auth_http_code = auth_http_code
 

	
 
    def build_authentication(self):
 
    def build_authentication(self, environ):
 
        head = paste.httpheaders.WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
 
        # Consume the whole body before sending a response
 
        try:
 
            request_body_size = int(environ.get('CONTENT_LENGTH', 0))
 
        except (ValueError):
 
            request_body_size = 0
 
        environ['wsgi.input'].read(request_body_size)
 
        if self._rc_auth_http_code and self._rc_auth_http_code == '403':
 
            # return 403 if alternative http return code is specified in
 
            # Kallithea config
 
@@ -202,17 +208,17 @@ class BasicAuth(paste.auth.basic.AuthBas
 
    def authenticate(self, environ):
 
        authorization = paste.httpheaders.AUTHORIZATION(environ)
 
        if not authorization:
 
            return self.build_authentication()
 
            return self.build_authentication(environ)
 
        (authmeth, auth) = authorization.split(' ', 1)
 
        if 'basic' != authmeth.lower():
 
            return self.build_authentication()
 
            return self.build_authentication(environ)
 
        auth = auth.strip().decode('base64')
 
        _parts = auth.split(':', 1)
 
        if len(_parts) == 2:
 
            username, password = _parts
 
            if self.authfunc(username, password, environ) is not None:
 
                return username
 
        return self.build_authentication()
 
        return self.build_authentication(environ)
 

	
 
    __call__ = authenticate
 

	
0 comments (0 inline, 0 general)