Skip to content
This repository was archived by the owner on Apr 3, 2023. It is now read-only.

Commit 57f625e

Browse files
author
Teddy Roncin
committed
🐛 (Issue) fixed issue 32
when we were making a request on PATCH /users/{id}, we couldn't pass the "address" tag without the server producing an error. When the old addresses were removed, they were not deleted, it was only the link which was. So I simply set the parameter orphanRemoval to true in the call to the annotation OneToMany on top of the address field in the User entity. Now, when an address is removed, it is also deleted from the database Closes #32
1 parent 12159ca commit 57f625e

File tree

5 files changed

+258
-8
lines changed

5 files changed

+258
-8
lines changed

src/Entity/User.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ class User implements UserInterface
276276
/**
277277
* The relation to the Addresses of the User.
278278
*
279-
* @ORM\OneToMany(targetEntity=UserAddress::class, mappedBy="user", cascade={"persist", "remove"})
279+
* @ORM\OneToMany(targetEntity=UserAddress::class, mappedBy="user", cascade={"persist", "remove"}, orphanRemoval=true)
280280
*
281281
* @Assert\Valid()
282282
*/

src/Entity/UserAddress.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class UserAddress
3232
/**
3333
* The relation to the User which live at this address.
3434
*
35-
* @ORM\ManyToOne(targetEntity=User::class, inversedBy="addresses")
35+
* @ORM\ManyToOne(targetEntity=User::class, inversedBy="addresses", cascade={"persist", "remove"})
3636
* @ORM\JoinColumn(nullable=false)
3737
*/
3838
private $user;

src/Repository/UserAdressRepository.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,21 @@
22

33
namespace App\Repository;
44

5-
use App\Entity\UserAdress;
5+
use App\Entity\UserAddress;
66
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
77
use Doctrine\Persistence\ManagerRegistry;
88

