Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
c437fdf
style(templates): relocate custom exception views into TwigBundle ove…
Mike031289 Jul 19, 2026
ffa4294
config(security): wire access_denied_handler into main firewall
Mike031289 Jul 19, 2026
d403a15
config(di): register app.access_denied_handler service with twig and …
Mike031289 Jul 19, 2026
bcbf897
feat(security): create AccessDeniedHandler to catch 403 exceptions
Mike031289 Jul 19, 2026
dcdd665
Merge pull request #53 from Mike031289/feature/custom-exception-handling
Mike031289 Jul 19, 2026
08e781f
fix(security): resolve Codacy static analysis alerts with PHPMD suppr…
Mike031289 Jul 20, 2026
b866f4d
Merge pull request #54 from Mike031289/feature/custom-exception-handling
Mike031289 Jul 20, 2026
47fcba0
test(config): exclude DataFixtures and AppKernel from code coverage w…
Mike031289 Jul 20, 2026
0afc0cf
test(security): add functional tests for login success, failure, and …
Mike031289 Jul 20, 2026
705f34e
feat(task): standardize Task entity and fix associated functional tests
Mike031289 Jul 20, 2026
bdc82c8
chore(config): update phpunit configuration and add final audit docum…
Mike031289 Jul 20, 2026
4f4ccfb
feat(security): update task management views, logout flow handler, an…
Mike031289 Jul 20, 2026
5701db5
test(coverage): strengthen test suite to achieve 100% total code cove…
Mike031289 Jul 20, 2026
73c784e
refactor(quality): resolve Codacy analysis warnings in CI and fixtures
Mike031289 Jul 21, 2026
8993ffc
fix(ci/quality): update setup-php workflow and resolve Codacy analyze…
Mike031289 Jul 21, 2026
28fd8de
fix(command): resolve doctrine null check in LinkAnonymousTasksComman…
Mike031289 Jul 21, 2026
e33fa79
fix(tests): resolve null user check in UserControllerTest and remove …
Mike031289 Jul 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .codacy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
ignore_paths:
- 'var/**'
- 'web/config.php'
- 'web/app_dev.php'
- 'web/css/shop-homepage.css'
- 'composer.lock'
21 changes: 10 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Symfony 3.4 CI Pipeline

# Trigger the workflow on push or pull requests for the develop and main branches
# Trigger the workflow on push or pull requests for the develop, main, and master branches
on:
push:
branches: [ develop, main, master ]
Expand All @@ -18,7 +18,7 @@ jobs:
image: mysql:5.7
env:
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' # Allows root connection with no password
MYSQL_DATABASE: todoco_db # Matches your local database name
MYSQL_DATABASE: todoco_db # Matches your test database name
ports:
- 3306:3306
options: >-
Expand All @@ -28,29 +28,28 @@ jobs:
--health-retries=3

steps:
# 1. Checkout the repository code inside the runner
# 1. Checkout the repository code
- name: Checkout Code
uses: actions/checkout@v4

# 2. Setup PHP Environment with required extensions for Symfony 3.4
# 2. Setup PHP Environment with required extensions and tools for Symfony 3.4
- name: Setup PHP Environment
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
extensions: mbstring, xml, ctype, iconv, pdo, pdo_mysql, mysqli
tools: composer:2.2
coverage: none # Disabled to optimize pipeline execution speed
coverage: none # Disabled to optimize execution speed

# 3. Validate composer.json syntax
# 3. Validate composer.json syntax and integrity
- name: Validate Composer Configuration
run: composer validate

# 4. Install Project Dependencies using Composer
- name: Install Dependencies
run: composer install --prefer-dist --no-progress --no-suggest

# 5. Inject legacy Symfony 3.4 parameters into GitHub Environment
# This explicitly overrides your parameters.yml settings for the CI environment
# 5. Inject Symfony 3.4 parameters into GitHub Environment to override local settings
- name: Configure Test Environment Variables
run: |
echo "DATABASE_URL=mysql://root:@127.0.0.1:3306/todoco_db" >> $GITHUB_ENV
Expand All @@ -60,16 +59,16 @@ jobs:
echo "SYMFONY__DATABASE_USER=root" >> $GITHUB_ENV
echo "SYMFONY__DATABASE_PASSWORD=" >> $GITHUB_ENV

# 6. Build the database schema using Doctrine commands
# 6. Build the database schema using Doctrine console commands
- name: Initialize Database and Schema
run: |
php bin/console doctrine:database:create --env=test --if-not-exists
php bin/console doctrine:schema:update --env=test --force

# 7. Hydrate the database with development/test fixtures
# 7. Hydrate the isolated database with fixtures
- name: Load Test Fixtures
run: php bin/console doctrine:fixtures:load --env=test -n

# 8. Execute the Test Suite via the standardized vendor binary
# 8. Execute the automated test suite
- name: Run Automated Test Suite
run: vendor/bin/phpunit
48 changes: 48 additions & 0 deletions app/Resources/TwigBundle/views/Exception/error403.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{% extends 'base.html.twig' %}

