Remove guest login: every user registers with a phone number

POST /api/auth/session is gone (404). Existing is_guest rows keep refresh and
upgrade-by-phone until the demo/guest rows are purged; the code that serves
them is listed in docs/security-before-production.md §10. Tests that used the
endpoint as a fixture now use accounts/testing.guest_session.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-05 17:38:18 +03:30
parent 03e81abe7e
commit 8835e01595
18 changed files with 101 additions and 198 deletions

View File

@@ -23,15 +23,15 @@
|---|---|---|---|
| ۱ | کلیدِ امضای توکن روی سرور همان رشته‌ی داخلِ ریپو است | بحرانی — جعلِ ورود به‌جای هر کاربر | کد آماده؛ اعمال روی سرور |
| ۲ | ۷ حسابِ دمو با کدِ ورودِ ثابت روی پروداکشن | بالا | `.env` سرور |
| ۳ | داده‌ی دمو و حسابِ مهمانِ عمومیِ `frontend-test-device` | بالا | دیتابیسِ سرور |
| ۳ | داده‌ی دمو و حسابِ مهمانِ نمایشیِ `frontend-test-device` | بالا | دیتابیسِ سرور |
| ۴ | `/api/health` جزئیاتِ داخلی را به همه نشان می‌دهد | متوسط | کد |
| ۵ | Swagger، ReDoc و schemaی کاملِ API عمومی‌اند | متوسط | کد |
| ۷ | پنلِ `/admin/` روی دامنه‌ی عمومی | متوسط | nginx + کد |
| ۸ | سرور بدونِ ArvanCloud روی پورتِ ۸۰ هم جواب می‌دهد | متوسط | ادمینِ سرور |
| ۹ | کشِ مشترک (Redis) نیست؛ محدودیت‌ها per-worker‌اند | متوسط | `.env` سرور |
| ۱۰ | ورودِ مهمان فقط با `deviceId` است | کم (طراحی) | کد |
| ۱۰ | حساب‌های مهمانِ قدیمی (دمو و کاربرانِ پیش از حذفِ ورودِ مهمان) هنوز در دیتابیس‌اند | کم | دیتابیسِ سرور |
| ۱۱ | کدِ OTP بی‌هش در دیتابیس و در فهرستِ پنل | کم | کد |
| ۱۲ | هزینه‌ی دستیارِ AI با ساختِ حسابِ مهمان قابلِ بالا بردن است | کم (مالی) | کد |
| ۱۲ | هزینه‌ی دستیارِ AI با ساختِ حسابِ تازه (هر شماره ۳ سوالِ رایگان) قابلِ بالا بردن است | کم (مالی) | کد |
| ۱۴ | SQLite روی دیسکِ مشترکِ پُر، بدونِ بکاپِ خودکارِ بیرون از ماشین | کم (داده) | زیرساخت |
| ۱۵ | دسترسیِ سرور: حسابِ مشترک، رمز، sudoِ کامل | بالا | ادمینِ سرور |
| ۱۶ | فرانت توکن را در `localStorage` نگه می‌دارد | کم | فرانت |
@@ -85,15 +85,15 @@
از تستِ دستیار؛ همراهِ بقیه پاک شوند.
- **چیست:** کاربرهای ۶ (`deviceId = frontend-test-device`) و ۱۰ تا ۱۵، با درس، گفتگو،
تیکت، خانواده و زیرمجموعه، روی دیتابیسِ پروداکشن ساخته شده‌اند. `frontend-test-device`
در ریپو نوشته شده؛ هر کس `POST /api/auth/session` با همین `deviceId` بزند، توکنِ همان
حساب را می‌گیرد.
در ریپو نوشته شده. `POST /api/auth/session` حذف شده، پس با آن دیگر توکن نمی‌دهد؛ ولی خودِ
ردیف‌ها (با `is_guest=True`) تا پاک نشوند در دیتابیس‌اند.
- **خطر:** داده‌ی ساختگی در آمار و جدولِ رتبه‌بندیِ واقعی؛ یک حسابِ عمومی که هر کس
می‌تواند با آن کار کند (ثبتِ تیکت، گفتگو با AI به هزینه‌ی ما).
- **رفع:** پیش از کاربرِ واقعی پاک شوند. `purge_demo_data` این‌ها را **نمی‌گیرد** (از مسیرِ
واقعیِ viewها ساخته شده‌اند)؛ باید با شناسه پاک شوند، پیش از آن بکاپ. همراهِ آن پیش‌فرضِ
`DEMO_GUEST_DEVICE_IDS` در `config/settings.py` (که حذفِ این مهمان را با `POST /api/user/delete`
می‌بندد) هم برداشته شود.
- **چطور بسنجید:** `POST /api/auth/session` با `frontend-test-device` باید `isNewUser: true` بدهد.
- **چطور بسنجید:** در پنل یا shell، `User.objects.filter(is_guest=True).count()` پس از پاک‌سازی باید صفر باشد.
## ۴. `/api/health` عمومی و پرجزئیات
@@ -157,16 +157,15 @@
- **رفع:** `CACHE_URL=redis://127.0.0.1:6379/1` (db0 مالِ اپِ دیگری است) و ری‌استارت.
- **چطور بسنجید:** `/api/health` → `checks.cache.backend` نباید `LocMemCache` باشد.
## ۱۰. ورودِ مهمان فقط با `deviceId`
## ۱۰. حساب‌های مهمانِ قدیمی
- **چیست:** `POST /api/auth/session` با هر `deviceId` یا حسابِ مهمانِ همان دستگاه را
برمی‌گرداند یا می‌سازد (`accounts/views.py::GuestSessionView`). هیچ رازِ دیگری در کار نیست.
- **خطر:** هر کس `deviceId`ِ یک مهمان را بداند (از `localStorage`، لاگ، اسکرین‌شات) وارد
حسابِ او می‌شود. بعد از ثبت‌نام با شماره این راه بسته می‌شود (مهمان دیگر مهمان نیست) و همه‌ی
نشست‌هایی که پیش از آن گرفته شده بودند باطل می‌شوند. شناسه‌ی دستگاه در پنل فقط برای ابرکاربر
نمایش داده می‌شود.
- **رفع:** برای مهمان هم یک رازِ تصادفیِ سمتِ سرور صادر شود و ورودِ دوباره با همان راز باشد،
نه با `deviceId`ِ خام.
- **چیست:** ورودِ مهمان (`POST /api/auth/session`) حذف شده و هر کاربرِ تازه با شماره ثبت‌نام می‌کند.
ردیف‌های مهمانِ پیشین (`User.is_guest=True`، شامل دموها) در دیتابیس مانده‌اند و کدِ ارتقای مهمان به
حسابِ شماره‌دار (`LoginView`) و throttleهای مهمان‌محور هنوز برایشان کار می‌کنند.
- **خطر:** مهمانِ قدیمی هنوز با `refreshToken` ذخیره‌شده در مرورگرش وارد می‌ماند؛ شناسه‌ی دستگاه دیگر
راهِ ورود نیست.
- **رفع:** پس از پاک‌سازیِ دموها (بند ۳) و دیدنِ این‌که مهمانِ واقعی نمانده، کدِ مهمان
(`is_guest`، `GuestAware*Throttle`، ارتقا) حذف شود. تا آن روز عمداً نگه داشته شده.
## ۱۱. کدِ OTP بی‌هش در دیتابیس
@@ -179,8 +178,8 @@
## ۱۲. هزینه‌ی دستیارِ AI
- **چیست:** هر کاربر روزی ۳ سؤالِ رایگان دارد (`AI_DAILY_QUOTA_FREE`)
و بعد فقط با اعتبارِ خریده‌شده؛ ۲۰ در ساعت. مهمان‌ها بر اساسِ IP محدود می‌شوند ولی هر IP ساعتی
۶۰ حسابِ مهمانِ تازه می‌سازد (`guest_session`)، و هر حسابِ تازه ۳ سؤالِ رایگانِ تازه دارد. سقفِ
و بعد فقط با اعتبارِ خریده‌شده؛ ۲۰ در ساعت. هر شماره‌ی تازه ۳ سؤالِ رایگان می‌گیرد و ساختنِ شماره با سقفِ پیامک
(بند ۲۴) محدود است. سقفِ
روزانه‌ی کلِ سامانه (`AI_GLOBAL_DAILY_MAX`، ۳۰۰۰) خرجِ کل را محدود می‌کند. طولِ سؤال حداکثر ۲۰۰۰ نویسه است.
- **خطر:** با چند IP، اعتبارِ GapGPT در چند ساعت تمام می‌شود (اعتبار یک بار در ۲۰۲۶-۰۹-۲۵
به صفر رسید، البته به دلیلِ دیگر).
@@ -325,6 +324,7 @@
| حفاظ | تست |
|---|---|
| ورودِ مهمان وجود ندارد (`POST /api/auth/session` ۴۰۴ می‌دهد و کاربر نمی‌سازد)؛ حسابِ مهمانِ قدیمی همچنان refresh و ارتقا دارد | `accounts/tests.py` (`test_guest_login_endpoint_is_gone`)، `accounts/tests_guest_refresh.py` |
| پلنِ هر خرید روی سفارش؛ خرید اشتراکِ فعال را دست نمی‌زند؛ یک کدِ تخفیف پیش از درگاه؛ امتیاز فقط از جوابِ ثبت‌شده | `payment/tests_audit_fixes.py`، `accounts/tests_audit_fixes.py` |
| لینکِ پرداخت (`/api/payment/orders/{id}/pay`) فقط با امضای کوتاه‌عمرِ همراهِ `paymentUrl` باز می‌شود | `payment/tests.py` (`PaymentRedirectViewTests`) |
| توکنِ تمدید فقط به‌صورتِ هشِ SHA-256 ذخیره می‌شود | `accounts/tests.py` (`RefreshTokenStoredHashedTests`) |