Initial push - still in a pretty unusable state.
[vern.git] / digestauth.py
1
2 from mitmproxy import ctx, exceptions
3 from requests.auth import HTTPDigestAuth
4 from requests.utils import parse_dict_header
5
6 import re
7 import threading
8 import requests
9 import typing
10 import getpass
11
12
13 class DigestAuthenticator:
14
15 def __init__(self):
16 self.lock = threading.Lock()
17 self.resubmitted_response = None
18 self._username = None
19 self._password = None
20
21 def load(self, loader):
22 ctx.log.info('in load')
23 loader.add_option(
24 name = "uname",
25 typespec = typing.Text,
26 default = '',
27 help = "Please enter your user name"
28 )
29
30 def configure(self, updates):
31 ctx.log.info('updates: ' + str(updates))
32 ctx.log.info('in configue: ' + str(ctx.options))
33 if ctx.options.uname == '':
34 raise exceptions.OptionsError("Please enter a non-empty value for uname with --set uname=your_username")
35 else:
36 self._username = ctx.options.uname
37 self._password = getpass.getpass("Please enter the password for " + self._username + ": ")
38
39
40 def response(self, flow):
41
42 if flow.response.status_code == 401 and 'Authorization' not in flow.request.headers:
43
44 www_authenticate_header = flow.response.headers['WWW-Authenticate']
45
46 if 'digest' in www_authenticate_header.lower():
47
48 http_digest_auth = HTTPDigestAuth(self._username, self._password)
49 http_digest_auth.init_per_thread_state()
50 pat = re.compile(r'digest ', flags=re.IGNORECASE)
51 http_digest_auth._thread_local.chal = parse_dict_header(pat.sub('', www_authenticate_header, count=1))
52 authorization_header = http_digest_auth.build_digest_header(flow.request.method, flow.request.url)
53
54 resubmitted_headers = {}
55 resubmitted_headers.update(flow.request.headers)
56 resubmitted_headers.update({'Authorization': str(authorization_header)})
57 if flow.request.method == 'GET':
58 resubmitted_response = requests.get(flow.request.url, headers=resubmitted_headers, allow_redirects=False)
59 else:
60 # TODO: This probably needs a data=payload arg...
61 resubmitted_response = requests.post(flow.request.url, headers=resubmitted_headers)
62
63 if resubmitted_response.status_code == 301:
64 ctx.log.info("301 response headers: " + str(resubmitted_response.headers))
65
66 flow.response.status_code = resubmitted_response.status_code
67 flow.response.reason = resubmitted_response.reason
68 flow.response.content = resubmitted_response.text.encode('ascii')
69
70 # TODO: additional response fields (headers and cookies)
71
72 else:
73 # TODO: switch info to debug eventually
74 ctx.log("DigestAuthenticator other request: " + str(flow.response.status_code) + "\n\n", "info")
75
76
77 addons = [
78 DigestAuthenticator()
79 ]