refactor user service

This commit is contained in:
Charles 2025-12-15 09:03:14 +01:00
parent 87ecf70d95
commit 271c2e31d1
3 changed files with 122 additions and 39 deletions

View File

@ -0,0 +1,25 @@
<?php
namespace App\Event;
use App\Entity\User;
use Symfony\Contracts\EventDispatcher\Event;
class UserCreatedEvent extends Event
{
public function __construct(
private readonly User $newUser,
private readonly User $actingUser
) {
}
public function getNewUser(): User
{
return $this->newUser;
}
public function getActingUser(): User
{
return $this->actingUser;
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace App\EventSubscriber;
use App\Event\UserCreatedEvent;
use App\Service\ActionService;
use App\Service\EmailService;
use App\Service\LoggerService;
use App\Service\UserService; // Only if you need helper methods, otherwise avoid to prevent circular ref
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class UserSubscriber implements EventSubscriberInterface
{
public function __construct(
private readonly EmailService $emailService,
private readonly LoggerService $loggerService,
private readonly ActionService $actionService,
) {
}
public static function getSubscribedEvents(): array
{
return [
UserCreatedEvent::class => '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()
);
}
}

View File

@ -8,7 +8,6 @@ use App\Entity\Roles;
use App\Entity\User; use App\Entity\User;
use App\Entity\UserOrganizatonApp; use App\Entity\UserOrganizatonApp;
use App\Entity\UsersOrganizations; use App\Entity\UsersOrganizations;
use App\Service\AwsService;
use DateTimeImmutable; use DateTimeImmutable;
use DateTimeZone; use DateTimeZone;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
@ -17,25 +16,24 @@ use Exception;
use League\Bundle\OAuth2ServerBundle\Model\AccessToken; use League\Bundle\OAuth2ServerBundle\Model\AccessToken;
use Random\RandomException; use Random\RandomException;
use RuntimeException; use RuntimeException;
use SebastianBergmann\CodeCoverage\Util\DirectoryCouldNotBeCreatedException;
use Symfony\Bundle\SecurityBundle\Security; use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\File\Exception\FileException; use Symfony\Component\HttpFoundation\File\Exception\FileException;
use App\Event\UserCreatedEvent;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
class UserService class UserService
{ {
public const NOT_FOUND = 'Entity not found'; public const NOT_FOUND = 'Entity not found';
public function __construct(private readonly EntityManagerInterface $entityManager, public function __construct(private readonly EntityManagerInterface $entityManager,
private readonly Security $security, private readonly Security $security,
private readonly AwsService $awsService, private readonly AwsService $awsService,
private readonly LoggerService $loggerService, private readonly LoggerService $loggerService,
private readonly ActionService $actionService, private readonly ActionService $actionService,
private readonly EmailService $emailService, private readonly EmailService $emailService,
private readonly OrganizationsService $organizationsService, private readonly OrganizationsService $organizationsService,
private readonly EventDispatcherInterface $eventDispatcher
) )
{ {
@ -185,9 +183,9 @@ class UserService
try { try {
$this->awsService->PutDocObj($_ENV['S3_PORTAL_BUCKET'], $picture, $customFilename, $extension, 'profile/'); $this->awsService->PutDocObj($_ENV['S3_PORTAL_BUCKET'], $picture, $customFilename, $extension, 'profile/');
$this->loggerService->logAWSAction( $this->loggerService->logAWSAction(
'Profile picture uploaded to S3',[ 'Profile picture uploaded to S3', [
'user_id' => $user->getId(), 'user_id' => $user->getId(),
'filename' => $customFilename, 'filename' => $customFilename,
]); ]);
$user->setPictureUrl('profile/' . $customFilename); $user->setPictureUrl('profile/' . $customFilename);
} catch (FileException $e) { } catch (FileException $e) {
@ -468,7 +466,7 @@ class UserService
$user->setName(trim($user->getName())); $user->setName(trim($user->getName()));
$user->setSurname(trim($user->getSurname())); $user->setSurname(trim($user->getSurname()));
$user->setEmail(trim($user->getEmail())); $user->setEmail(trim($user->getEmail()));
if($setPassword) { if ($setPassword) {
//FOR SETTING A DEFAULT RANDOM PASSWORD OF 50 CHARACTERS until user set his own password //FOR SETTING A DEFAULT RANDOM PASSWORD OF 50 CHARACTERS until user set his own password
try { try {
$user->setPassword(bin2hex(random_bytes(50))); $user->setPassword(bin2hex(random_bytes(50)));
@ -480,7 +478,7 @@ class UserService
} }
} }
if($picture) { if ($picture) {
$this->handleProfilePicture($user, $picture); $this->handleProfilePicture($user, $picture);
} }
} }
@ -489,10 +487,11 @@ class UserService
* Handle existing user being added to an organization * Handle existing user being added to an organization
*/ */
public function addExistingUserToOrganization( public function addExistingUserToOrganization(
User $existingUser, User $existingUser,
Organizations $org, Organizations $org,
User $actingUser, User $actingUser,
): int { ): int
{
try { try {
$uoId = $this->handleExistingUser($existingUser, $org); $uoId = $this->handleExistingUser($existingUser, $org);
@ -513,9 +512,9 @@ class UserService
return $uoId; return $uoId;
} catch (\Exception $e) { } catch (\Exception $e) {
$this->loggerService->logError('Error linking existing user to organization: ' . $e->getMessage(), [ $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(), 'organization_id' => $org->getId(),
'acting_user_id' => $actingUser->getId(), 'acting_user_id' => $actingUser->getId(),
]); ]);
throw $e; throw $e;
} }
@ -528,17 +527,20 @@ class UserService
{ {
try { try {
$this->formatUserData($user, $picture, true); $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->persist($user);
$this->entityManager->flush(); $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) { } 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(), [ $this->loggerService->logError('Error creating new user: ' . $e->getMessage(), [
'target_user_email' => $user->getEmail(), 'target_user_email' => $user->getEmail(),
'acting_user_id' => $actingUser->getId(), 'acting_user_id' => $actingUser->getId(),
]); ]);
throw $e; throw $e;
} }
@ -548,10 +550,11 @@ class UserService
* Link newly created user to an organization * Link newly created user to an organization
*/ */
public function linkUserToOrganization( public function linkUserToOrganization(
User $user, User $user,
Organizations $org, Organizations $org,
User $actingUser, User $actingUser,
): UsersOrganizations { ): UsersOrganizations
{
try { try {
$uo = new UsersOrganizations(); $uo = new UsersOrganizations();
$uo->setUsers($user); $uo->setUsers($user);
@ -567,7 +570,7 @@ class UserService
$org->getId(), $org->getId(),
$actingUser->getId(), $actingUser->getId(),
$uo->getId(), $uo->getId(),
); );
$this->actionService->createAction( $this->actionService->createAction(
@ -582,9 +585,9 @@ class UserService
return $uo; return $uo;
} catch (\Exception $e) { } catch (\Exception $e) {
$this->loggerService->logError('Error linking user to organization: ' . $e->getMessage(), [ $this->loggerService->logError('Error linking user to organization: ' . $e->getMessage(), [
'target_user_id' => $user->getId(), 'target_user_id' => $user->getId(),
'organization_id' => $org->getId(), 'organization_id' => $org->getId(),
'acting_user_id' => $actingUser->getId(), 'acting_user_id' => $actingUser->getId(),
]); ]);
throw $e; throw $e;
} }
@ -597,11 +600,11 @@ class UserService
$token = $this->generatePasswordToken($user, $org->getId()); $token = $this->generatePasswordToken($user, $org->getId());
$this->emailService->sendExistingUserNotificationEmail($user, $org, $token); $this->emailService->sendExistingUserNotificationEmail($user, $org, $token);
$this->loggerService->logExistingUserNotificationSent($user->getId(), $org->getId()); $this->loggerService->logExistingUserNotificationSent($user->getId(), $org->getId());
$this->organizationsService->notifyOrganizationAdmins(['user'=> $user, 'acting_user_id'=>$actingUser->getId(), $this->organizationsService->notifyOrganizationAdmins(['user' => $user, 'acting_user_id' => $actingUser->getId(),
'organization'=> $org], 'USER_INVITED'); 'organization' => $org], 'USER_INVITED');
} catch (\Exception $e) { } catch (\Exception $e) {
$this->loggerService->logError("Error sending existing user notification: " . $e->getMessage(), [ $this->loggerService->logError("Error sending existing user notification: " . $e->getMessage(), [
'target_user_id' => $user->getId(), 'target_user_id' => $user->getId(),
'organization_id' => $org->getId(), 'organization_id' => $org->getId(),
]); ]);
} }
@ -612,11 +615,11 @@ class UserService
try { try {
$token = $this->generatePasswordToken($user, $org->getId()); $token = $this->generatePasswordToken($user, $org->getId());
$this->emailService->sendPasswordSetupEmail($user, $token); $this->emailService->sendPasswordSetupEmail($user, $token);
$this->organizationsService->notifyOrganizationAdmins(['user'=> $user, 'acting_user_id'=>$actingUser->getId(), $this->organizationsService->notifyOrganizationAdmins(['user' => $user, 'acting_user_id' => $actingUser->getId(),
'organization'=> $org], 'USER_INVITED'); 'organization' => $org], 'USER_INVITED');
} catch (\Exception $e) { } catch (\Exception $e) {
$this->loggerService->logError("Error sending password setup email: " . $e->getMessage(), [ $this->loggerService->logError("Error sending password setup email: " . $e->getMessage(), [
'target_user_id' => $user->getId(), 'target_user_id' => $user->getId(),
'organization_id' => $org->getId(), 'organization_id' => $org->getId(),
]); ]);
} }