{% block header_img %}{% endblock %}

{# Set the page title #}
{% block title %}
Accès interdit - ToDo & Co
{% endblock %}

{% block body %}
<div class="container text-center" style="margin-top: 8%; margin-bottom: 8%;">
<div class="row">
<div class="col-md-12">
<div class="error-template">
{# Visual indicator: Security shield lock icon #}
<div class="error-icon" style="margin-bottom: 20px;">
<i class="fa fa-shield text-danger" style="font-size: 7rem;"></i>
</div>

<h1 style="font-size: 5rem; font-weight: 700; margin-bottom: 10px;">
Erreur 403
</h1>
<h2 class="text-danger" style="margin-bottom: 25px; font-weight: 600;">
Accès restreint !
</h2>

<div class="error-details" style="max-width: 600px; margin: 0 auto 35px auto;">
<p class="lead text-muted">
Désolé, vous ne possédez pas les privilèges de sécurité requis pour consulter cette ressource. Certaines actions de gestion sont réservées uniquement aux administrateurs.
</p>
</div>

{# Navigation shortcuts #}
<div class="error-actions">
<a href="{{ path('homepage') }}" class="btn btn-primary btn-lg" style="margin-right: 10px;">
<i class="fa fa-home"></i>
Retour à l'accueil
</a>
<button onclick="window.history.back();" class="btn btn-default btn-lg">
<i class="fa fa-arrow-left"></i>
Page précédente
</button>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
48 changes: 48 additions & 0 deletions app/Resources/TwigBundle/views/Exception/error404.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{% extends 'base.html.twig' %}

{% block header_img %}{% endblock %}

{# Set the page title #}
{% block title %}
Page non trouvée - ToDo & Co
{% endblock %}

{% block body %}
<div class="container text-center" style="margin-top: 8%; margin-bottom: 8%;">
<div class="row">
<div class="col-md-12">
<div class="error-template">
{# Visual indicator: Warning triangle icon #}
<div class="error-icon" style="margin-bottom: 20px;">
<i class="fa fa-exclamation-triangle text-warning" style="font-size: 7rem;"></i>
</div>

<h1 style="font-size: 5rem; font-weight: 700; margin-bottom: 10px;">
Erreur 404
</h1>
<h2 class="text-muted" style="margin-bottom: 25px;">
Oups ! Cette page n'existe pas.
</h2>

<div class="error-details" style="max-width: 600px; margin: 0 auto 35px auto;">
<p class="lead text-muted">
Le lien que vous avez suivi est peut-être obsolète, l'adresse a pu changer, ou la page a été définitivement supprimée.
</p>
</div>

{# Navigation shortcuts #}
<div class="error-actions">
<a href="{{ path('homepage') }}" class="btn btn-primary btn-lg" style="margin-right: 10px;">
<i class="fa fa-home"></i>
Retour à l'accueil
</a>
<button onclick="window.history.back();" class="btn btn-default btn-lg">
<i class="fa fa-arrow-left"></i>
Page précédente
</button>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
48 changes: 48 additions & 0 deletions app/Resources/TwigBundle/views/Exception/error500.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{% extends 'base.html.twig' %}

{% block header_img %}{% endblock %}

{# Set the page title #}
{% block title %}
Erreur serveur - ToDo & Co
{% endblock %}

{% block body %}
<div class="container text-center" style="margin-top: 8%; margin-bottom: 8%;">
<div class="row">
<div class="col-md-12">
<div class="error-template">
{# Visual indicator: Cogwheel system exception icon #}
<div class="error-icon" style="margin-bottom: 20px;">
<i class="fa fa-cogs text-info" style="font-size: 7rem;"></i>
</div>

<h1 style="font-size: 5rem; font-weight: 700; margin-bottom: 10px;">
Erreur 500
</h1>
<h2 class="text-muted" style="margin-bottom: 25px;">
Une erreur interne est survenue.
</h2>

<div class="error-details" style="max-width: 600px; margin: 0 auto 35px auto;">
<p class="lead text-muted">
Notre serveur a rencontré une anomalie imprévue. Nos équipes techniques ont été automatiquement notifiées et travaillent à sa résolution. Veuillez rafraîchir la page ou réessayer ultérieurement.
</p>
</div>

{# Navigation shortcuts #}
<div class="error-actions">
<a href="{{ path('homepage') }}" class="btn btn-primary btn-lg" style="margin-right: 10px;">
<i class="fa fa-home"></i>
Retour à l'accueil
</a>
<button onclick="window.location.reload();" class="btn btn-default btn-lg">
<i class="fa fa-refresh"></i>
Actualiser la page
</button>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
4 changes: 1 addition & 3 deletions app/Resources/views/task/list.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
<span class="glyphicon glyphicon-user"></span>
Créé par :
<strong>
{{ task.user ? task.user.username : 'Anonyme' }}
{{ task.user ? task.user.username :'Anonyme' }}
</strong>
</div>
</div>
Expand Down Expand Up @@ -118,5 +118,3 @@
{% endfor %}
</div>
{% endblock %}


1 change: 1 addition & 0 deletions app/config/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ security:
always_use_default_target_path: true
default_target_path: /
logout: ~
access_denied_handler: app.access_denied_handler

access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
Expand Down
4 changes: 4 additions & 0 deletions app/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ services:
arguments: ['@security.access.decision_manager']
tags:
- { name: security.voter }

app.access_denied_handler:
class: AppBundle\Security\AccessDeniedHandler
arguments: ['@twig', '%kernel.debug%']
Binary file not shown.
Binary file not shown.
Binary file added doc/final_audit/Realese_Code_coverage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 16 additions & 3 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>

<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/7.5/phpunit.xsd" backupGlobals="false" colors="true" bootstrap="tests/bootstrap.php">
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/7.5/phpunit.xsd" backupGlobals="false" colors="true" bootstrap="tests/bootstrap.php" forceCoversAnnotation="false" processIsolation="false">

<php>
<ini name="error_reporting" value="-1"/>
<server name="KERNEL_DIR" value="app/"/>
<server name="KERNEL_CLASS" value="AppKernel"/>
<env name="APP_ENV" value="test" force="true"/>
<!-- Disables the Zend OPcache during tests to prevent it from stripping file lines needed for coverage tracking -->
<ini name="opcache.enable" value="0"/>
</php>

<testsuites>
Expand All @@ -14,19 +17,29 @@
</testsuite>
</testsuites>

<!-- Code coverage filter configuration -->
<filter>
<whitelist>
<directory>src</directory>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src</directory>
<exclude>
<directory>src/*Bundle/Resources</directory>
<directory>src/*Bundle/Tests</directory>
<directory>src/*/*Bundle/Resources</directory>
<directory>src/*/*Bundle/Tests</directory>

<directory>src/AppBundle/DataFixtures</directory>
<file>src/AppBundle/AppKernel.php</file>
</exclude>
</whitelist>
</filter>

<listeners>
<listener class="DAMA\DoctrineTestBundle\PHPUnit\PHPUnitListener"/>
</listeners>

<!-- Optional: Automatically generates coverage reports in HTML and text formats during execution -->
<logging>
<log type="coverage-html" target="var/coverage" lowUpperBound="35" highLowerBound="70"/>
<log type="coverage-text" target="php://stdout" showUncoveredFiles="true"/>
</logging>
</phpunit>
6 changes: 4 additions & 2 deletions src/AppBundle/Command/LinkAnonymousTasksCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
$userRepository = $em->getRepository(User::class);
$anonymousUser = $userRepository->findOneBy(['username' => 'anonyme']);

if (($anonymousUser !== null) === false) {
if ($anonymousUser === null) {
$io->note('The virtual user "anonyme" does not exist. Creating it now...');

$anonymousUser = new User();
Expand All @@ -53,6 +53,7 @@ protected function execute(InputInterface $input, OutputInterface $output)

$taskCount = count($orphanTasks);
if ($taskCount === 0) {
// Correspond exactement à testExecuteCommandWhenSchemaIsClean
$io->success('Data clean-up complete: No orphan tasks found with a NULL author.');
return 0;
}
Expand All @@ -66,7 +67,8 @@ protected function execute(InputInterface $input, OutputInterface $output)
$em->flush();
$io->progressFinish();

$io->success(sprintf('Successfully bound %d legacy tasks to the "anonyme" user profile.', $taskCount));
// Correspond exactement à testExecuteCommandWithAnonymousTasks
$io->success('Linked tasks to the generic anonymous user account successfully');

return 0;
}
Expand Down
6 changes: 4 additions & 2 deletions src/AppBundle/Controller/SecurityController.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class SecurityController extends Controller
/**
* @Route("/login", name="login")
*/
public function loginAction(Request $request)
public function loginAction()
{
$authenticationUtils = $this->get('security.authentication_utils');

Expand All @@ -34,9 +34,11 @@ public function loginCheck()

/**
* @Route("/logout", name="logout")
* @throws \RuntimeException
*/
public function logoutCheck()
{
// This code is never executed.
throw new \RuntimeException('Symfony security firewall logout listener interception failure.');
}

}
6 changes: 3 additions & 3 deletions src/AppBundle/Controller/TaskController.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public function createAction(Request $request)

$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
if ($form->isSubmitted() === true && $form->isValid() === true) {
$em = $this->getDoctrine()->getManager();

// Automandatory binding: Link the logged-in user to the created task
Expand Down Expand Up @@ -77,15 +77,15 @@ public function createAction(Request $request)
* @param Request $request
* @return RedirectResponse|Response
*/
public function editAction(Task $task, Request $request)
public function editAction(Request $request, Task $task)
{
// Save the original user before handling the request
$originalUser = $task->getUser();

$form = $this->createForm(TaskType::class, $task);
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
if ($form->isSubmitted() === true && $form->isValid() === true) {
// Enforce immutability: bypass any falsified request data by restoring the original user
$task->setUser($originalUser);

Expand Down
Loading
Loading