diff --git a/server/web_app/composer.json b/server/web_app/composer.json index 8fc8bf22ac5af63af5a4d2ff39dc1a1e9bdb8ff8..2075e56099dcbd0eab3da5f38c027a8c5e5bd563 100644 --- a/server/web_app/composer.json +++ b/server/web_app/composer.json @@ -18,6 +18,7 @@ "symfony/runtime": "7.2.*", "symfony/security-bundle": "7.2.*", "symfony/security-core": "7.2.*", + "symfony/validator": "7.2.*", "symfony/yaml": "7.2.*" }, "config": { diff --git a/server/web_app/config/packages/validator.yaml b/server/web_app/config/packages/validator.yaml new file mode 100644 index 0000000000000000000000000000000000000000..dd47a6ad82a8ecc3adc7dd22262ebe27b4b10534 --- /dev/null +++ b/server/web_app/config/packages/validator.yaml @@ -0,0 +1,11 @@ +framework: + validation: + # Enables validator auto-mapping support. + # For instance, basic validation constraints will be inferred from Doctrine's metadata. + #auto_mapping: + # App\Entity\: [] + +when@test: + framework: + validation: + not_compromised_password: false diff --git a/server/web_app/src/Controller/UserController.php b/server/web_app/src/Controller/UserController.php index b80a3e4f96e16261eaac20fac1009aaff66740a8..0ebd3f9cbd86b883d2f05f67b4037a091a77bf00 100644 --- a/server/web_app/src/Controller/UserController.php +++ b/server/web_app/src/Controller/UserController.php @@ -3,17 +3,129 @@ namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Attribute\Route; -final class UserController extends AbstractController +use App\Interface\UserControllerInterface; +use App\Entity\User; +use App\Enum\UserRole; +use App\Repository\UserRepository; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; +use Symfony\Component\Validator\Validator\ValidatorInterface; + +#[Route('/users')] +final class UserController extends AbstractController implements UserControllerInterface { - #[Route('/user', name: 'app_user')] - public function index(): JsonResponse + private EntityManagerInterface $entityManager; + private UserRepository $userRepository; + private UserPasswordHasherInterface $passwordHasher; + private ValidatorInterface $validator; + + public function __construct( + EntityManagerInterface $entityManager, + UserRepository $userRepository, + UserPasswordHasherInterface $passwordHasher, + ValidatorInterface $validator + ) { + $this->entityManager = $entityManager; + $this->userRepository = $userRepository; + $this->passwordHasher = $passwordHasher; + $this->validator = $validator; + } + + #[Route('', methods: ['POST'])] + public function createUser(Request $req): JsonResponse { + $data = json_decode($req->getContent(), true); + + $user = new User(); + $user->setUserName($data['userName'] ?? null); + $user->setName($data['name'] ?? null); + $user->setSurname($data['surname'] ?? null); + $user->setEmail($data['email'] ?? null); + $user->setRole(UserRole::from($data['role'] ?? 'ROLE_USER')); + $user->setCreatedAt(new \DateTimeImmutable()); + + if (isset($data['password'])) { + $user->setPassword($this->passwordHasher->hashPassword($user, $data['password'])); + } + + // Validate the User entity + $errors = $this->validator->validate($user); + if (count($errors) > 0) { + $errorMessages = []; + foreach ($errors as $error) { + $errorMessages[] = $error->getMessage(); + } + return new JsonResponse(['errors' => $errorMessages], Response::HTTP_BAD_REQUEST); + } + + $this->entityManager->persist($user); + $this->entityManager->flush(); + return $this->json([ - 'message' => 'Welcome to your new controller!', + 'message' => 'User created successfully', 'path' => 'src/Controller/UserController.php', + ], Response::HTTP_CREATED); + } + + #[Route('', methods: ['GET'])] + public function getAllUsers(): JsonResponse + { + $users = $this->userRepository->findAllUsers(); + $userArray = array_map(fn($user) => ['id' => $user->getId(), 'username' => $user->getUsername()], $users); + + return $this->json([ + 'users' => $userArray, + 'path' => 'src/Controller/ItemController.php', + ]); + } + + #[Route('/{id}', methods: ['GET'])] + public function getUserById(int $userId): JsonResponse + { + return $this->json([ + 'message' => 'Welcome to your new controller!', + 'path' => 'src/Controller/ItemController.php', + ]); + } + + #[Route('/{id}', methods: ['UPDATE'])] + public function updateUser(int $userId, Request $req): JsonResponse + { + return $this->json([ + 'message' => 'Welcome to your new controller!', + 'path' => 'src/Controller/ItemController.php', + ]); + } + + #[Route('/{id}', methods: ['DELETE'])] + public function deleteUser(int $userId): JsonResponse + { + return $this->json([ + 'message' => 'Welcome to your new controller!', + 'path' => 'src/Controller/ItemController.php', + ]); + } + + #[Route('/authenticate', methods: ['POST'])] + public function authenticateUser(string $username, string $password): JsonResponse + { + return $this->json([ + 'message' => 'Welcome to your new controller!', + 'path' => 'src/Controller/ItemController.php', + ]); + } + + #[Route('/authenticate/changePassword', methods: ['POST'])] + public function changePassword(int $userId, string $oldPassword, string $newPassword): JsonResponse + { + return $this->json([ + 'message' => 'Welcome to your new controller!', + 'path' => 'src/Controller/ItemController.php', ]); } } diff --git a/server/web_app/src/Entity/User.php b/server/web_app/src/Entity/User.php index 2e9ce2ffcbad1edd3238bd35b72564e15aaa9615..e7b77317f06f8dfbe43e76c804796ca8771e0064 100644 --- a/server/web_app/src/Entity/User.php +++ b/server/web_app/src/Entity/User.php @@ -7,6 +7,7 @@ use App\Repository\UserRepository; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; +use Symfony\Component\Validator\Constraints as Assert; #[ORM\Entity(repositoryClass: UserRepository::class)] class User implements UserInterface, PasswordAuthenticatedUserInterface @@ -17,18 +18,28 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface private ?int $id = null; #[ORM\Column(length: 255, nullable: true)] + #[Assert\NotBlank(message: "Username cannot be empty.")] + #[Assert\Length(min: 3, max: 50, minMessage: "Username must be at least {{ limit }} characters long.")] private ?string $userName = null; #[ORM\Column(length: 255, nullable: true)] + #[Assert\NotBlank(message: "Name cannot be empty.")] + #[Assert\Length(min: 2, max: 100, minMessage: "Name must be at least {{ limit }} characters long.")] private ?string $name = null; #[ORM\Column(length: 255, nullable: true)] + #[Assert\NotBlank(message: "Surname cannot be empty.")] + #[Assert\Length(min: 2, max: 100, minMessage: "Surname must be at least {{ limit }} characters long.")] private ?string $surname = null; #[ORM\Column(length: 255, unique: true)] + #[Assert\NotBlank(message: "Email cannot be empty.")] + #[Assert\Email(message: "Invalid email format.")] private ?string $email = null; #[ORM\Column(length: 255)] + #[Assert\NotBlank(message: "Password cannot be empty.")] + #[Assert\Length(min: 8, minMessage: "Password must be at least {{ limit }} characters long.")] private ?string $password = null; #[ORM\Column(nullable: true)] diff --git a/server/web_app/src/Interface/UserControllerInterface.php b/server/web_app/src/Interface/UserControllerInterface.php index 3fc74b54da7b5ece0a3c638f28dd390a66bdfdd6..a8dbe3ba8c91f3d05bb2740da3b133a3824f6e39 100644 --- a/server/web_app/src/Interface/UserControllerInterface.php +++ b/server/web_app/src/Interface/UserControllerInterface.php @@ -10,13 +10,13 @@ interface UserControllerInterface public function getAllUsers(): Response; - public function getUserById(int $user_id): Response; + public function getUserById(int $userId): Response; - public function updateUser(int $user_id, Request $req): Response; + public function updateUser(int $userId, Request $req): Response; - public function deleteUser(int $user_id): Response; + public function deleteUser(int $userId): Response; public function authenticateUser(string $username, string $password): Response; - public function changePassword(int $user_id, string $old_password, string $new_password): Response; + public function changePassword(int $userId, string $oldPassword, string $newPassword): Response; } diff --git a/server/web_app/src/Repository/UserRepository.php b/server/web_app/src/Repository/UserRepository.php index b29153bdebc8c935907c000813740c61a20275d8..8e1efc7b815aef9b9d658dcfd04f438a292f261c 100644 --- a/server/web_app/src/Repository/UserRepository.php +++ b/server/web_app/src/Repository/UserRepository.php @@ -16,6 +16,37 @@ class UserRepository extends ServiceEntityRepository parent::__construct($registry, User::class); } + public function findAllUsers (): array { + return $this->findAll(); + } + + // Custom query method to find active users + public function findActiveUsers(): array + { + return $this->createQueryBuilder('u') + ->andWhere('u.isBlocked = :isBlocked') + ->setParameter('isBlocked', false) + ->getQuery() + ->getResult(); + } + + // Custom query method to find a user by email + public function findOneByEmail(string $email): ?User + { + return $this->findOneBy(['email' => $email]); + } + + // Custom query method to find users by role + public function findByRole(UserRole $role): array + { + return $this->createQueryBuilder('u') + ->andWhere('u.role = :role') + ->setParameter('role', $role) + ->getQuery() + ->getResult(); + } + + // /** // * @return User[] Returns an array of User objects // */ diff --git a/server/web_app/symfony.lock b/server/web_app/symfony.lock index 65f77fc5c33ef2543d9284cf7230cb0d0ab7a0ab..4ddf444e42085914d334d9b5aff511569e222a5a 100644 --- a/server/web_app/symfony.lock +++ b/server/web_app/symfony.lock @@ -116,5 +116,17 @@ "config/packages/security.yaml", "config/routes/security.yaml" ] + }, + "symfony/validator": { + "version": "7.2", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "7.0", + "ref": "8c1c4e28d26a124b0bb273f537ca8ce443472bfd" + }, + "files": [ + "config/packages/validator.yaml" + ] } }