- Extract import logic from AdminApiController into DoctorImportService (thin DoctorImportController keeps the same route/contract) - Surrogate users get marker role ROLE_UNCLAIMED_DOCTOR (+ backfill command app:doctors:backfill-surrogate-role) enabling safe deletion after claim - DB-level UNIQUE (source, medical_system_code) + concurrent-import retry - Doctor profile claim flow (climed.md): shahkar + PersonInfo identity checks via existing ApiIrService, Persian name normalization (PersianText), pessimistic-lock race protection, DoctorClaimRequest audit table (national code hashed, mobile masked), doctor_claim rate limiter, public claim-info endpoint, welcome SMS - Admin support tools: manual transfer endpoint + paginated doctor-claims audit list + owner_status filter/fields in admin doctors list - Least privilege: system owner now gets ROLE_IMPORTER (ROLE_ADMIN stripped), import endpoint accepts ADMIN|IMPORTER, isStaff includes IMPORTER - Headless crawler login: X-Service-Token header bypasses captcha only (rate limit + password checks intact; empty env = no bypass) - docs: doctor-claim.md (new), doctor-import.md, admin.md, doctor.md - tests: DoctorImportTest (6), DoctorClaimTest (11), PersianTextTest (5) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
38 lines
1.1 KiB
YAML
38 lines
1.1 KiB
YAML
framework:
|
|
rate_limiter:
|
|
# OTP send-code: max 5 requests per hour per IP (prevents SMS flood)
|
|
send_code:
|
|
policy: 'sliding_window'
|
|
limit: 5
|
|
interval: '60 minutes'
|
|
|
|
# Login: max 10 attempts per minute per IP (brute force protection)
|
|
login:
|
|
policy: 'fixed_window'
|
|
limit: 10
|
|
interval: '1 minute'
|
|
|
|
# OTP verify: max 10 attempts per 15 minutes per IP
|
|
verify_code:
|
|
policy: 'sliding_window'
|
|
limit: 10
|
|
interval: '15 minutes'
|
|
|
|
# Token issuance (oauth/token, otp-login): max 10 per 5 minutes per IP
|
|
token_issue:
|
|
policy: 'sliding_window'
|
|
limit: 10
|
|
interval: '5 minutes'
|
|
|
|
# Password reset: max 5 per hour per IP
|
|
password_reset:
|
|
policy: 'sliding_window'
|
|
limit: 5
|
|
interval: '60 minutes'
|
|
|
|
# Doctor profile claim: max 5 attempts per hour per (user, doctor) — ضد brute-force هویت
|
|
doctor_claim:
|
|
policy: 'sliding_window'
|
|
limit: 5
|
|
interval: '60 minutes'
|