tests/httpserverauth.py
author Gregory Szorc <gregory.szorc@gmail.com>
Thu, 07 Mar 2019 14:02:02 -0800
changeset 41918 5d9fdc9b0178
parent 41589 46432c04f010
child 43076 2372284d9457
permissions -rw-r--r--
setup: include hgext3rd package in py2exe builds This is a core Mercurial package and we should always ship it. Differential Revision: https://phab.mercurial-scm.org/D6094
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
41587
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
     7
from mercurial import (
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
     8
    node,
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
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    11
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
    12
    """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
    13
    parsed = {}
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    14
    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
    15
        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
    16
        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
    17
            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
    18
        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
    19
    return parsed
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    20
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'
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    45
        return b'realm="%s", nonce="%s", algorithm=MD5, qop="auth"' % (realm,
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    46
                                                                       nonce)
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'))
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    56
            raise common.ErrorResponse(common.HTTP_FORBIDDEN,
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    57
                                       b"unknown algorithm")
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    58
        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
    59
        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
    60
        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
    61
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    62
        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
    63
        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
    64
            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
    65
            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
    66
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    67
        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
    68
        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
    69
            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
    70
            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
    71
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    72
        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
    73
        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
    74
            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
    75
            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
    76
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    77
        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
    78
        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
    79
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    80
        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
    81
        if respdig != resp[b'response']:
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    82
            log.write(b'User/realm "%s/%s" gave %s, but expected %s'
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    83
                      % (user, realm, resp[b'response'], respdig))
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    84
            return False
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    85
ccaa52865fac tests: add code to handle HTTP digests on the server side
Matt Harbison <matt_harbison@yahoo.com>
parents: 41585
diff changeset
    86
        return True
41585
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
    87
41589
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
    88
digest = digestauthserver()
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
    89
41585
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
    90
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
    91
    auth = req.headers.get(b'Authorization')
41589
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
    92
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
    93
    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
    94
        if not auth:
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
    95
            challenge = digest.makechallenge(b'mercurial')
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
    96
            raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, b'who',
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
    97
                    [(b'WWW-Authenticate', b'Digest %s' % challenge)])
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
    98
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
    99
        if not digest.checkauth(req, auth[7:]):
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   100
            raise common.ErrorResponse(common.HTTP_FORBIDDEN, b'no')
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   101
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   102
        return
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   103
41585
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   104
    if not auth:
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   105
        raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, b'who',
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   106
                [(b'WWW-Authenticate', b'Basic Realm="mercurial"')])
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   107
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   108
    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
   109
        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
   110
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   111
def extsetup(ui):
549af2fa089f tests: extract the http server authentication extension to a single module
Matt Harbison <matt_harbison@yahoo.com>
parents:
diff changeset
   112
    common.permhooks.insert(0, perform_authentication)
41589
46432c04f010 tests: enable HTTP digest testing
Matt Harbison <matt_harbison@yahoo.com>
parents: 41587
diff changeset
   113
    digest.adduser(b'user', b'pass', b'mercurial')