-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathflask_httpauth.py
105 lines (89 loc) · 3.58 KB
/
flask_httpauth.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
"""
flask.ext.httpauth
==================
This module provides Basic and Digest HTTP authentication for Flask routes.
:copyright: (C) 2013 by Miguel Grinberg.
:license: BSD, see LICENSE for more details.
"""
from functools import wraps
from hashlib import md5
from random import Random, SystemRandom
from flask import request, make_response, session
class HTTPAuth(object):
def __init__(self):
def default_get_password(username):
return None
def default_auth_error():
return "Unauthorized Access"
self.realm = "Authentication Required"
self.get_password(default_get_password)
self.error_handler(default_auth_error)
self.username = None
def get_password(self, f):
self.get_password_callback = f
def error_handler(self, f):
@wraps(f)
def decorated(*args, **kwargs):
res = f(*args, **kwargs)
if type(res) == str:
res = make_response(res)
res.status_code = 401
if 'WWW-Authenticate' not in res.headers.keys():
res.headers['WWW-Authenticate'] = self.authenticate_header()
return res
self.auth_error_callback = decorated
return decorated
def login_required(self, f):
@wraps(f)
def decorated(*args, **kwargs):
self.username = None
auth = request.authorization
if not auth:
return self.auth_error_callback()
password = self.get_password_callback(auth.username)
if not password:
return self.auth_error_callback()
if not self.authenticate(auth, password):
return self.auth_error_callback()
self.username = auth.username
return f(*args, **kwargs)
return decorated
class HTTPBasicAuth(HTTPAuth):
def __init__(self):
super(HTTPBasicAuth, self).__init__()
self.hash_password(None)
def hash_password(self, f):
self.hash_password_callback = f
def authenticate_header(self):
return 'Basic realm="' + self.realm + '"'
def authenticate(self, auth, password):
client_password = auth.password
if self.hash_password_callback:
client_password = self.hash_password_callback(client_password)
return client_password == password
class HTTPDigestAuth(HTTPAuth):
def __init__(self):
super(HTTPDigestAuth, self).__init__()
self.random = SystemRandom()
try:
self.random.random()
except NotImplementedError:
self.random = Random()
def get_nonce(self):
return md5(str(self.random.random()).encode('utf-8')).hexdigest()
def authenticate_header(self):
session["auth_nonce"] = self.get_nonce()
session["auth_opaque"] = self.get_nonce()
return 'Digest realm="' + self.realm + '",nonce="' + session["auth_nonce"] + '",opaque="' + session["auth_opaque"] + '"'
def authenticate(self, auth, password):
if not auth.username or not auth.realm or not auth.uri or not auth.nonce or not auth.response:
return False
if auth.nonce != session.get("auth_nonce") or auth.opaque != session.get("auth_opaque"):
return False
a1 = auth.username + ":" + auth.realm + ":" + password
ha1 = md5(a1.encode('utf-8')).hexdigest()
a2 = request.method + ":" + auth.uri
ha2 = md5(a2.encode('utf-8')).hexdigest()
a3 = ha1 + ":" + auth.nonce + ":" + ha2
response = md5(a3.encode('utf-8')).hexdigest()
return response == auth.response