tests/httpserverauth.py
author Manuel Jacob <me@manueljacob.de>
Mon, 11 Jul 2022 01:51:20 +0200
branchstable
changeset 49378 094a5fa3cf52
parent 48946 642e31cb55f0
permissions -rw-r--r--
procutil: make stream detection in make_line_buffered more correct and strict In make_line_buffered(), we don’t want to wrap the stream if we know that lines get flushed to the underlying raw stream already. Previously, the heuristic was too optimistic. It assumed that any stream which is not an instance of io.BufferedIOBase doesn’t need wrapping. However, there are buffered streams that aren’t instances of io.BufferedIOBase, like Mercurial’s own winstdout. The new logic is different in two ways: First, only for the check, if unwraps any combination of WriteAllWrapper and winstdout. Second, it skips wrapping the stream only if it is an instance of io.RawIOBase (or already wrapped). If it is an instance of io.BufferedIOBase, it gets wrapped. In any other case, the function raises an exception. This ensures that, if an unknown stream is passed or we add another wrapper in the future, we don’t wrap the stream if it’s already line buffered or not wrap the stream if it’s not line buffered. In fact, this was already helpful during development of this change. Without it, I possibly would have forgot that WriteAllWrapper needs to be ignored for the check, leading to unnecessary wrapping if stdout is unbuffered. The alternative would have been to always wrap unknown streams. However, I don’t think that anyone would benefit from being less strict. We can expect streams from the standard library to be subclassing either io.RawIOBase or io.BufferedIOBase, so running Mercurial in the standard way should not regress by this change. Py2exe might replace sys.stdout and sys.stderr, but that currently breaks Mercurial anyway and also these streams don’t claim to be interactive, so this function is not called for them.
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
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
     2
import hashlib
41585
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
     3
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
     4
from mercurial.hgweb import common
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
     5
from mercurial import node
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
     6
41587
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
     7
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
     8
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
     9
    """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
    10
    parsed = {}
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    11
    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
    12
        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
    13
        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
    14
            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
    15
        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
    16
    return parsed
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    17
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    18
48946
642e31cb55f0 py3: use class X: instead of class X(object):
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48875
diff changeset
    19
class digestauthserver:
41587
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    20
    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
    21
        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
    22
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    23
    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
    24
        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
    25
            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
    26
            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
    27
            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
    28
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    29
        h = _md5sum
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
        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
    32
        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
    33
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    34
    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
    35
        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
    36
        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
    37
        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
    38
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    39
    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
    40
        # 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
    41
        # 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
    42
        nonce = b'064af982c5b571cea6450d8eda91c20d'
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    43
        return b'realm="%s", nonce="%s", algorithm=MD5, qop="auth"' % (
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    44
            realm,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    45
            nonce,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    46
        )
41587
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    47
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    48
    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
    49
        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
    50
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    51
        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
    52
        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
    53
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    54
        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
    55
            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
    56
            raise common.ErrorResponse(
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    57
                common.HTTP_FORBIDDEN, b"unknown algorithm"
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    58
            )
41587
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    59
        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
    60
        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
    61
        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
    62
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    63
        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
    64
        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
    65
            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
    66
            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
    67
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    68
        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
    69
        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
    70
            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
    71
            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
    72
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    73
        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
    74
        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
    75
            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
    76
            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
    77
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    78
        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
    79
        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
    80
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    81
        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
    82
        if respdig != resp[b'response']:
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    83
            log.write(
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    84
                b'User/realm "%s/%s" gave %s, but expected %s'
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    85
                % (user, realm, resp[b'response'], respdig)
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    86
            )
41587
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    87
            return False
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    88
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    89
        return True
41585
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
    90
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    91
41589
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
    92
digest = digestauthserver()
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
    93
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
    94
41585
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
    95
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
    96
    auth = req.headers.get(b'Authorization')
41589
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
    97
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
    98
    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
    99
        if not auth:
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   100
            challenge = digest.makechallenge(b'mercurial')
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   101
            raise common.ErrorResponse(
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   102
                common.HTTP_UNAUTHORIZED,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   103
                b'who',
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   104
                [(b'WWW-Authenticate', b'Digest %s' % challenge)],
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   105
            )
41589
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   106
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   107
        if not digest.checkauth(req, auth[7:]):
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   108
            raise common.ErrorResponse(common.HTTP_FORBIDDEN, b'no')
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   109
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   110
        return
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   111
41585
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   112
    if not auth:
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   113
        raise common.ErrorResponse(
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   114
            common.HTTP_UNAUTHORIZED,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   115
            b'who',
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   116
            [(b'WWW-Authenticate', b'Basic Realm="mercurial"')],
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   117
        )
41585
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   118
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   119
    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
   120
        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
   121
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 41589
diff changeset
   122
41585
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   123
def extsetup(ui):
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   124
    common.permhooks.insert(0, perform_authentication)
41589
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   125
    digest.adduser(b'user', b'pass', b'mercurial')