99
/**
10-
* @method null|UserAdress find($id, $lockMode = null, $lockVersion = null)
11-
* @method null|UserAdress findOneBy(array $criteria, array $orderBy = null)
12-
* @method UserAdress[] findAll()
13-
* @method UserAdress[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
10+
* @method null|UserAddress find($id, $lockMode = null, $lockVersion = null)
11+
* @method null|UserAddress findOneBy(array $criteria, array $orderBy = null)
12+
* @method UserAddress[] findAll()
13+
* @method UserAddress[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
1414
*/
1515
class UserAdressRepository extends ServiceEntityRepository
1616
{
1717
public function __construct(ManagerRegistry $registry)
1818
{
19-
parent::__construct($registry, UserAdress::class);
19+
parent::__construct($registry, UserAddress::class);
2020
}
2121

2222
// /**

tests/Users/DeleteUser.php

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
namespace App\Tests\Users;
4+
5+
use App\Entity\User;
6+
use App\Tests\EtuUTTApiTestCase;
7+
use Faker\Provider\Uuid;
8+
use Symfony\Component\HttpFoundation\Response;
9+
10+
class DeleteUser extends EtuUTTApiTestCase
11+
{
12+
13+
public function testNormal() : void
14+
{
15+
$client = static::createClient();
16+
$client->setDefaultOptions([ 'headers' => [ 'CAS-LOGIN' => 'test' ]]);
17+
$tempUser = new User();
18+
$tempUser->setLogin('foobar');
19+
$tempUser->setFirstName('foo');
20+
$tempUser->setLastName('bar');
21+
$this->em->persist($tempUser);
22+
$this->em->flush();
23+
$client->request('DELETE', '/users/'.$tempUser->getId());
24+
$this->assertResponseStatusCodeSame(Response::HTTP_NO_CONTENT);
25+
$users = $this->em->createQueryBuilder()
26+
->select('user.id')
27+
->from(User::class, 'user')
28+
->where('user.id=\''.$tempUser->getId().'\'')
29+
->getQuery()
30+
->execute();
31+
$this->assertEmpty($users);
32+
}
33+
34+
public function testNoPermission() : void
35+
{
36+
$client = static::createClient();
37+
$client->setDefaultOptions([ 'headers' => [ 'CAS-LOGIN' => 'test' ]]);
38+
$this->user->removeRole('ROLE_ADMIN');
39+
$tempUser = new User();
40+
$tempUser->setLogin('foobar');
41+
$tempUser->setFirstName('foo');
42+
$tempUser->setLastName('bar');
43+
$this->em->persist($tempUser);
44+
$this->em->flush();
45+
// Test with non existing user
46+
$client->request('DELETE', '/users/'.Uuid::uuid());
47+
$this->assertResponseStatusCodeSame(Response::HTTP_UNAUTHORIZED);
48+
// Test with existing user
49+
$client->request('DELETE', '/users/'.$tempUser->getId());
50+
$this->assertResponseStatusCodeSame(Response::HTTP_UNAUTHORIZED);
51+
$users = $this->em->createQueryBuilder()
52+
->select('user.id')
53+
->from(User::class, 'user')
54+
->where('user.id=\''.$tempUser->getId().'\'')
55+
->getQuery()
56+
->execute();
57+
$this->assertNotEmpty($users);
58+
}
59+
60+
public function testNotConnected() : void
61+
{
62+
$client = static::createClient();
63+
$testUser = $this->em->createQueryBuilder()
64+
->select('user.id')
65+
->from(User::class, 'user')
66+
->where('user.login = \'test\'')
67+
->getQuery()
68+
->execute();
69+
$client->request('DELETE', '/users/'.($testUser[0]['id']->jsonSerialize()));
70+
$this->assertResponseStatusCodeSame(Response::HTTP_UNAUTHORIZED);
71+
$client->request('DELETE', '/users/'.(Uuid::uuid()));
72+
$this->assertResponseStatusCodeSame(Response::HTTP_UNAUTHORIZED);
73+
}
74+
75+
public function testNonExistingUser() : void
76+
{
77+
$client = static::createClient();
78+
$client->setDefaultOptions([ 'headers' => [ 'CAS-LOGIN' => 'test' ]]);
79+
$client->request('DELETE', '/users/'.Uuid::uuid());
80+
$this->assertResponseStatusCodeSame(Response::HTTP_NOT_FOUND);
81+
}
82+
83+
public function testSQLInjection() : void
84+
{
85+
$client = static::createClient();
86+
$client->setDefaultOptions([ 'headers' => [ 'CAS-LOGIN' => 'test' ]]);
87+
$client->request('DELETE', '/users/\'');
88+
$this->assertResponseStatusCodeSame(Response::HTTP_NOT_FOUND);
89+
$client->request('DELETE', '/users/"');
90+
$this->assertResponseStatusCodeSame(Response::HTTP_NOT_FOUND);
91+
}
92+
93+
}

tests/Users/UpdateUser.php

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
<?php
2+
3+
namespace App\Tests\Users;
4+
5+
use App\DataFixtures\UserSeeder;
6+
use App\Entity\User;
7+
use App\Entity\UserAddress;
8+
use App\Repository\UserRepository;
9+
use App\Tests\EtuUTTApiTestCase;
10+
use DateTimeInterface;
11+
use Faker\Provider\Address;
12+
use Faker\Provider\Uuid;
13+
use Symfony\Component\HttpFoundation\Response;
14+
15+
class UpdateUser extends EtuUTTApiTestCase
16+
{
17+
18+
public function testNormal() : void
19+
{
20+
$client = static::createClient();
21+
$client->setDefaultOptions([ 'headers' => [ 'CAS-LOGIN' => 'test', 'Content-Type' => 'application/merge-patch+json' ]]);
22+
$testUser = $this->createUser('foo', 'bar', 'foobar');
23+
$testUserId = $testUser->getId();
24+
$testUserBirthday = $testUser->getInfos()->getBirthday();
25+
$crawler = $client->request('PATCH', '/users/'.$testUser->getId(), [ 'body' => json_encode([
26+
'socialNetwork' => [
27+
'facebook' => 'https://facebook.com/foobar',
28+
'twitter' => 'https://twitter.com/foobar',
29+
'instagram' => 'https://instagram.com/foobar',
30+
'linkedin' => 'https://linkedin.com/foobar',
31+
'pseudoDiscord' => 'foobar',
32+
'wantDiscordUTT' => true
33+
],
34+
'RGPD' => [
35+
'isKeepingAccount' => true,
36+
'isDeletingEverything' => true
37+
],
38+
'preferences' => [
39+
'birthdayDisplayOnlyAge' => false,
40+
'language' => 'en',
41+
'wantDaymail' => false,
42+
'wantDayNotif' => false
43+
],
44+
'infos' => [
45+
'sex' => 'Féminin',
46+
'nickname' => 'foobar',
47+
'passions' => 'I have no passions :(',
48+
'website' => 'https://foobar.com'
49+
],
50+
'addresses' => [
51+
[
52+
'street' => 'avenue Foobar',
53+
'postalCode' => '00 000',
54+
'city' => 'Foobar City',
55+
'country' => 'United States of Foobar'
56+
]
57+
],
58+
'mailsPhones' => [
59+
'mailPersonal' => '[email protected]',
60+
'phoneNumber' => '0123456789'
61+
]
62+
])]);
63+
$this->assertResponseStatusCodeSame(Response::HTTP_OK);
64+
$response = json_decode($crawler->getContent());
65+
// User checks
66+
$this->assertEquals($testUserId, $response->{'id'});
67+
$this->assertEquals('foobar', $response->{'login'});
68+
$this->assertEquals(null, $response->{'studentId'});
69+
$this->assertEquals('foo', $response->{'firstName'});
70+
$this->assertEquals('bar', $response->{'lastName'});
71+
// Social network checks
72+
$this->assertEquals('https://facebook.com/foobar', $response->{'socialNetwork'}->{'facebook'});
73+
$this->assertEquals('https://twitter.com/foobar', $response->{'socialNetwork'}->{'twitter'});
74+
$this->assertEquals('https://instagram.com/foobar', $response->{'socialNetwork'}->{'instagram'});
75+
$this->assertEquals('https://linkedin.com/foobar', $response->{'socialNetwork'}->{'linkedin'});
76+
$this->assertEquals('foobar', $response->{'socialNetwork'}->{'pseudoDiscord'});
77+
$this->assertEquals(true, $response->{'socialNetwork'}->{'wantDiscordUTT'});
78+
// RGPD checks
79+
$this->assertEmpty($response->{'RGPD'});
80+
// Badges checks
81+
$this->assertEmpty($response->{'badges'});
82+
// Badges checks
83+
//$this->assertEmpty($response->{'branche'});
84+
// Formation checks
85+
//$this->assertEmpty($response->{'formation'});
86+
// Preference checks
87+
$this->assertEmpty($response->{'preference'});
88+
// Infos checks
89+
$this->assertEquals('Féminin', $response->{'infos'}->{'sex'});
90+
$this->assertNull($response->{'infos'}->{'nationality'});
91+
$this->assertEquals($testUserBirthday->format(DateTimeInterface::RFC3339), $response->{'infos'}->{'birthday'});
92+
$this->assertEquals('/default_user_avatar.png', $response->{'infos'}->{'avatar'});
93+
$this->assertEquals('foobar', $response->{'infos'}->{'nickname'});
94+
$this->assertEquals('I have no passions :(', $response->{'infos'}->{'passions'});
95+
$this->assertEquals('https://foobar.com', $response->{'infos'}->{'website'});
96+
// Addresses checks
97+
$this->assertEmpty($response->{'addresses'});
98+
$this->assertEquals(1, $response->{'addresses'}->length());
99+
$this->assertEquals("avenue Foobar", $response->{'addresses'}[0]->{'street'});
100+
$this->assertEquals("00 000", $response->{'addresses'}[0]->{'street'});
101+
$this->assertEquals("Foobar City", $response->{'addresses'}[0]->{'street'});
102+
$this->assertEquals("United States of Foobar", $response->{'addresses'}[0]->{'street'});
103+
// Mails-phones checks
104+
$this->assertEquals(1, $response->{'mailsPhones'}->length());
105+
$this->assertEquals('[email protected]', $response->{'mailsPhones'}->{'mailPersonal'});
106+
$this->assertEquals('0123456789', $response->{'mailsPhones'}->{'phoneNumber'});
107+
}
108+
109+
public function testNotConnected() : void
110+
{
111+
$client = static::createClient();
112+
$client->setDefaultOptions([ 'headers' => ['Content-Type' => 'application/merge-patch+json' ]]);
113+
$client->request('PATCH', '/users/'.$this->user->getId(), [ 'body' => []]);
114+
$this->assertResponseStatusCodeSame(Response::HTTP_UNAUTHORIZED);
115+
$client->request('PATCH', '/users/'.Uuid::uuid(), [ 'body' => []]);
116+
$this->assertResponseStatusCodeSame(Response::HTTP_UNAUTHORIZED);
117+
}
118+
119+
public function testNonExistingUser() : void
120+
{
121+
$client = static::createClient();
122+
$client->setDefaultOptions([ 'headers' => [ 'CAS-LOGIN' => 'test', 'Content-Type' => 'application/merge-patch+json' ]]);
123+
$client->request('PATCH', '/users/'.Uuid::uuid(), [ 'body' => []]);
124+
$this->assertResponseStatusCodeSame(Response::HTTP_NOT_FOUND);
125+
}
126+
127+
public function testNoParameter() : void
128+
{
129+
$client = static::createClient();
130+
$client->setDefaultOptions([ 'headers' => [ 'CAS-LOGIN' => 'test', 'Content-Type' => 'application/merge-patch+json' ]]);
131+
$client->request('PATCH', '/users/'.$this->user->getId());
132+
$this->assertResponseStatusCodeSame(Response::HTTP_BAD_REQUEST);
133+
}
134+
135+
public function testSQLInjection() : void
136+
{
137+
$client = static::createClient();
138+
$client->setDefaultOptions([ 'headers' => [ 'CAS-LOGIN' => 'test', 'Content-Type' => 'application/merge-patch+json' ]]);
139+
$testUser = $this->createUser('foo', 'bar', 'foobar');
140+
$client->request('PATCH', '/users/\'', [ 'body' => '{}' ]);
141+
$this->assertResponseStatusCodeSame(Response::HTTP_NOT_FOUND);
142+
$client->request('PATCH', '/users/"', [ 'body' => '{}' ]);
143+
$this->assertResponseStatusCodeSame(Response::HTTP_NOT_FOUND);
144+
$client->request('PATCH', '/users/'.$testUser->getId(), [ 'body' => json_encode([ 'socialNetwork' => ['facebook' => '\''] ])]);
145+
$this->assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY);
146+
$client->request('PATCH', '/users/'.$testUser->getId(), [ 'body' => json_encode([ 'socialNetwork' => ['facebook' => '"'] ])]);
147+
$this->assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY);
148+
}
149+
150+
public function testInvalidFieldContent() : void
151+
{
152+
$client = static::createClient();
153+
$client->setDefaultOptions([ 'headers' => [ 'CAS-LOGIN' => 'test', 'Content-Type' => 'application/merge-patch+json' ]]);
154+
$testUser = $this->createUser('foo', 'bar', 'foobar');
155+
}
156+
157+
}

0 commit comments

Comments
 (0)