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

Gregory Szorc gregory.szorc at gmail.com
Mon Apr 3 17:50:13 EDT 2017


On Mon, Apr 3, 2017 at 2:00 PM, Augie Fackler <raf at durin42.com> wrote:

> 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?
>

I have no intent to build server support for cookies at this time.


>
> >
> > 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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.mercurial-scm.org/pipermail/mercurial-devel/attachments/20170403/307695c4/attachment.html>


More information about the Mercurial-devel mailing list