This commit is contained in:
frogolonso
2026-08-26 12:53:27 +03:00
parent 12b1d02f39
commit 0d1b49ac1b
7 changed files with 133 additions and 34 deletions
+23 -10
View File
@@ -1,5 +1,6 @@
#!/usr/bin/python3
from flask import Flask, make_response, request, redirect
import os
import requests as r
app = Flask(__name__)
database_node_port = 5002
@@ -7,6 +8,8 @@ logic_node_port = 5003
database_node = "database:" + str(database_node_port)
IMG_MIN, IMG_MAX = 1, 16
INTERNAL = os.environ.get("RUSGRAM_INTERNAL", "e9b2f6d14a8c7035e1d0a6b8c4f2e7a9")
DBH = {"X-Internal": INTERNAL}
def bad_request(status="error", code=400):
@@ -31,9 +34,16 @@ def valid_username(username):
return False # имя юзера = имя файла в db/users/
if username.lower().endswith((".html", ".htm")):
return False # имя, которое content мог бы отрендерить как шаблон
if "{{" in username or "{%" in username or "{#" in username:
return False
return True
def clean_value(value):
"""Не даём записать Jinja-конструкции в поля, которые когда-то рендерились как шаблон."""
return isinstance(value, str) and "{{" not in value and "{%" not in value and "{#" not in value
def current_user():
"""Возвращает имя авторизованного юзера или None. Не бросает исключений."""
session = request.cookies.get("session")
@@ -46,7 +56,8 @@ def current_user():
if not username:
return None
try:
resp = r.get(f"http://{database_node}/users/{username}/password", timeout=5)
resp = r.get(f"http://{database_node}/users/{username}/password",
headers=DBH, timeout=5)
if resp.status_code != 200: # PATCH V8: несуществующий юзер -> KeyError -> 500
return None
if resp.json().get("data") != password:
@@ -70,7 +81,8 @@ def login():
if not isinstance(username, str) or not isinstance(password, str):
return bad_request()
try:
resp = r.get(f"http://{database_node}/users/{username}/password", timeout=5)
resp = r.get(f"http://{database_node}/users/{username}/password",
headers=DBH, timeout=5)
correct_password = resp.json()["data"] if resp.status_code == 200 else None
except (r.RequestException, ValueError, KeyError):
return bad_request()
@@ -96,7 +108,8 @@ def register():
# его пароль (остальные поля, включая флаг, сохранялись) => полный захват
# чужого аккаунта без единого запроса на аутентификацию.
try:
exists = r.get(f"http://{database_node}/users/{username}/password", timeout=5)
exists = r.get(f"http://{database_node}/users/{username}/password",
headers=DBH, timeout=5)
if exists.status_code == 200:
return make_response({"status": "user_exists"}, 409)
except r.RequestException:
@@ -104,7 +117,7 @@ def register():
try:
resp = r.post(f"http://{database_node}/users/{username}/password",
json={"value": password}, timeout=5)
json={"value": password}, headers=DBH, timeout=5)
except r.RequestException:
return bad_request()
if resp.status_code == 200:
@@ -123,12 +136,12 @@ def update_personal():
if data is None:
return bad_request()
for field in ("first_name", "second_name", "email"):
if field not in data or not isinstance(data[field], str):
if field not in data or not clean_value(data[field]):
return bad_request()
try:
for field in ("first_name", "second_name", "email"):
r.post(f"http://{database_node}/users/{username}/{field}",
json={"value": data[field]}, timeout=5)
json={"value": data[field]}, headers=DBH, timeout=5)
except r.RequestException:
return bad_request()
return make_response({"status": "ok"}, 200)
@@ -140,11 +153,11 @@ def send_msg():
if username is None:
return unauthorized()
data = json_body()
if data is None or not isinstance(data.get("msg"), str):
if data is None or not clean_value(data.get("msg")):
return bad_request()
try:
r.post(f"http://{database_node}/users/{username}/msg",
json={"value": data["msg"]}, timeout=5)
json={"value": data["msg"]}, headers=DBH, timeout=5)
except r.RequestException:
return bad_request()
return make_response({"status": "ok"}, 200)
@@ -156,7 +169,7 @@ def post_comment():
if username is None:
return unauthorized()
data = json_body()
if data is None or not isinstance(data.get("msg"), str):
if data is None or not clean_value(data.get("msg")):
return bad_request()
# PATCH V8: img_id раньше уходил в URL внутреннего запроса как есть
try:
@@ -167,7 +180,7 @@ def post_comment():
return bad_request()
try:
r.post(f"http://{database_node}/images/{img_id}",
json={"value": [username, data["msg"]]}, timeout=5)
json={"value": [username, data["msg"]]}, headers=DBH, timeout=5)
except r.RequestException:
return bad_request()
return make_response({"status": "ok"}, 200)