From 2e276b1cafaf1794fe1b7f5dd90411b5b68e197e Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 30 Jan 2025 15:27:04 +0100 Subject: [PATCH 1/5] Complete user sign up flow --- src/ApiResource/User/UserApiResource.php | 8 ++++-- src/Dto/UserSignupDto.php | 29 ++++++++++++++++++++ src/State/User/UserSignupProcessor.php | 34 ++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 src/Dto/UserSignupDto.php create mode 100644 src/State/User/UserSignupProcessor.php diff --git a/src/ApiResource/User/UserApiResource.php b/src/ApiResource/User/UserApiResource.php index 5d94640..6b47bc0 100644 --- a/src/ApiResource/User/UserApiResource.php +++ b/src/ApiResource/User/UserApiResource.php @@ -6,10 +6,13 @@ use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; use ApiPlatform\Doctrine\Orm\State\Options; use ApiPlatform\Metadata as API; use App\ApiResource\Accounting\AccountingApiResource; +use App\Dto\UserSignupDto; use App\Entity\User\User; use App\Filter\OrderedLikeFilter; use App\Filter\UserQueryFilter; use App\State\ApiResourceStateProvider; +use App\State\User\UserSignupProcessor; +use App\State\User\UserStateProcessor; use Symfony\Component\Validator\Constraints as Assert; /** @@ -18,10 +21,11 @@ use Symfony\Component\Validator\Constraints as Assert; #[API\ApiResource( shortName: 'User', stateOptions: new Options(entityClass: User::class), - provider: ApiResourceStateProvider::class + provider: ApiResourceStateProvider::class, + processor: UserStateProcessor::class, )] #[API\GetCollection()] -#[API\Post()] +#[API\Post(input: UserSignupDto::class, processor: UserSignupProcessor::class)] #[API\Get()] #[API\Patch(security: 'is_granted("USER_EDIT", object)')] #[API\Delete(security: 'is_granted("USER_EDIT", object)')] diff --git a/src/Dto/UserSignupDto.php b/src/Dto/UserSignupDto.php new file mode 100644 index 0000000..86146d7 --- /dev/null +++ b/src/Dto/UserSignupDto.php @@ -0,0 +1,29 @@ +autoMapper->map($data, User::class); + $user->setPassword($this->userPasswordHasher->hashPassword($user, $data->password)); + $user = $this->entityStateProcessor->process($user, $operation, $uriVariables, $context); + + return $this->autoMapper->map($user, UserApiResource::class); + } +} From f3a643490a4796e066347e45a9a506dfbed72d88 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 30 Jan 2025 15:32:16 +0100 Subject: [PATCH 2/5] Allow null to be passed for user param to voter canEdit method --- src/Security/Voter/UserVoter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Security/Voter/UserVoter.php b/src/Security/Voter/UserVoter.php index 54f233c..563d078 100644 --- a/src/Security/Voter/UserVoter.php +++ b/src/Security/Voter/UserVoter.php @@ -35,7 +35,7 @@ class UserVoter extends Voter return false; } - private function canEdit(UserApiResource $subject, User $user): bool + private function canEdit(UserApiResource $subject, ?User $user): bool { if (!$user instanceof User) { return false; From 0316dd9ad0f163da16983f3445b4d216375eaf05 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 30 Jan 2025 15:48:44 +0100 Subject: [PATCH 3/5] Ensure unique user email and username at property level --- src/Entity/User/User.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Entity/User/User.php b/src/Entity/User/User.php index a29fba4..630fce8 100644 --- a/src/Entity/User/User.php +++ b/src/Entity/User/User.php @@ -29,8 +29,8 @@ use Symfony\Component\Security\Core\User\UserInterface; #[MapProvider(EntityMapProvider::class)] #[ORM\Entity(repositoryClass: UserRepository::class)] #[ORM\Index(fields: ['migratedId'])] -#[UniqueEntity(fields: ['username'], message: 'This usernames already exists.')] -#[UniqueEntity(fields: ['email'], message: 'This email address is already registered.')] +#[UniqueEntity('email', message: 'This email address is already registered.')] +#[UniqueEntity('username', message: 'This usernames already exists.')] class User implements UserInterface, PasswordAuthenticatedUserInterface, AccountingOwnerInterface { use MigratedEntity; @@ -43,7 +43,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface, Account private ?int $id = null; #[Gedmo\Versioned] - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, unique: true)] private ?string $email = null; /** @@ -56,7 +56,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface, Account * Human readable, non white space, unique string. */ #[Gedmo\Versioned] - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, unique: true)] private ?string $username = null; /** From 007f3bef06d56ebe181d608f52ee738a5a9d5ac7 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 30 Jan 2025 15:51:17 +0100 Subject: [PATCH 4/5] Rewards property on project should not be editable --- src/ApiResource/Project/ProjectApiResource.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ApiResource/Project/ProjectApiResource.php b/src/ApiResource/Project/ProjectApiResource.php index ae2e319..4af6ddd 100644 --- a/src/ApiResource/Project/ProjectApiResource.php +++ b/src/ApiResource/Project/ProjectApiResource.php @@ -91,5 +91,6 @@ class ProjectApiResource * * @var array */ + #[API\ApiProperty(writable: false)] public array $rewards; } From 92f8ea9d3bc024d3181e38a8870f8127228adc54 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 30 Jan 2025 15:57:57 +0100 Subject: [PATCH 5/5] Mark project territory as required --- src/ApiResource/Project/ProjectApiResource.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ApiResource/Project/ProjectApiResource.php b/src/ApiResource/Project/ProjectApiResource.php index 4af6ddd..a260938 100644 --- a/src/ApiResource/Project/ProjectApiResource.php +++ b/src/ApiResource/Project/ProjectApiResource.php @@ -69,6 +69,7 @@ class ProjectApiResource /** * ISO 3166 data about the Project's territory of interest. */ + #[Assert\NotBlank()] #[Assert\Valid()] public ProjectTerritoryApiResource $territory;