summaryrefslogtreecommitdiff
path: root/tests/test_auth/test_auth_digest.py
blob: 1d44038bd7e6cb1af3f79ad661106872f4f385ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# (c) 2005 Clark C. Evans
# This module is part of the Python Paste Project and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php

from paste.auth.digest import *
from paste.wsgilib import raw_interactive
from paste.httpexceptions import *
from paste.httpheaders import AUTHORIZATION, WWW_AUTHENTICATE, REMOTE_USER
import os
import six

def application(environ, start_response):
    content = REMOTE_USER(environ)
    start_response("200 OK",(('Content-Type', 'text/plain'),
                             ('Content-Length', len(content))))

    if six.PY3:
        content = content.encode('utf8')
    return [content]

realm = "tag:clarkevans.com,2005:testing"

def backwords(environ, realm, username):
    """ dummy password hash, where user password is just reverse """
    password = list(username)
    password.reverse()
    password = "".join(password)
    return digest_password(realm, username, password)

application = AuthDigestHandler(application,realm,backwords)
application = HTTPExceptionHandler(application)

def check(username, password, path="/"):
    """ perform two-stage authentication to verify login """
    (status,headers,content,errors) = \
        raw_interactive(application,path, accept='text/html')
    assert status.startswith("401")
    challenge = WWW_AUTHENTICATE(headers)
    response = AUTHORIZATION(username=username, password=password,
                             challenge=challenge, path=path)
    assert "Digest" in response and username in response
    (status,headers,content,errors) = \
        raw_interactive(application,path,
                        HTTP_AUTHORIZATION=response)
    if status.startswith("200"):
        return content
    if status.startswith("401"):
        return None
    assert False, "Unexpected Status: %s" % status

def test_digest():
    assert b'bing' == check("bing","gnib")
    assert check("bing","bad") is None

#
# The following code uses sockets to test the functionality,
# to enable use:
#
# $ TEST_SOCKET py.test
#

if os.environ.get("TEST_SOCKET",""):
    from six.moves.urllib.error import HTTPError
    from six.moves.urllib.request import build_opener, HTTPDigestAuthHandler
    from paste.debug.testserver import serve
    server = serve(application)

    def authfetch(username,password,path="/",realm=realm):
        server.accept(2)
        import socket
        socket.setdefaulttimeout(5)
        uri = ("http://%s:%s" % server.server_address) + path
        auth = HTTPDigestAuthHandler()
        auth.add_password(realm,uri,username,password)
        opener = build_opener(auth)
        result = opener.open(uri)
        return result.read()

    def test_success():
        assert "bing" == authfetch('bing','gnib')

    def test_failure():
        # urllib tries 5 more times before it gives up
        server.accept(5)
        try:
            authfetch('bing','wrong')
            assert False, "this should raise an exception"
        except HTTPError as e:
            assert e.code == 401

    def test_shutdown():
        server.stop()