diff options
author | Jeremy Benoist <jeremy.benoist@gmail.com> | 2017-05-30 07:56:01 +0200 |
---|---|---|
committer | Jeremy Benoist <jeremy.benoist@gmail.com> | 2017-05-30 07:56:01 +0200 |
commit | 5709ecb36809fb009446a11a758232bbe8f264e4 (patch) | |
tree | 0880f40fe8e6e563ef5648c267bb1f65251a0355 /src | |
parent | 2251045901875aa815dee43ec467fb1af8d416d0 (diff) | |
download | wallabag-5709ecb36809fb009446a11a758232bbe8f264e4.tar.gz wallabag-5709ecb36809fb009446a11a758232bbe8f264e4.tar.zst wallabag-5709ecb36809fb009446a11a758232bbe8f264e4.zip |
Re-use `NewUserType` to validate registration
The only ugly things is how we handle error by generating the view and then parse the content to retrieve all errors…
Fix exposition fields in User entity
Diffstat (limited to 'src')
-rw-r--r-- | src/Wallabag/ApiBundle/Controller/UserRestController.php | 119 | ||||
-rw-r--r-- | src/Wallabag/UserBundle/Entity/User.php | 25 |
2 files changed, 101 insertions, 43 deletions
diff --git a/src/Wallabag/ApiBundle/Controller/UserRestController.php b/src/Wallabag/ApiBundle/Controller/UserRestController.php index c5ffbdf1..a1b78e3f 100644 --- a/src/Wallabag/ApiBundle/Controller/UserRestController.php +++ b/src/Wallabag/ApiBundle/Controller/UserRestController.php | |||
@@ -6,12 +6,14 @@ use FOS\UserBundle\Event\UserEvent; | |||
6 | use FOS\UserBundle\FOSUserEvents; | 6 | use FOS\UserBundle\FOSUserEvents; |
7 | use JMS\Serializer\SerializationContext; | 7 | use JMS\Serializer\SerializationContext; |
8 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | 8 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; |
9 | use Symfony\Component\HttpFoundation\Request; | ||
9 | use Symfony\Component\HttpFoundation\JsonResponse; | 10 | use Symfony\Component\HttpFoundation\JsonResponse; |
11 | use Wallabag\UserBundle\Entity\User; | ||
10 | 12 | ||
11 | class UserRestController extends WallabagRestController | 13 | class UserRestController extends WallabagRestController |
12 | { | 14 | { |
13 | /** | 15 | /** |
14 | * Retrieve user informations | 16 | * Retrieve current logged in user informations. |
15 | * | 17 | * |
16 | * @ApiDoc() | 18 | * @ApiDoc() |
17 | * | 19 | * |
@@ -21,78 +23,117 @@ class UserRestController extends WallabagRestController | |||
21 | { | 23 | { |
22 | $this->validateAuthentication(); | 24 | $this->validateAuthentication(); |
23 | 25 | ||
24 | $serializationContext = SerializationContext::create()->setGroups(['user_api']); | 26 | return $this->sendUser($this->getUser()); |
25 | $json = $this->get('serializer')->serialize($this->getUser(), 'json', $serializationContext); | ||
26 | |||
27 | return (new JsonResponse())->setJson($json); | ||
28 | } | 27 | } |
29 | 28 | ||
30 | /** | 29 | /** |
31 | * Register an user | 30 | * Register an user. |
32 | * | 31 | * |
33 | * @ApiDoc( | 32 | * @ApiDoc( |
34 | * requirements={ | 33 | * requirements={ |
35 | * {"name"="username", "dataType"="string", "required"=true, "description"="The user's username"}, | 34 | * {"name"="username", "dataType"="string", "required"=true, "description"="The user's username"}, |
36 | * {"name"="password", "dataType"="string", "required"=true, "description"="The user's password"} | 35 | * {"name"="password", "dataType"="string", "required"=true, "description"="The user's password"}, |
37 | * {"name"="email", "dataType"="string", "required"=true, "description"="The user's email"} | 36 | * {"name"="email", "dataType"="string", "required"=true, "description"="The user's email"} |
38 | * } | 37 | * } |
39 | * ) | 38 | * ) |
39 | * | ||
40 | * @todo Make this method (or the whole API) accessible only through https | ||
41 | * | ||
40 | * @return JsonResponse | 42 | * @return JsonResponse |
41 | */ | 43 | */ |
42 | // TODO : Make this method (or the whole API) accessible only through https | 44 | public function putUserAction(Request $request) |
43 | public function putUserAction($username, $password, $email) | ||
44 | { | 45 | { |
45 | if (!$this->container->getParameter('fosuser_registration')) { | 46 | if (!$this->container->getParameter('fosuser_registration')) { |
46 | $json = $this->get('serializer')->serialize(['error' => "Server doesn't allow registrations"], 'json'); | 47 | $json = $this->get('serializer')->serialize(['error' => "Server doesn't allow registrations"], 'json'); |
48 | |||
47 | return (new JsonResponse())->setJson($json)->setStatusCode(403); | 49 | return (new JsonResponse())->setJson($json)->setStatusCode(403); |
48 | } | 50 | } |
49 | 51 | ||
50 | if ($password === '') { // TODO : might be a good idea to enforce restrictions here | 52 | $userManager = $this->get('fos_user.user_manager'); |
51 | $json = $this->get('serializer')->serialize(['error' => 'Password is blank'], 'json'); | 53 | $user = $userManager->createUser(); |
52 | return (new JsonResponse())->setJson($json)->setStatusCode(400); | 54 | // enable created user by default |
53 | } | 55 | $user->setEnabled(true); |
54 | 56 | ||
57 | $form = $this->createForm('Wallabag\UserBundle\Form\NewUserType', $user, [ | ||
58 | 'csrf_protection' => false, | ||
59 | ]); | ||
55 | 60 | ||
56 | // TODO : Make only one call to database by using a custom repository method | 61 | // simulate form submission |
57 | if ($this->getDoctrine() | 62 | $form->submit([ |
58 | ->getRepository('WallabagUserBundle:User') | 63 | 'username' => $request->request->get('username'), |
59 | ->findOneByUserName($username)) { | 64 | 'plainPassword' => [ |
60 | $json = $this->get('serializer')->serialize(['error' => 'Username is already taken'], 'json'); | 65 | 'first' => $request->request->get('password'), |
61 | return (new JsonResponse())->setJson($json)->setStatusCode(409); | 66 | 'second' => $request->request->get('password'), |
62 | } | 67 | ], |
68 | 'email' => $request->request->get('email'), | ||
69 | ]); | ||
63 | 70 | ||
64 | if ($this->getDoctrine() | 71 | if ($form->isSubmitted() && false === $form->isValid()) { |
65 | ->getRepository('WallabagUserBundle:User') | 72 | $view = $this->view($form, 400); |
66 | ->findOneByEmail($email)) { | 73 | $view->setFormat('json'); |
67 | $json = $this->get('serializer')->serialize(['error' => 'An account with this email already exists'], 'json'); | ||
68 | return (new JsonResponse())->setJson($json)->setStatusCode(409); | ||
69 | } | ||
70 | 74 | ||
71 | $em = $this->get('doctrine.orm.entity_manager'); | 75 | // handle errors in a more beautiful way than the default view |
76 | $data = json_decode($this->handleView($view)->getContent(), true)['children']; | ||
77 | $errors = []; | ||
72 | 78 | ||
73 | $userManager = $this->get('fos_user.user_manager'); | 79 | if (isset($data['username']['errors'])) { |
74 | $user = $userManager->createUser(); | 80 | $errors['username'] = $this->translateErrors($data['username']['errors']); |
81 | } | ||
75 | 82 | ||
76 | $user->setUsername($username); | 83 | if (isset($data['email']['errors'])) { |
84 | $errors['email'] = $this->translateErrors($data['email']['errors']); | ||
85 | } | ||
77 | 86 | ||
78 | $user->setPlainPassword($password); | 87 | if (isset($data['plainPassword']['children']['first']['errors'])) { |
88 | $errors['password'] = $this->translateErrors($data['plainPassword']['children']['first']['errors']); | ||
89 | } | ||
79 | 90 | ||
80 | $user->setEmail($email); | 91 | $json = $this->get('serializer')->serialize(['error' => $errors], 'json'); |
81 | 92 | ||
82 | $user->setEnabled(true); | 93 | return (new JsonResponse())->setJson($json)->setStatusCode(400); |
83 | $user->addRole('ROLE_USER'); | 94 | } |
84 | 95 | ||
85 | $em->persist($user); | 96 | $userManager->updateUser($user); |
86 | 97 | ||
87 | // dispatch a created event so the associated config will be created | 98 | // dispatch a created event so the associated config will be created |
88 | $event = new UserEvent($user); | 99 | $event = new UserEvent($user, $request); |
89 | $this->get('event_dispatcher')->dispatch(FOSUserEvents::USER_CREATED, $event); | 100 | $this->get('event_dispatcher')->dispatch(FOSUserEvents::USER_CREATED, $event); |
90 | 101 | ||
91 | $serializationContext = SerializationContext::create()->setGroups(['user_api']); | 102 | return $this->sendUser($user); |
92 | $json = $this->get('serializer')->serialize($user, 'json', $serializationContext); | 103 | } |
93 | 104 | ||
94 | return (new JsonResponse())->setJson($json); | 105 | /** |
106 | * Send user response. | ||
107 | * | ||
108 | * @param User $user | ||
109 | * | ||
110 | * @return JsonResponse | ||
111 | */ | ||
112 | private function sendUser(User $user) | ||
113 | { | ||
114 | $json = $this->get('serializer')->serialize( | ||
115 | $user, | ||
116 | 'json', | ||
117 | SerializationContext::create()->setGroups(['user_api']) | ||
118 | ); | ||
95 | 119 | ||
120 | return (new JsonResponse())->setJson($json); | ||
96 | } | 121 | } |
97 | 122 | ||
123 | /** | ||
124 | * Translate errors message. | ||
125 | * | ||
126 | * @param array $errors | ||
127 | * | ||
128 | * @return array | ||
129 | */ | ||
130 | private function translateErrors($errors) | ||
131 | { | ||
132 | $translatedErrors = []; | ||
133 | foreach ($errors as $error) { | ||
134 | $translatedErrors[] = $this->get('translator')->trans($error); | ||
135 | } | ||
136 | |||
137 | return $translatedErrors; | ||
138 | } | ||
98 | } | 139 | } |
diff --git a/src/Wallabag/UserBundle/Entity/User.php b/src/Wallabag/UserBundle/Entity/User.php index 1863c966..1ff3046a 100644 --- a/src/Wallabag/UserBundle/Entity/User.php +++ b/src/Wallabag/UserBundle/Entity/User.php | |||
@@ -5,11 +5,10 @@ namespace Wallabag\UserBundle\Entity; | |||
5 | use Doctrine\Common\Collections\ArrayCollection; | 5 | use Doctrine\Common\Collections\ArrayCollection; |
6 | use Doctrine\ORM\Mapping as ORM; | 6 | use Doctrine\ORM\Mapping as ORM; |
7 | use JMS\Serializer\Annotation\Groups; | 7 | use JMS\Serializer\Annotation\Groups; |
8 | use JMS\Serializer\Annotation\XmlRoot; | ||
8 | use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface; | 9 | use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface; |
9 | use Scheb\TwoFactorBundle\Model\TrustedComputerInterface; | 10 | use Scheb\TwoFactorBundle\Model\TrustedComputerInterface; |
10 | use FOS\UserBundle\Model\User as BaseUser; | 11 | use FOS\UserBundle\Model\User as BaseUser; |
11 | use JMS\Serializer\Annotation\ExclusionPolicy; | ||
12 | use JMS\Serializer\Annotation\Expose; | ||
13 | use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; | 12 | use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; |
14 | use Symfony\Component\Security\Core\User\UserInterface; | 13 | use Symfony\Component\Security\Core\User\UserInterface; |
15 | use Wallabag\ApiBundle\Entity\Client; | 14 | use Wallabag\ApiBundle\Entity\Client; |
@@ -19,23 +18,24 @@ use Wallabag\CoreBundle\Entity\Entry; | |||
19 | /** | 18 | /** |
20 | * User. | 19 | * User. |
21 | * | 20 | * |
21 | * @XmlRoot("user") | ||
22 | * @ORM\Entity(repositoryClass="Wallabag\UserBundle\Repository\UserRepository") | 22 | * @ORM\Entity(repositoryClass="Wallabag\UserBundle\Repository\UserRepository") |
23 | * @ORM\Table(name="`user`") | 23 | * @ORM\Table(name="`user`") |
24 | * @ORM\HasLifecycleCallbacks() | 24 | * @ORM\HasLifecycleCallbacks() |
25 | * @ExclusionPolicy("all") | ||
26 | * | 25 | * |
27 | * @UniqueEntity("email") | 26 | * @UniqueEntity("email") |
28 | * @UniqueEntity("username") | 27 | * @UniqueEntity("username") |
29 | */ | 28 | */ |
30 | class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterface | 29 | class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterface |
31 | { | 30 | { |
31 | /** @Serializer\XmlAttribute */ | ||
32 | /** | 32 | /** |
33 | * @var int | 33 | * @var int |
34 | * | 34 | * |
35 | * @Expose | ||
36 | * @ORM\Column(name="id", type="integer") | 35 | * @ORM\Column(name="id", type="integer") |
37 | * @ORM\Id | 36 | * @ORM\Id |
38 | * @ORM\GeneratedValue(strategy="AUTO") | 37 | * @ORM\GeneratedValue(strategy="AUTO") |
38 | * | ||
39 | * @Groups({"user_api"}) | 39 | * @Groups({"user_api"}) |
40 | */ | 40 | */ |
41 | protected $id; | 41 | protected $id; |
@@ -44,14 +44,30 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf | |||
44 | * @var string | 44 | * @var string |
45 | * | 45 | * |
46 | * @ORM\Column(name="name", type="text", nullable=true) | 46 | * @ORM\Column(name="name", type="text", nullable=true) |
47 | * | ||
47 | * @Groups({"user_api"}) | 48 | * @Groups({"user_api"}) |
48 | */ | 49 | */ |
49 | protected $name; | 50 | protected $name; |
50 | 51 | ||
51 | /** | 52 | /** |
53 | * @var string | ||
54 | * | ||
55 | * @Groups({"user_api"}) | ||
56 | */ | ||
57 | protected $username; | ||
58 | |||
59 | /** | ||
60 | * @var string | ||
61 | * | ||
62 | * @Groups({"user_api"}) | ||
63 | */ | ||
64 | protected $email; | ||
65 | |||
66 | /** | ||
52 | * @var date | 67 | * @var date |
53 | * | 68 | * |
54 | * @ORM\Column(name="created_at", type="datetime") | 69 | * @ORM\Column(name="created_at", type="datetime") |
70 | * | ||
55 | * @Groups({"user_api"}) | 71 | * @Groups({"user_api"}) |
56 | */ | 72 | */ |
57 | protected $createdAt; | 73 | protected $createdAt; |
@@ -60,6 +76,7 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf | |||
60 | * @var date | 76 | * @var date |
61 | * | 77 | * |
62 | * @ORM\Column(name="updated_at", type="datetime") | 78 | * @ORM\Column(name="updated_at", type="datetime") |
79 | * | ||
63 | * @Groups({"user_api"}) | 80 | * @Groups({"user_api"}) |
64 | */ | 81 | */ |
65 | protected $updatedAt; | 82 | protected $updatedAt; |