Skip to content

Commit f2b3dc7

Browse files
committed
Use passlib.apache.HtpasswdFile instead of bcrypt to handle the databse file
1 parent ad7f045 commit f2b3dc7

4 files changed

Lines changed: 24 additions & 17 deletions

File tree

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@
3636
'redis',
3737
],
3838
extras_requires={
39-
'bcrypt': ['bcrypt']
39+
'passlib': ['passlib==1.7.4']
40+
'bcrypt': ['bcrypt==4.0.1']
4041
},
4142
zip_safe=False,
4243
entry_points={

test-requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ nose2
33
six
44
redis
55
wrapt<=1.12.1;python_version<="3.4"
6-
bcrypt
6+
passlib==1.7.4
7+
bcrypt==4.0.1

tests/test_auth_plugins.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
""" Unit tests for Authentication plugins"""
44

5-
from websockify.auth_plugins import BasicHTTPAuth, HtPasswdAuth, AuthenticationError
5+
from websockify.auth_plugins import BasicHTTPAuth, HtpasswdAuth, AuthenticationError
66
import unittest
77
import tempfile
88

@@ -28,7 +28,7 @@ def test_garbage_auth(self):
2828
headers = {'Authorization': 'Basic xxxxxxxxxxxxxxxxxxxxxxxxxxxx'}
2929
self.assertRaises(AuthenticationError, self.plugin.authenticate, headers, 'localhost', '1234')
3030

31-
class HtPasswdAuthTestCase(unittest.TestCase):
31+
class HtpasswdAuthTestCase(unittest.TestCase):
3232

3333

3434
def setUp(self):
@@ -38,7 +38,7 @@ def setUp(self):
3838
self._temporary_htpasswd_file.write(file_content.encode('utf-8'))
3939
self._temporary_htpasswd_file.close()
4040

41-
self.plugin = HtPasswdAuth(self._temporary_htpasswd_file.name)
41+
self.plugin = HtpasswdAuth(self._temporary_htpasswd_file.name)
4242

4343
def test_no_auth(self):
4444
headers = {}

websockify/auth_plugins.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import bcrypt
2-
1+
try:
2+
from passlib.apache import HtpasswdFile
3+
except ImportError:
4+
HtpasswdFile: None
35
class BasePlugin():
46
def __init__(self, src=None):
57
self.source = src
@@ -25,10 +27,9 @@ def __init__(self, expected, actual):
2527
self.expected_origin = expected
2628
self.actual_origin = actual
2729

28-
super().__init__(
29-
response_msg='Invalid Origin',
30-
log_msg="Invalid Origin Header: Expected one of "
31-
"%s, got '%s'" % (expected, actual))
30+
super().__init__(response_msg='Invalid Origin',
31+
log_msg="Invalid Origin Header: Expected one of "
32+
"%s, got '%s'" % (expected, actual))
3233

3334

3435
class BasicHTTPAuth():
@@ -78,21 +79,25 @@ def demand_auth(self):
7879
raise AuthenticationError(response_code=401,
7980
response_headers={'WWW-Authenticate': 'Basic realm="Websockify"'})
8081

81-
class HtPasswdAuth(BasicHTTPAuth):
82+
class HtpasswdAuth(BasicHTTPAuth):
8283
"""Verifies Basic Auth headers against a htpasswd database. Specify src as the path to the htpasswd file"""
8384

8485
def __init__(self, src=None):
8586
self.src = src
87+
if HtpasswdFile is None:
88+
raise AuthenticationError(response_code=500, response_msg=f"Internal Server Error")
8689

8790
def validate_creds(self, username, password):
8891
if self.src == None:
8992
return False
9093
try:
91-
with open(self.src, 'r') as file:
92-
for line in file:
93-
stored_user, stored_hash = line.strip().split(':', 1)
94-
if stored_user == username:
95-
return bcrypt.checkpw(password.encode('utf-8'), stored_hash.encode('utf-8'))
94+
#TODO: Add a argument or config to change the HtpasswdFile scheme
95+
htfile = HtpasswdFile(self.src, new=False, default_scheme="bcrypt", encoding="utf-8")
96+
isvalid_hash = htfile.check_password(username, password)
97+
if isvalid_hash == None:
98+
#log user not found
99+
raise AuthenticationError(response_code=403)
100+
return isvalid_hash
96101
except (FileNotFoundError, PermissionError, OSError, ValueError) as e:
97102
#log error "%s: %s" % (type(e).__name__, e)
98103
raise AuthenticationError(response_code=500, response_msg=f"Internal Server Error")

0 commit comments

Comments
 (0)