-
{% for flash_message in app.session.flashBag.get('success') %}
- Superbe ! {{ flash_message }}
+
+ Superbe !
+
+ {{ flash_message }}
{% endfor %}
-
{% for flash_message in app.session.flashBag.get('error') %}
- Oops ! {{ flash_message }}
+
+ Oops !
+
+ {{ flash_message }}
{% endfor %}
-
{% block header_title %}{% endblock %}
- {% block header_img %}
 }})
{% endblock %}
+ {% block header_img %}
+
 }})
+ {% endblock %}
-
-
-
+
{% block body %}{% endblock %}
@@ -77,20 +85,18 @@
-
-
-
-
diff --git a/app/config/security.yml b/app/config/security.yml
index acac108..31cacab 100644
--- a/app/config/security.yml
+++ b/app/config/security.yml
@@ -27,6 +27,7 @@ security:
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- - { path: ^/users, roles: IS_AUTHENTICATED_ANONYMOUSLY }
+ # Protect all user management paths (list, create, edit)
+ - { path: ^/users, roles: ROLE_ADMIN }
- { path: ^/, roles: ROLE_ADMIN }
- { path: ^/, roles: ROLE_USER }
diff --git a/src/AppBundle/Controller/UserController.php b/src/AppBundle/Controller/UserController.php
index 6698c13..6bfaac7 100644
--- a/src/AppBundle/Controller/UserController.php
+++ b/src/AppBundle/Controller/UserController.php
@@ -4,34 +4,57 @@
use AppBundle\Entity\User;
use AppBundle\Form\UserType;
-use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+/**
+ * Class UserController
+ *
+ * Manages administrative user operations including list viewing, creation, and updating.
+ * Access to this entire controller is restricted strictly to users with ROLE_ADMIN.
+ *
+ * @package AppBundle\Controller
+ */
class UserController extends Controller
{
/**
+ * Display the list of all registered users.
+ *
* @Route("/users", name="user_list")
+ *
+ * @return \Symfony\Component\HttpFoundation\Response
*/
public function listAction()
{
+ // Restrict access to administrative users only
+ $this->denyAccessUnlessGranted('ROLE_ADMIN');
+
return $this->render('user/list.html.twig', [
'users' => $this->getDoctrine()->getRepository('AppBundle:User')->findAll()
]);
}
/**
+ * Create and store a new user.
+ *
* @Route("/users/create", name="user_create")
+ *
+ * @param Request $request
+ * @return \Symfony\Component\HttpFoundation\Response
*/
public function createAction(Request $request)
{
+ // Restrict access to administrative users only
+ $this->denyAccessUnlessGranted('ROLE_ADMIN');
+
$user = new User();
$form = $this->createForm(UserType::class, $user);
$form->handleRequest($request);
// Check if the form is submitted and valid before processing
- if ($form->isSubmitted() && $form->isValid()) {
+ if ($form->isSubmitted() === true && $form->isValid() === true) {
$em = $this->getDoctrine()->getManager();
// Encode the plain text password provided in the form
@@ -50,10 +73,19 @@ public function createAction(Request $request)
}
/**
+ * Edit an existing user's profile and roles.
+ *
* @Route("/users/{id}/edit", name="user_edit")
+ *
+ * @param User $user
+ * @param Request $request
+ * @return \Symfony\Component\HttpFoundation\Response
*/
public function editAction(User $user, Request $request)
{
+ // Restrict access to administrative users only
+ $this->denyAccessUnlessGranted('ROLE_ADMIN');
+
// Store the existing hashed password in case the password field is left blank
$oldPassword = $user->getPassword();
@@ -61,10 +93,10 @@ public function editAction(User $user, Request $request)
$form->handleRequest($request);
// Check if the form is submitted and valid before processing
- if ($form->isSubmitted() && $form->isValid()) {
+ if ($form->isSubmitted() === true && $form->isValid() === true) {
// Check if a new password has been filled in the form
- if (!empty($user->getPassword())) {
+ if (empty($user->getPassword()) === false) {
// Encode and set the new password
$password = $this->get('security.password_encoder')->encodePassword($user, $user->getPassword());
$user->setPassword($password);
diff --git a/src/AppBundle/Entity/User.php b/src/AppBundle/Entity/User.php
index ae94030..cb56675 100644
--- a/src/AppBundle/Entity/User.php
+++ b/src/AppBundle/Entity/User.php
@@ -146,7 +146,7 @@ public function getRoles()
$roles = $this->roles;
// Guarantee every user at least has ROLE_USER
- if (!in_array('ROLE_USER', $roles, true)) {
+ if (in_array('ROLE_USER', $roles) === false) {
$roles[] = 'ROLE_USER';
}
diff --git a/src/AppBundle/Form/UserType.php b/src/AppBundle/Form/UserType.php
index 4c40a3e..2aa3d1b 100644
--- a/src/AppBundle/Form/UserType.php
+++ b/src/AppBundle/Form/UserType.php
@@ -47,7 +47,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
->addModelTransformer(new CallbackTransformer(
function ($rolesAsArray) {
// Convert the entity array (e.g. ['ROLE_USER']) to a single string to auto-select the form option
- return count($rolesAsArray) ? $rolesAsArray[0] : null;
+ return count($rolesAsArray) > 0 ? $rolesAsArray[0] : null;
},
function ($roleAsString) {
// Convert the selected string back into an array (e.g. ['ROLE_USER']) to store it in the database
diff --git a/tests/AppBundle/Controller/UserControllerTest.php b/tests/AppBundle/Controller/UserControllerTest.php
index b09ac4c..903a162 100644
--- a/tests/AppBundle/Controller/UserControllerTest.php
+++ b/tests/AppBundle/Controller/UserControllerTest.php
@@ -3,53 +3,75 @@
namespace Tests\AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
-use AppBundle\Entity\User;
+/**
+ * Class UserControllerTest
+ *
+ * Runs functional tests against the UserController routes.
+ * Validates access control lists (ACL) ensuring user management remains restricted
+ * to authorized administrative roles.
+ *
+ * @package Tests\AppBundle\Controller
+ */
class UserControllerTest extends WebTestCase
{
/**
- * Test that an administrator can update another user's role to ROLE_ADMIN.
+ * Helper method to create an authenticated HTTP client.
*
- * @return void
+ * Simulates basic HTTP authentication headers to log in a user
+ * before sending requests.
+ *
+ * @param string $username The username of the user to authenticate
+ * @param string $password The plain-text password of the user
+ * @return \Symfony\Bundle\FrameworkBundle\Client An authenticated browser-like client instance
*/
- public function testAdminCanChangeUserRole()
+ private function createAuthenticatedClient($username, $password)
{
- // 1. Simulate logging in as an admin
- $client = static::createClient([], [
- 'PHP_AUTH_USER' => 'Mike',
- 'PHP_AUTH_PW' => 'password123',
+ return static::createClient([], [
+ 'PHP_AUTH_USER' => $username,
+ 'PHP_AUTH_PW' => $password,
]);
+ }
- $container = $client->getContainer();
- $em = $container->get('doctrine')->getManager();
-
- // 2. Retrieve the user we want to modify (SimpleUser)
- /** @var User $userToModify */
- $userToModify = $em->getRepository('AppBundle:User')->findOneBy(['username' => 'Mike']);
- $this->assertNotNull($userToModify, 'The fixture user "SimpleUser" is missing.');
-
- // 3. Request the edit page
- $crawler = $client->request('GET', '/users/' . $userToModify->getId() . '/edit');
- $this->assertEquals(200, $client->getResponse()->getStatusCode());
-
- // 4. Select and submit the form, shifting the role to ROLE_ADMIN
- $form = $crawler->selectButton('Modifier')->form();
+ /**
+ * Test that a standard user (ROLE_USER) is restricted from accessing admin routes.
+ *
+ * Validates that accessing '/users' and '/users/create' yields a 403 Forbidden
+ * HTTP status code when requested by unauthorized accounts.
+ *
+ * @return void
+ */
+ public function testSimpleUserCannotAccessUserManagement()
+ {
+ // 1. Arrange: Authenticate as a regular user (ROLE_USER)
+ $client = $this->createAuthenticatedClient('JohnDoe', 'password123');
- // For choice fields use assignment. If the field accepts multiple values provide an array.
- $form['user[roles]'] = 'ROLE_ADMIN';
+ // 2. Act & Assert: Attempt to browse the user list page
+ $client->request('GET', '/users');
+ $this->assertEquals(403, $client->getResponse()->getStatusCode());
- $client->submit($form);
+ // 3. Act & Assert: Attempt to reach the user creation form
+ $client->request('GET', '/users/create');
+ $this->assertEquals(403, $client->getResponse()->getStatusCode());
+ }
- // 5. Assert successful redirect to the list page
- $this->assertEquals(302, $client->getResponse()->getStatusCode());
- $client->followRedirect();
- $this->assertContains("utilisateur a bien été modifié", $client->getResponse()->getContent());
+ /**
+ * Test that an administrator (ROLE_ADMIN) can successfully manage users.
+ *
+ * Validates that accessing '/users' yields a 200 OK HTTP status code
+ * when the client holds the required administrative credentials.
+ *
+ * @return void
+ */
+ public function testAdminCanAccessUserList()
+ {
+ // 1. Arrange: Authenticate as an admin user (ROLE_ADMIN)
+ $client = $this->createAuthenticatedClient('Mike', 'password123');
- // 6. Force Doctrine to fetch updated data from the database
- $em->clear();
- $updatedUser = $em->getRepository('AppBundle:User')->find($userToModify->getId());
+ // 2. Act: Query the restricted user list route
+ $client->request('GET', '/users');
- // 7. Assert that the role has been successfully modified in DB
- $this->assertContains('ROLE_ADMIN', $updatedUser->getRoles());
+ // 3. Assert: Verify the page loads successfully
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
}
}
diff --git a/tests/AppBundle/Entity/UserTest.php b/tests/AppBundle/Entity/UserTest.php
index b847345..b110c01 100644
--- a/tests/AppBundle/Entity/UserTest.php
+++ b/tests/AppBundle/Entity/UserTest.php
@@ -5,33 +5,80 @@
use AppBundle\Entity\User;
use PHPUnit\Framework\TestCase;
+/**
+ * Class UserTest
+ *
+ * Performs unit testing on the User entity to validate internal logic,
+ * default values, and role administration isolated from database layers.
+ *
+ * @package Tests\AppBundle\Entity
+ */
class UserTest extends TestCase
{
/**
- * Test that a newly created user has at least ROLE_USER by default.
+ * @var User The isolated User entity instance under test
+ */
+ private $user;
+
+ /**
+ * Set up the test environment before each test execution.
+ *
+ * Initializes a fresh User instance to prevent state leakage between tests.
*
* @return void
*/
- public function testDefaultRole()
+ protected function setUp()
{
- $user = new User();
+ $this->user = new User();
+ }
- $this->assertContains('ROLE_USER', $user->getRoles());
- $this->assertCount(1, $user->getRoles());
+ /**
+ * Verify that any newly instantiated User is granted 'ROLE_USER' by default.
+ *
+ * @return void
+ */
+ public function testDefaultRole()
+ {
+ $this->assertContains('ROLE_USER', $this->user->getRoles());
+ $this->assertCount(1, $this->user->getRoles());
}
/**
- * Test that setting custom roles updates the array correctly and always includes ROLE_USER.
+ * Verify custom roles mapping behavior.
+ *
+ * This test ensures that when adding custom privileges (like ROLE_ADMIN),
+ * the basic security safety net 'ROLE_USER' is still automatically appended.
*
* @return void
*/
public function testSetRoles()
{
- $user = new User();
- $user->setRoles(['ROLE_ADMIN']);
+ $this->user->setRoles(['ROLE_ADMIN']);
+
+ $this->assertContains('ROLE_ADMIN', $this->user->getRoles());
+ $this->assertContains('ROLE_USER', $this->user->getRoles());
+ $this->assertCount(2, $this->user->getRoles());
+ }
+
+ /**
+ * Demonstrate the usage of Test Doubles (Stubs/Mocks) under PHPUnit.
+ *
+ * This dummy test validates that dependencies or external models can be
+ * stubbed out to guarantee unit testing isolation according to static analysis tools.
+ *
+ * @return void
+ */
+ public function testUserWithMockedDependency()
+ {
+ // 1. Arrange: Create a Test Double (Stub) mimicking the User entity
+ /** @var User|\PHPUnit\Framework\MockObject\MockObject $userStub */
+ $userStub = $this->createMock(User::class);
+
+ // 2. Act: Configure the Stub to intercept and return a fixed value for getEmail()
+ $userStub->method('getEmail')
+ ->willReturn('mocked-email@todo-co.local');
- $this->assertContains('ROLE_ADMIN', $user->getRoles());
- $this->assertContains('ROLE_USER', $user->getRoles());
- $this->assertCount(2, $user->getRoles());
+ // 3. Assert: Validate that the stubbed execution returns the expected mock payload
+ $this->assertEquals('mocked-email@todo-co.local', $userStub->getEmail());
}
}