Реализован интерфейс согласий

This commit is contained in:
mi
2026-07-23 14:29:16 +03:00
parent b1ed714d5b
commit 31efbf3b69
27 changed files with 817 additions and 110 deletions
+146 -6
View File
@@ -1,4 +1,6 @@
На ВМ выполните:
# Отправка СМС
## На ВМ выполните:
cd /opt/han-chat/backend
umask 077
@@ -46,7 +48,7 @@ with open(sys.argv[1], "w", encoding="utf-8") as file:
PY
Создайте функцию отправки:
## Создайте функцию отправки:
send_sms_smoke() {
docker compose --env-file .env --profile ops run --rm --no-deps \
@@ -66,13 +68,13 @@ send_sms_smoke() {
'
}
Отправка:
## Отправка:
send_sms_smoke
Ожидается:
HTTP 202 и JSON с sms_message_id.
Проверьте журнал:
## Проверьте журнал:
SELECT
id,
phone_masked,
@@ -86,6 +88,144 @@ FROM sms.sms_outbound_message
ORDER BY created_at DESC
LIMIT 5;
После проверки удалите секретные данные:
## После проверки удалите секретные данные:
shred -u "$REQUEST_FILE" 2>/dev/null || rm -f "$REQUEST_FILE"
unset SMS_TOKEN OTP_CODE TEST_PHONE CHALLENGE_ID REQUEST_FILE
unset SMS_TOKEN OTP_CODE TEST_PHONE CHALLENGE_ID REQUEST_FILE
# Тесты
Выполняйте на ВМ из `/opt/han-chat/backend`.
### 1. Проверить запрет публичного internal API
```bash
PUBLIC_WEB_URL=$(python3 - <<'PY'
from pathlib import Path
for line in Path(".env").read_text().splitlines():
if line.startswith("PUBLIC_WEB_URL="):
print(line.split("=", 1)[1].strip().strip("\"'"))
break
PY
)
curl -sS -o /dev/null -w 'HTTP %{http_code}\n' \
"$PUBLIC_WEB_URL/internal/sms/v1/messages/00000000-0000-0000-0000-000000000000"
```
Ожидается:
```text
HTTP 404
```
### 2. Проверить callback с неправильного IP
```bash
curl -sS -o /dev/null -w 'HTTP %{http_code}\n' \
-X POST \
-H 'Content-Type: application/json' \
--data '[]' \
"$PUBLIC_WEB_URL/callbacks/idgtl/sms"
```
Ожидается:
```text
HTTP 403
```
Заголовок `X-Forwarded-For` не должен позволять обойти ограничение.
### 3. Проверить Basic auth внутри Docker-сети
Получите credentials из `.env`:
```bash
CB_USER=$(python3 - <<'PY'
from pathlib import Path
for line in Path(".env").read_text().splitlines():
if line.startswith("IDGTL_SMS_CALLBACK_USERNAME="):
print(line.split("=", 1)[1].strip().strip("\"'"))
break
PY
)
CB_PASS=$(python3 - <<'PY'
from pathlib import Path
for line in Path(".env").read_text().splitlines():
if line.startswith("IDGTL_SMS_CALLBACK_PASSWORD="):
print(line.split("=", 1)[1].strip().strip("\"'"))
break
PY
)
export CB_USER CB_PASS
```
Неверные credentials:
```bash
docker compose --env-file .env --profile ops run --rm --no-deps \
--entrypoint sh toolbox -ec '
curl -sS -o /dev/null -w "HTTP %{http_code}\n" \
-u invalid:invalid \
-H "Content-Type: application/json" \
--data "[]" \
http://sms-service:8080/callbacks/idgtl/sms
'
```
Ожидается `HTTP 401`.
Правильные credentials:
```bash
docker compose --env-file .env --profile ops run --rm --no-deps \
--entrypoint sh -e CB_USER -e CB_PASS toolbox -ec '
curl -sS -o /dev/null -w "HTTP %{http_code}\n" \
-u "$CB_USER:$CB_PASS" \
-H "Content-Type: application/json" \
--data "[]" \
http://sms-service:8080/callbacks/idgtl/sms
'
```
Ожидается `HTTP 422`: авторизация прошла, но пустой callback-массив невалиден.
После проверки:
```bash
unset CB_USER CB_PASS
```
### 4. Проверить реальный callback Direct
После тестовой SMS:
```sql
SELECT
id,
send_status,
delivery_status,
provider_message_id,
callback_last_at,
sent_at,
delivered_at
FROM sms.sms_outbound_message
ORDER BY created_at DESC
LIMIT 5;
```
Успешный реальный callback подтверждается:
- `callback_last_at IS NOT NULL`;
- `delivery_status = sent` или `delivered`;
- заполняются `sent_at`/`delivered_at`.
Дополнительно:
```bash
docker compose --env-file .env logs --since=30m nginx sms-service
```
Для callback должен быть ответ `204`. Только реальный запрос Direct может полноценно подтвердить IP allowlist.