feat(auth): Separate Login, Register, and Profile views with robust validations and SQL-safe backend

This commit is contained in:
2026-09-17 10:22:02 +00:00
parent 5f3abba27c
commit 4e450d758b
5 changed files with 392 additions and 126 deletions
+78 -18
View File
@@ -164,8 +164,53 @@ class FeedbackHandler(http.server.BaseHTTPRequestHandler):
self.wfile.write(json.dumps({"error": f"Invalid JSON: {e}"}).encode("utf-8"))
return
# 1. User Authentication (Login & Auto-Registration)
if path in ["/api/auth/login", "/api/bugs/auth/login", "/api/auth/register"]:
# 1a. User Login
if path in ["/api/auth/login", "/api/bugs/auth/login"]:
username = str(payload.get("username", "")).strip()
password_hash = str(payload.get("passwordHash", "")).strip()
if not username or not password_hash:
self.send_response(400)
self._set_cors()
self.end_headers()
self.wfile.write(json.dumps({"success": False, "message": "Введите логин и пароль"}).encode("utf-8"))
return
import re
if not re.match(r"^[a-zA-Zа-яА-ЯёЁ0-9_-]{3,20}$", username):
self.send_response(400)
self._set_cors()
self.end_headers()
self.wfile.write(json.dumps({"success": False, "message": "Логин может содержать только буквы, цифры, '-' и '_' (3-20 симв.)"}).encode("utf-8"))
return
user = db.get_user_by_username(username)
if not user:
self.send_response(404)
self._set_cors()
self.end_headers()
self.wfile.write(json.dumps({"success": False, "message": "Пользователь с таким логином не найден. Зарегистрируйтесь."}).encode("utf-8"))
return
res = db.authenticate_user(username, password_hash)
if not res or not res.get("success"):
self.send_response(401)
self._set_cors()
self.end_headers()
self.wfile.write(json.dumps(res or {"success": False, "message": "Неверный логин или пароль"}).encode("utf-8"))
return
data = json.dumps(res, ensure_ascii=False).encode("utf-8")
self.send_response(200)
self._set_cors()
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
return
# 1b. User Registration
if path in ["/api/auth/register", "/api/bugs/auth/register"]:
username = str(payload.get("username", "")).strip()
password_hash = str(payload.get("passwordHash", "")).strip()
initial_data = payload.get("initialData")
@@ -174,27 +219,42 @@ class FeedbackHandler(http.server.BaseHTTPRequestHandler):
self.send_response(400)
self._set_cors()
self.end_headers()
self.wfile.write(json.dumps({"success": False, "message": "Имя пользователя и пароль обязательны"}).encode("utf-8"))
self.wfile.write(json.dumps({"success": False, "message": "Введите логин и пароль для регистрации"}).encode("utf-8"))
return
import re
if not re.match(r"^[a-zA-Zа-яА-ЯёЁ0-9_-]{3,20}$", username):
self.send_response(400)
self._set_cors()
self.end_headers()
self.wfile.write(json.dumps({"success": False, "message": "Логин может содержать только буквы, цифры, '-' и '_' (3-20 симв.)"}).encode("utf-8"))
return
if len(password_hash) < 1:
self.send_response(400)
self._set_cors()
self.end_headers()
self.wfile.write(json.dumps({"success": False, "message": "Пароль не может быть пустым"}).encode("utf-8"))
return
if username.lower() in ["гость", "guest", "admin", "null", "undefined", "system", "anonymous"]:
self.send_response(400)
self._set_cors()
self.end_headers()
self.wfile.write(json.dumps({"success": False, "message": "Это имя пользователя зарезервировано"}).encode("utf-8"))
return
# Check if user exists
user = db.get_user_by_username(username)
if user:
res = db.authenticate_user(username, password_hash)
if not res or not res.get("success"):
self.send_response(401)
self._set_cors()
self.end_headers()
self.wfile.write(json.dumps(res or {"success": False, "message": "Неверный пароль"}).encode("utf-8"))
return
resp_json = res
else:
# Auto-register new user
res = db.register_user(username, password_hash, initial_data)
resp_json = res
self.send_response(409)
self._set_cors()
self.end_headers()
self.wfile.write(json.dumps({"success": False, "message": "Пользователь с таким логином уже существует"}).encode("utf-8"))
return
data = json.dumps(resp_json, ensure_ascii=False).encode("utf-8")
self.send_response(200)
res = db.register_user(username, password_hash, initial_data)
data = json.dumps(res, ensure_ascii=False).encode("utf-8")
self.send_response(200 if res.get("success") else 400)
self._set_cors()
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(data)))
+20
View File
@@ -186,6 +186,7 @@ def test_auth_system():
"getCurrentUser",
"isGuest",
"login",
"register",
"loginAsGuest",
"logout",
"getUserData",
@@ -193,6 +194,8 @@ def test_auth_system():
"flushSave",
"scheduleSave",
"apiPost",
"validateUsername",
"validatePassword"
]
for m in auth_methods:
if m in auth_code:
@@ -205,6 +208,19 @@ def test_auth_system():
record("auth.js has debounced save (scheduleSave)", "PASS" if "scheduleSave" in auth_code else "FAIL")
record("auth.js has flushSave for game-over sync", "PASS" if "flushSave" in auth_code else "FAIL")
# Check HTML auth views
index_html_path = os.path.join(GAME_DIR, "index.html")
with open(index_html_path, "r", encoding="utf-8") as f:
html_code = f.read()
record("Auth Login view present", "PASS" if 'id="auth-login-view"' in html_code else "FAIL")
record("Auth Register view present", "PASS" if 'id="auth-register-view"' in html_code else "FAIL")
record("Auth Logged-in view present", "PASS" if 'id="auth-logged-in-view"' in html_code else "FAIL")
record("Auth switch to register button present", "PASS" if 'id="btn-auth-to-register"' in html_code else "FAIL")
record("Auth do register button present", "PASS" if 'id="btn-auth-do-register"' in html_code else "FAIL")
record("Auth guest button present", "PASS" if 'id="btn-auth-guest"' in html_code else "FAIL")
record("Auth logout button present", "PASS" if 'id="btn-auth-logout"' in html_code else "FAIL")
# Server-side SQLite DB tests
print("\n--- 4b. SQLite DB & Auth API CRUD Tests ---")
sys.path.insert(0, os.path.join(BASE_DIR, "scripts"))
@@ -233,6 +249,10 @@ def test_auth_system():
record("SQLite DB: reject wrong password", "PASS" if bad_auth and not bad_auth.get("success") else "FAIL",
"Bad password was accepted!" if bad_auth and bad_auth.get("success") else "")
# Test duplicate registration rejection
dup_reg = pvz_db.register_user("kopri", "c63bc483")
record("SQLite DB: reject duplicate user registration", "PASS" if not dup_reg.get("success") else "FAIL")
# Test save profile (update newbie coins)
save_res = pvz_db.save_user_profile("newbie_test", {"coins": 999, "level": 3, "highScore": 2500})
record("SQLite DB: save_user_profile() succeeds", "PASS" if save_res.get("success") else "FAIL")