2025-12-25 upload

This commit is contained in:
“shengyudong”
2025-12-25 11:16:59 +08:00
commit 322ac74336
2241 changed files with 639966 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
"""
A standalone, minimal htpasswd parser.
This implementation currently supports bcrypt and SHA1 passwords. SHA1 is insecure.
"""
from __future__ import annotations
import base64
import hashlib
from pathlib import Path
import bcrypt
class HtpasswdFile:
def __init__(self, content: str):
"""
Create a HtpasswdFile from a string.
"""
self.users: dict[str, str] = {}
for line in content.splitlines():
line = line.strip()
if not line or line.startswith("#"):
continue
if ":" not in line:
raise ValueError(f"Malformed htpasswd line: {line!r}")
user, pwhash = line.split(":", 1)
if not user:
raise ValueError(f"Malformed htpasswd line: {line!r}")
is_sha = pwhash.startswith("{SHA}")
is_bcrypt = pwhash.startswith(("$2y$", "$2b$", "$2a$"))
if not is_sha and not is_bcrypt:
raise ValueError(f"Unsupported htpasswd format for user {user!r}")
self.users[user] = pwhash
@classmethod
def from_file(cls, path: Path) -> HtpasswdFile:
"""
Initializes and loads an htpasswd file.
Args:
path: The path to the htpasswd file.
Raises:
OSError: If the file cannot be read.
ValueError: If the file is malformed.
"""
try:
content = path.read_text("utf-8")
except FileNotFoundError:
raise OSError(f"Htpasswd file not found: {path}") from None
return cls(content)
def check_password(self, username: str, password: str) -> bool:
"""
Checks if a username and password combination is valid.
Args:
username: The username to check.
password: The password to check.
Returns:
True if the password is valid, False otherwise.
"""
pwhash = self.users.get(username)
if pwhash is None:
return False
pwhash = pwhash.split(":", 1)[0]
if pwhash.startswith("{SHA}"):
# Apache's {SHA} is base64-encoded SHA-1.
# https://httpd.apache.org/docs/2.4/misc/password_encryptions.html
digest = hashlib.sha1(password.encode("utf-8")).digest()
expected = base64.b64encode(digest).decode("ascii")
return pwhash[5:] == expected
else: # pwhash.startswith(("$2y$", "$2b$", "$2a$")):
return bcrypt.checkpw(password.encode("utf-8"), pwhash.encode("utf-8"))