forked from mozilla/django-browserid
/
base.py
132 lines (101 loc) · 3.99 KB
/
base.py
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import logging
import urllib
try:
import json
except ImportError:
import simplejson as json
from django.conf import settings
import requests
log = logging.getLogger(__name__)
DEFAULT_HTTP_TIMEOUT = 5
DEFAULT_VERIFICATION_URL = 'https://browserid.org/verify'
OKAY_RESPONSE = 'okay'
def get_audience(request):
"""Uses Django settings to format the audience.
To use this function, make sure there is either a SITE_URL in
your settings.py file or PROTOCOL and DOMAIN.
Examples using SITE_URL:
SITE_URL = 'http://127.0.0.1:8001'
SITE_URL = 'https://example.com'
SITE_URL = 'http://example.com'
If you don't have a SITE_URL you can also use these varables:
PROTOCOL, DOMAIN, and (optionally) PORT.
Example 1:
PROTOCOL = 'https://'
DOMAIN = 'example.com'
Example 2:
PROTOCOL = 'http://'
DOMAIN = '127.0.0.1'
PORT = '8001'
If none are set, we trust the request to populate the audience.
This is *not secure*!
"""
site_url = getattr(settings, 'SITE_URL', False)
# Note audience based on request for developer warnings
if request.is_secure():
req_proto = 'https://'
else:
req_proto = 'http://'
req_domain = request.get_host()
# If we don't define it explicitly
if not site_url:
protocol = getattr(settings, 'PROTOCOL', req_proto)
if not getattr(settings, 'DOMAIN'):
log.warning('django-browserid WARNING you are missing '
'settings.SITE_URL. This is not a secure way '
'to verify assertions. Please fix me. '
'Setting domain to %s.' % req_domain)
# DOMAIN is example.com req_domain is example.com:8001
domain = getattr(settings, 'DOMAIN', req_domain.split(':')[0])
standards = {'https://': 443, 'http://': 80}
if ':' in req_domain:
req_port = req_domain.split(':')[1]
else:
req_port = None
port = getattr(settings, 'PORT', req_port or standards[protocol])
if port == standards[protocol]:
site_url = ''.join(map(str, (protocol, domain)))
else:
site_url = ''.join(map(str, (protocol, domain, ':', port)))
req_url = "%s%s" % (req_proto, req_domain)
if site_url != "%s%s" % (req_proto, req_domain):
log.warning('Misconfigured SITE_URL? settings has [%s], but '
'actual request was [%s] BrowserID may fail on '
'audience' % (site_url, req_url))
return site_url
def _verify_http_request(url, qs):
parameters = {
'data': qs,
'proxies': getattr(settings, 'BROWSERID_PROXY_INFO', None),
'verify': not getattr(settings, 'BROWSERID_DISABLE_CERT_CHECK', False),
'headers': {'Content-type': 'application/x-www-form-urlencoded'},
'params': {
'timeout': getattr(settings, 'BROWSERID_HTTP_TIMEOUT',
DEFAULT_HTTP_TIMEOUT)
}
}
if parameters['verify']:
parameters['verify'] = getattr(settings, 'BROWSERID_CACERT_FILE', True)
r = requests.post(url, **parameters)
try:
rv = json.loads(r.content)
except ValueError:
log.debug('Failed to decode JSON. Resp: %s, Content: %s' %
(r.status_code, r.content))
return dict(status='failure')
return rv
def verify(assertion, audience):
"""Verify assertion using an external verification service."""
verify_url = getattr(settings, 'BROWSERID_VERIFICATION_URL',
DEFAULT_VERIFICATION_URL)
log.info("Verification URL: %s" % verify_url)
result = _verify_http_request(verify_url, urllib.urlencode({
'assertion': assertion,
'audience': audience
}))
if result['status'] == OKAY_RESPONSE:
return result
log.error('BrowserID verification failure. Response: %r '
'Audience: %r' % (result, audience))
log.error("BID assert: %r" % assertion)
return False