tests/httpserverauth.py
author Matt Harbison <matt_harbison@yahoo.com>
Mon, 14 Dec 2020 17:59:10 -0500
changeset 46136 3158522085f0
parent 43076 2372284d9457
child 48875 6000f5b25c9b
permissions -rw-r--r--
setup: exclude the git extension from py2 builds This can't be built on Windows with the py2 compiler, and while old versions can be installed via pip on Linux, I can't get the tests to run (even with py3.8) using pygit2 0.28.2. Some manually run commands work, and others spew stack traces that don't occur with the current 1.4.0 release using py3. Differential Revision: https://phab.mercurial-scm.org/D9604
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
41585
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
     1
from __future__ import absolute_import
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
     2
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
     3
import base64
41587
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
     4
import hashlib
41585
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
     5
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
     6
from mercurial.hgweb import common
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
     7
from mercurial import node
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
     8
41587
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
     9
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    10
def parse_keqv_list(req, l):
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    11
    """Parse list of key=value strings where keys are not duplicated."""
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    12
    parsed = {}
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    13
    for elt in l:
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    14
        k, v = elt.split(b'=', 1)
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    15
        if v[0:1] == b'"' and v[-1:] == b'"':
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    16
            v = v[1:-1]
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    17
        parsed[k] = v
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    18
    return parsed
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    19
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    20
41587
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    21
class digestauthserver(object):
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    22
    def __init__(self):
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    23
        self._user_hashes = {}
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    24
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    25
    def gethashers(self):
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    26
        def _md5sum(x):
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    27
            m = hashlib.md5()
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    28
            m.update(x)
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    29
            return node.hex(m.digest())
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    30
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    31
        h = _md5sum
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    32
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    33
        kd = lambda s, d, h=h: h(b"%s:%s" % (s, d))
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    34
        return h, kd
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    35
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    36
    def adduser(self, user, password, realm):
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    37
        h, kd = self.gethashers()
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    38
        a1 = h(b'%s:%s:%s' % (user, realm, password))
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    39
        self._user_hashes[(user, realm)] = a1
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    40
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    41
    def makechallenge(self, realm):
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    42
        # We aren't testing the protocol here, just that the bytes make the
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    43
        # proper round trip.  So hardcoded seems fine.
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    44
        nonce = b'064af982c5b571cea6450d8eda91c20d'
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    45
        return b'realm="%s", nonce="%s", algorithm=MD5, qop="auth"' % (
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    46
            realm,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    47
            nonce,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    48
        )
41587
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    49
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    50
    def checkauth(self, req, header):
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    51
        log = req.rawenv[b'wsgi.errors']
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    52
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    53
        h, kd = self.gethashers()
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    54
        resp = parse_keqv_list(req, header.split(b', '))
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    55
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    56
        if resp.get(b'algorithm', b'MD5').upper() != b'MD5':
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    57
            log.write(b'Unsupported algorithm: %s' % resp.get(b'algorithm'))
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    58
            raise common.ErrorResponse(
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    59
                common.HTTP_FORBIDDEN, b"unknown algorithm"
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    60
            )
41587
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    61
        user = resp[b'username']
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    62
        realm = resp[b'realm']
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    63
        nonce = resp[b'nonce']
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    64
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    65
        ha1 = self._user_hashes.get((user, realm))
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    66
        if not ha1:
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    67
            log.write(b'No hash found for user/realm "%s/%s"' % (user, realm))
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    68
            raise common.ErrorResponse(common.HTTP_FORBIDDEN, b"bad user")
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    69
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    70
        qop = resp.get(b'qop', b'auth')
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    71
        if qop != b'auth':
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    72
            log.write(b"Unsupported qop: %s" % qop)
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    73
            raise common.ErrorResponse(common.HTTP_FORBIDDEN, b"bad qop")
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    74
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    75
        cnonce, ncvalue = resp.get(b'cnonce'), resp.get(b'nc')
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    76
        if not cnonce or not ncvalue:
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    77
            log.write(b'No cnonce (%s) or ncvalue (%s)' % (cnonce, ncvalue))
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    78
            raise common.ErrorResponse(common.HTTP_FORBIDDEN, b"no cnonce")
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    79
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    80
        a2 = b'%s:%s' % (req.method, resp[b'uri'])
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    81
        noncebit = b"%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, h(a2))
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    82
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    83
        respdig = kd(ha1, noncebit)
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    84
        if respdig != resp[b'response']:
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    85
            log.write(
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    86
                b'User/realm "%s/%s" gave %s, but expected %s'
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    87
                % (user, realm, resp[b'response'], respdig)
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    88
            )
41587
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    89
            return False
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    90
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    91
        return True
41585
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
    92
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    93
41589
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
    94
digest = digestauthserver()
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
    95
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    96
41585
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
    97
def perform_authentication(hgweb, req, op):
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
    98
    auth = req.headers.get(b'Authorization')
41589
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
    99
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   100
    if req.headers.get(b'X-HgTest-AuthType') == b'Digest':
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   101
        if not auth:
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   102
            challenge = digest.makechallenge(b'mercurial')
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   103
            raise common.ErrorResponse(
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   104
                common.HTTP_UNAUTHORIZED,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   105
                b'who',
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   106
                [(b'WWW-Authenticate', b'Digest %s' % challenge)],
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   107
            )
41589
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   108
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   109
        if not digest.checkauth(req, auth[7:]):
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   110
            raise common.ErrorResponse(common.HTTP_FORBIDDEN, b'no')
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   111
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   112
        return
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   113
41585
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   114
    if not auth:
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   115
        raise common.ErrorResponse(
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   116
            common.HTTP_UNAUTHORIZED,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   117
            b'who',
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   118
            [(b'WWW-Authenticate', b'Basic Realm="mercurial"')],
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   119
        )
41585
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   120
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   121
    if base64.b64decode(auth.split()[1]).split(b':', 1) != [b'user', b'pass']:
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   122
        raise common.ErrorResponse(common.HTTP_FORBIDDEN, b'no')
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   123
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   124
41585
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   125
def extsetup(ui):
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   126
    common.permhooks.insert(0, perform_authentication)
41589
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   127
    digest.adduser(b'user', b'pass', b'mercurial')