[PATCH 3 of 3 V2] url: support auth.cookiefile for adding cookies to HTTP requests

Augie Fackler raf at durin42.com
Mon Apr 3 21:00:13 UTC 2017


On Tue, Mar 28, 2017 at 08:00:50PM -0700, Gregory Szorc wrote:
> # HG changeset patch
> # User Gregory Szorc <gregory.szorc at gmail.com>
> # Date 1490756307 25200
> #      Tue Mar 28 19:58:27 2017 -0700
> # Node ID 05b01865ed532a11cd6a824b600876c94c2e293e
> # Parent  5cf00a25e8c066bd664dde4cb302bd52a75195d6
> url: support auth.cookiefile for adding cookies to HTTP requests

I think I'm fine with these patches as-is. I assume the theory is that
this cookie would be checked by some reverse proxy, rather than hgweb
itself? Or should we be expecting more changes that introduce
cookie-checking hooks of some kind right into hgweb?

>
> Mercurial can't currently send cookies as part of HTTP requests.
> Some authentication systems use cookies. So, adding support for
> sending cookies seems like a useful feature.
>
> This patch implements support for reading cookies from a file
> and automatically sending them as part of the request. We rely
> on the "cookiejar" Python module to do the heavy lifting of
> parsing cookies files. We currently support the Mozilla (really
> Netscape-era) and LWP cookie formats. These are the formats
> supported by Python 2's cookielib.
>
> diff --git a/mercurial/help/config.txt b/mercurial/help/config.txt
> --- a/mercurial/help/config.txt
> +++ b/mercurial/help/config.txt
> @@ -323,12 +323,31 @@ related options for the diff command.
>  ``auth``
>  --------
>
> -Authentication credentials for HTTP authentication. This section
> -allows you to store usernames and passwords for use when logging
> -*into* HTTP servers. See :hg:`help config.web` if
> -you want to configure *who* can login to your HTTP server.
> -
> -Each line has the following format::
> +Authentication credentials and other authentication-like configuration
> +for HTTP connections. This section allows you to store usernames and
> +passwords for use when logging *into* HTTP servers. See
> +:hg:`help config.web` if you want to configure *who* can login to
> +your HTTP server.
> +
> +The following options apply to all hosts.
> +
> +``cookiefile``
> +    Path to a file containing HTTP cookie lines. Cookies matching a
> +    host will be sent automatically.
> +
> +    The file format uses the Mozilla cookies.txt or libwww-perl "Set-Cookie3"
> +    format. For specifics of each format, do an Internet search for
> +    "Netscape cookies.txt format" or "libwww-perl cookie format."
> +
> +    Note: the Mozilla cookies parser does not handle port numbers on domains.
> +    You will need to remove ports from the domain for the cookie to be
> +    recognized. This could result in a cookie being disclosed to an unwanted
> +    server.
> +
> +    The cookies file is read-only.
> +
> +Other options in this section are grouped by name and have the following
> +format::
>
>      <name>.<argument> = <value>
>
> diff --git a/mercurial/url.py b/mercurial/url.py
> --- a/mercurial/url.py
> +++ b/mercurial/url.py
> @@ -417,6 +417,45 @@ class httpbasicauthhandler(urlreq.httpba
>          else:
>              return None
>
> +class cookiehandler(urlreq.basehandler):
> +    """HTTP request handler that adds cookies seen in a cookies file."""
> +
> +    # Classes that support the CookieJar interface.
> +    COOKIEJARS = (
> +        util.cookielib.MozillaCookieJar,
> +        util.cookielib.LWPCookieJar,
> +    )
> +
> +    def __init__(self, ui):
> +        self.cookiejar = None
> +
> +        cookiefile = ui.config('auth', 'cookiefile')
> +        if not cookiefile:
> +            return
> +
> +        cookiefile = util.expandpath(cookiefile)
> +
> +        for cls in self.COOKIEJARS:
> +            try:
> +                jar = cls(cookiefile)
> +                jar.load()
> +                self.cookiejar = jar
> +                break
> +            except util.cookielib.LoadError:
> +                pass
> +
> +        if self.cookiejar is None:
> +            ui.warn(_('(error loading cookie file %s: continuing without '
> +                      'cookies)\n') % cookiefile)
> +
> +    def http_request(self, request):
> +        if self.cookiejar:
> +            self.cookiejar.add_cookie_header(request)
> +
> +        return request
> +
> +    https_request = http_request
> +
>  handlerfuncs = []
>
>  def opener(ui, authinfo=None):
> @@ -450,6 +489,7 @@ def opener(ui, authinfo=None):
>      handlers.extend((httpbasicauthhandler(passmgr),
>                       httpdigestauthhandler(passmgr)))
>      handlers.extend([h(ui, passmgr) for h in handlerfuncs])
> +    handlers.append(cookiehandler(ui))
>      opener = urlreq.buildopener(*handlers)
>
>      # The user agent should should *NOT* be used by servers for e.g.
> diff --git a/tests/test-http.t b/tests/test-http.t
> --- a/tests/test-http.t
> +++ b/tests/test-http.t
> @@ -1,4 +1,4 @@
> -#require serve
> +#require killdaemons serve
>
>    $ hg init test
>    $ cd test
> @@ -333,3 +333,87 @@ check abort error reporting while pullin
>    abort: pull failed on remote
>    [255]
>    $ cat error.log
> +
> +corrupt cookies file should yield a warning
> +
> +  $ cat > $TESTTMP/cookies.txt << EOF
> +  > bad format
> +  > EOF
> +
> +  $ hg --config auth.cookiefile=$TESTTMP/cookies.txt id http://localhost:$HGPORT/
> +  (error loading cookie file $TESTTMP/cookies.txt: continuing without cookies)
> +  56f9bc90cce6
> +
> +  $ killdaemons.py
> +
> +Create dummy authentication handler that looks for cookies. It doesn't do anything
> +useful. It just raises an HTTP 500 with details about the Cookie request header.
> +We raise HTTP 500 because its message is printed in the abort message.
> +
> +  $ cat > cookieauth.py << EOF
> +  > from mercurial import util
> +  > from mercurial.hgweb import common
> +  > def perform_authentication(hgweb, req, op):
> +  >     cookie = req.env.get('HTTP_COOKIE')
> +  >     if not cookie:
> +  >         raise common.ErrorResponse(common.HTTP_SERVER_ERROR, 'no-cookie')
> +  >     raise common.ErrorResponse(common.HTTP_SERVER_ERROR, 'Cookie: %s' % cookie)
> +  > def extsetup():
> +  >     common.permhooks.insert(0, perform_authentication)
> +  > EOF
> +
> +  $ hg serve --config extensions.cookieauth=cookieauth.py -R test -p $HGPORT -d --pid-file=pid
> +  $ cat pid > $DAEMON_PIDS
> +
> +Request without cookie sent should fail due to lack of cookie
> +
> +  $ hg id http://localhost:$HGPORT
> +  abort: HTTP Error 500: no-cookie
> +  [255]
> +
> +Populate a Netscape cookies file
> +
> +  $ cat > cookies.txt << EOF
> +  > # HTTP Cookie File
> +  > # Expiration is 2030-01-01 at midnight
> +  > .example.com	TRUE	/	FALSE	1893456000	hgkey	mozvalue
> +  > EOF
> +
> +Should not send a cookie for another domain
> +
> +  $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
> +  abort: HTTP Error 500: no-cookie
> +  [255]
> +
> +Add a cookie entry for our test server and verify it is sent
> +
> +  $ cat >> cookies.txt << EOF
> +  > localhost.local	FALSE	/	FALSE	1893456000	hgkey	mozlocalhostvalue
> +  > EOF
> +
> +  $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
> +  abort: HTTP Error 500: Cookie: hgkey=mozlocalhostvalue
> +  [255]
> +
> +Now do the same for an LWP cookie jar
> +
> +  $ cat > cookies.txt << EOF
> +  > #LWP-Cookies-1.0
> +  > Set-Cookie3: hgkey=lwpvalue; path="/"; domain=.example.com; path_spec; expires="2030-01-01 00:00:00"; version=0
> +  > EOF
> +
> +Should not send a cookie for another domain
> +
> +  $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
> +  abort: HTTP Error 500: no-cookie
> +  [255]
> +
> +Cookie for our test server should be sent
> +
> +  $ cat >> cookies.txt << EOF
> +  > Set-Cookie3: hgkey=lwplocalhostvalue; path="/"; domain=localhost.local; path_spec; expires="2030-01-01 00:00:00"; version=0
> +  > EOF
> +
> +  $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
> +  abort: HTTP Error 500: Cookie: hgkey=lwplocalhostvalue
> +  [255]
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


More information about the Mercurial-devel mailing list