diff --git a/src/Event/UserCreatedEvent.php b/src/Event/UserCreatedEvent.php new file mode 100644 index 0000000..3f706f0 --- /dev/null +++ b/src/Event/UserCreatedEvent.php @@ -0,0 +1,25 @@ +newUser; + } + + public function getActingUser(): User + { + return $this->actingUser; + } +} diff --git a/src/EventSubscriber/UserSubscriber.php b/src/EventSubscriber/UserSubscriber.php new file mode 100644 index 0000000..3e27e14 --- /dev/null +++ b/src/EventSubscriber/UserSubscriber.php @@ -0,0 +1,55 @@ + 'onUserCreated', + ]; + } + + public function onUserCreated(UserCreatedEvent $event): void + { + $user = $event->getNewUser(); + $actingUser = $event->getActingUser(); + + // 1. Generate Token (If logic was moved here, otherwise assume UserService set it) + // If the token generation logic is still in UserService, just send the email here. + // If you moved generating the token here, do it now. + + // 2. Send Email + // Note: You might need to pass the token in the Event if it's not stored in the DB entity + // or generate a new one here if appropriate. + if ($user->getPasswordToken()) { + $this->emailService->sendPasswordSetupEmail($user, $user->getPasswordToken()); + } + + // 3. Log the creation + $this->loggerService->logUserCreated($user->getId(), $actingUser->getId()); + + // 4. Create the Audit Action + $this->actionService->createAction( + "Create new user", + $actingUser, + null, + $user->getUserIdentifier() + ); + } +} \ No newline at end of file diff --git a/src/Service/UserService.php b/src/Service/UserService.php index 16c3c34..a8078a4 100644 --- a/src/Service/UserService.php +++ b/src/Service/UserService.php @@ -8,7 +8,6 @@ use App\Entity\Roles; use App\Entity\User; use App\Entity\UserOrganizatonApp; use App\Entity\UsersOrganizations; -use App\Service\AwsService; use DateTimeImmutable; use DateTimeZone; use Doctrine\ORM\EntityManagerInterface; @@ -17,25 +16,24 @@ use Exception; use League\Bundle\OAuth2ServerBundle\Model\AccessToken; use Random\RandomException; use RuntimeException; -use SebastianBergmann\CodeCoverage\Util\DirectoryCouldNotBeCreatedException; use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\HttpFoundation\File\Exception\FileException; +use App\Event\UserCreatedEvent; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; class UserService { public const NOT_FOUND = 'Entity not found'; - public function __construct(private readonly EntityManagerInterface $entityManager, - private readonly Security $security, - private readonly AwsService $awsService, - private readonly LoggerService $loggerService, - private readonly ActionService $actionService, - private readonly EmailService $emailService, - private readonly OrganizationsService $organizationsService, - - - + public function __construct(private readonly EntityManagerInterface $entityManager, + private readonly Security $security, + private readonly AwsService $awsService, + private readonly LoggerService $loggerService, + private readonly ActionService $actionService, + private readonly EmailService $emailService, + private readonly OrganizationsService $organizationsService, + private readonly EventDispatcherInterface $eventDispatcher ) { @@ -185,9 +183,9 @@ class UserService try { $this->awsService->PutDocObj($_ENV['S3_PORTAL_BUCKET'], $picture, $customFilename, $extension, 'profile/'); $this->loggerService->logAWSAction( - 'Profile picture uploaded to S3',[ - 'user_id' => $user->getId(), - 'filename' => $customFilename, + 'Profile picture uploaded to S3', [ + 'user_id' => $user->getId(), + 'filename' => $customFilename, ]); $user->setPictureUrl('profile/' . $customFilename); } catch (FileException $e) { @@ -468,7 +466,7 @@ class UserService $user->setName(trim($user->getName())); $user->setSurname(trim($user->getSurname())); $user->setEmail(trim($user->getEmail())); - if($setPassword) { + if ($setPassword) { //FOR SETTING A DEFAULT RANDOM PASSWORD OF 50 CHARACTERS until user set his own password try { $user->setPassword(bin2hex(random_bytes(50))); @@ -480,7 +478,7 @@ class UserService } } - if($picture) { + if ($picture) { $this->handleProfilePicture($user, $picture); } } @@ -489,10 +487,11 @@ class UserService * Handle existing user being added to an organization */ public function addExistingUserToOrganization( - User $existingUser, + User $existingUser, Organizations $org, - User $actingUser, - ): int { + User $actingUser, + ): int + { try { $uoId = $this->handleExistingUser($existingUser, $org); @@ -513,9 +512,9 @@ class UserService return $uoId; } catch (\Exception $e) { $this->loggerService->logError('Error linking existing user to organization: ' . $e->getMessage(), [ - 'target_user_id' => $existingUser->getId(), + 'target_user_id' => $existingUser->getId(), 'organization_id' => $org->getId(), - 'acting_user_id' => $actingUser->getId(), + 'acting_user_id' => $actingUser->getId(), ]); throw $e; } @@ -528,17 +527,20 @@ class UserService { try { $this->formatUserData($user, $picture, true); + + // Generate token here if it's part of the user persistence flow + $token = $this->generatePasswordToken($user); + $this->entityManager->persist($user); $this->entityManager->flush(); + + $this->eventDispatcher->dispatch(new UserCreatedEvent($user, $actingUser)); - $this->loggerService->logUserCreated($user->getId(), $actingUser->getId()); - $token = $this->generatePasswordToken($user); - $this->emailService->sendPasswordSetupEmail($user, $token); - $this->actionService->createAction("Create new user", $actingUser, null, $user->getUserIdentifier()); } catch (\Exception $e) { + // Error logging remains here because the event won't fire if exception occurs $this->loggerService->logError('Error creating new user: ' . $e->getMessage(), [ 'target_user_email' => $user->getEmail(), - 'acting_user_id' => $actingUser->getId(), + 'acting_user_id' => $actingUser->getId(), ]); throw $e; } @@ -548,10 +550,11 @@ class UserService * Link newly created user to an organization */ public function linkUserToOrganization( - User $user, + User $user, Organizations $org, - User $actingUser, - ): UsersOrganizations { + User $actingUser, + ): UsersOrganizations + { try { $uo = new UsersOrganizations(); $uo->setUsers($user); @@ -567,7 +570,7 @@ class UserService $org->getId(), $actingUser->getId(), $uo->getId(), - + ); $this->actionService->createAction( @@ -582,9 +585,9 @@ class UserService return $uo; } catch (\Exception $e) { $this->loggerService->logError('Error linking user to organization: ' . $e->getMessage(), [ - 'target_user_id' => $user->getId(), + 'target_user_id' => $user->getId(), 'organization_id' => $org->getId(), - 'acting_user_id' => $actingUser->getId(), + 'acting_user_id' => $actingUser->getId(), ]); throw $e; } @@ -597,11 +600,11 @@ class UserService $token = $this->generatePasswordToken($user, $org->getId()); $this->emailService->sendExistingUserNotificationEmail($user, $org, $token); $this->loggerService->logExistingUserNotificationSent($user->getId(), $org->getId()); - $this->organizationsService->notifyOrganizationAdmins(['user'=> $user, 'acting_user_id'=>$actingUser->getId(), - 'organization'=> $org], 'USER_INVITED'); + $this->organizationsService->notifyOrganizationAdmins(['user' => $user, 'acting_user_id' => $actingUser->getId(), + 'organization' => $org], 'USER_INVITED'); } catch (\Exception $e) { $this->loggerService->logError("Error sending existing user notification: " . $e->getMessage(), [ - 'target_user_id' => $user->getId(), + 'target_user_id' => $user->getId(), 'organization_id' => $org->getId(), ]); } @@ -612,11 +615,11 @@ class UserService try { $token = $this->generatePasswordToken($user, $org->getId()); $this->emailService->sendPasswordSetupEmail($user, $token); - $this->organizationsService->notifyOrganizationAdmins(['user'=> $user, 'acting_user_id'=>$actingUser->getId(), - 'organization'=> $org], 'USER_INVITED'); + $this->organizationsService->notifyOrganizationAdmins(['user' => $user, 'acting_user_id' => $actingUser->getId(), + 'organization' => $org], 'USER_INVITED'); } catch (\Exception $e) { $this->loggerService->logError("Error sending password setup email: " . $e->getMessage(), [ - 'target_user_id' => $user->getId(), + 'target_user_id' => $user->getId(), 'organization_id' => $org->getId(), ]); }