feat(doctor): complete IRIMC import feature — claim flow, least-privilege importer, unique import key
- 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>
This commit is contained in:
@@ -70,11 +70,17 @@ class SystemOwnerCommand extends Command
|
||||
}
|
||||
}
|
||||
|
||||
// least privilege: کاربر سیستمی فقط ROLE_IMPORTER میگیرد (دسترسی فقط به اندپوینت
|
||||
// ایمپورت پزشک). اگر از نسخههای قبلی ROLE_ADMIN دارد، حذف میشود.
|
||||
$roles = $user->getRoles();
|
||||
if (!in_array('ROLE_ADMIN', $roles, true)) {
|
||||
$roles[] = 'ROLE_ADMIN';
|
||||
$user->setRoles(array_values(array_unique($roles)));
|
||||
if (in_array('ROLE_ADMIN', $roles, true)) {
|
||||
$roles = array_values(array_diff($roles, ['ROLE_ADMIN']));
|
||||
$io->note('ROLE_ADMIN از کاربر سیستمی حذف شد (least privilege).');
|
||||
}
|
||||
if (!in_array('ROLE_IMPORTER', $roles, true)) {
|
||||
$roles[] = 'ROLE_IMPORTER';
|
||||
}
|
||||
$user->setRoles(array_values(array_unique($roles)));
|
||||
|
||||
if ($password !== null) {
|
||||
$user->setPasswordHash($this->hasher->hashPassword($user, (string) $password));
|
||||
|
||||
Reference in New Issue
Block a user