6 Commits

Author SHA1 Message Date
f2365c08e9 Merge pull request 'docs: enrichissement du README.md' (#3) from docs/maj_documentation into main
Reviewed-on: #3
2026-03-01 18:10:31 +01:00
MLeveque
7a9dfa52db docs: enrichissement du README.md
- Ajout d'une section pour la procédure Git Flow (features, fixes, maintenance, documentation).
- Ajout du git clone dans les étapes de démarrage du projet.
2026-03-01 18:10:09 +01:00
ba83f4a075 Merge pull request 'refactor: suppression des fichiers, templates et configurations inutilisés' (#2) from feat/structure_fichiers_twig into main
Reviewed-on: #2
2026-03-01 18:00:19 +01:00
MLeveque
a515be554f refactor: suppression des fichiers, templates et configurations inutilisés 2026-03-01 17:59:32 +01:00
ec4919230b Merge pull request 'feat(api): implémentation du service KazApiService et intégration dans le UserController' (#1) from feat/ajout_client_kazapi into main
Reviewed-on: #1
2026-03-01 17:57:03 +01:00
MLeveque
72d7add8d8 feat(api): implémentation du service KazApiService et intégration dans le UserController
- Création du service `KazApiService` pour gérer les interactions avec l'API Kaz.
  - Authentification via token JWT.
  - Vérification de l'existence d'un utilisateur dans le service LDAP.
- Ajout d'une route dans le `UserController` pour rendre une vue utilisateur basée sur les données obtenues via l'API.
- Configuration du client HTTP `kaz_api.client` dans `framework.yaml` avec une base URI et des headers par défaut.
- Ajout des paramètres d'environnement liés à l'API (`KAZ_API_USER`, `KAZ_API_PASSWORD`, `KAZ_API_BASE_URL`) dans le fichier `.env`.
- Mise à jour des services dans `services.yaml` pour inclure les dépendances nécessaires.
2026-03-01 17:55:08 +01:00
15 changed files with 188 additions and 155 deletions

13
.env
View File

@@ -1,18 +1,9 @@
###> symfony/framework-bundle ###
APP_ENV=dev APP_ENV=dev
APP_SECRET= APP_SECRET=
APP_SHARE_DIR=var/share APP_SHARE_DIR=var/share
APP_VERSION=0.0.1 APP_VERSION=0.0.1
###< symfony/framework-bundle ###
###> doctrine/doctrine-bundle ###
DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8" DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8"
###< doctrine/doctrine-bundle ###
###> symfony/mailer ###
MAILER_DSN="smtp://localhost:1025" MAILER_DSN="smtp://localhost:1025"
###< symfony/mailer ###
###> symfony/routing ###
DEFAULT_URI="http://localhost:8000" DEFAULT_URI="http://localhost:8000"
###< symfony/routing ### KAZ_API_USER=
KAZ_API_PASSWORD=

View File

@@ -18,7 +18,7 @@ Cette application web permet aux adhérents de l'association KAZ de gérer leur
Cette base est conçue pour évoluer et intégrer de nouveaux outils et fonctionnalités à l'avenir. Cette base est conçue pour évoluer et intégrer de nouveaux outils et fonctionnalités à l'avenir.
## Prérequis ## Prérequis
- **PHP** 8.4 ou supérieur - **PHP** 8.4
- **[Composer](https://getcomposer.org/download/)** : (Gestionnaire de dépendances PHP) - **[Composer](https://getcomposer.org/download/)** : (Gestionnaire de dépendances PHP)
- **[Symfony CLI](https://getcomposer.org/download/)** (Interface en ligne de commande Symfony) - **[Symfony CLI](https://getcomposer.org/download/)** (Interface en ligne de commande Symfony)
- **Docker** Permet de lancer les services lié: postgres (base de données), mailpit (serveur de messagerie pour le dev) - **Docker** Permet de lancer les services lié: postgres (base de données), mailpit (serveur de messagerie pour le dev)
@@ -31,34 +31,77 @@ Le projet suit les standards de développement suivants :
- **[Git Flow](https://git-flow.readthedocs.io/fr/latest/presentation.html)** : Modèle de gestion de branches. - **[Git Flow](https://git-flow.readthedocs.io/fr/latest/presentation.html)** : Modèle de gestion de branches.
- **[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)** : Norme pour les messages de commit. - **[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)** : Norme pour les messages de commit.
### Procédure de contribution (Git Flow)
Toutes les modifications doivent passer par une branche dédiée avant d'être fusionnées dans la branche principal `main` via une Pull Request (Demande d'ajout).
#### Exemple d'ajout d'une fonctionnalité (feature)
1. **Mise à jour de l'environnement local** :
```bash
git checkout main
git pull origin main
```
2. **Création de la branche de fonctionnalité** (préfixe `feat/`) :
```bash
git checkout -b feat/nom-de-ma-feature
```
3. **Développement et commit** (respectant les Conventional Commits) :
```bash
git add .
git commit -m "feat: ajout de la nouvelle fonctionnalité"
```
4. **Publication de la branche** :
```bash
git push origin feat/nom-de-ma-feature
```
5. **Création de la Pull Request** : Rendez-vous sur Gitea pour ouvrir une PR de `feat/nom-de-ma-feature` vers `main`. Une fois revue et validée, elle sera fusionnée via l'interface.
#### Exemple de correction de bug (fix)
La procédure est identique, mais utilisez le préfixe `fix/` :
```bash
git checkout -b fix/nom-du-bug
# ... corrections ...
git commit -m "fix: résolution du problème"
git push origin fix/nom-du-bug
```
Pour les tâches de maintenance ou documentation, utilisez respectivement les préfixes `chore/` ou `docs/`.
## Quick start ## Quick start
### 1. Installation des dépendances ### 1. Clonage du projet
Clonez le projet et installez les dépendances avec Composer : Clonez le projet en utilisant SSH :
```bash
git clone ssh://git@git.kaz.bzh:2202/melvin-leveque/interface-kaznautes.git
cd interface-kaznautes
```
### 2. Installation des dépendances
Installez les dépendances avec Composer :
```bash ```bash
composer install composer install
``` ```
### 2. Configuration de l'environnement ### 3. Configuration de l'environnement
Copiez le fichier `.env` en `.env.local` et configurez vos accès à la base de données et à l'API : Copiez le fichier `.env` en `.env.local` et configurez vos accès à la base de données et à l'API :
```bash ```bash
cp .env .env.local cp .env .env.local
``` ```
### 3. Base de données ### 4. Base de données
Créez la base de données et exécutez les migrations : Créez la base de données et exécutez les migrations :
```bash ```bash
php bin/console doctrine:database:create php bin/console doctrine:database:create
php bin/console doctrine:migrations:migrate php bin/console doctrine:migrations:migrate
``` ```
### 4. Installation des assets ### 5. Installation des assets
Le projet utilise AssetMapper. Installez les dépendances JS : Le projet utilise AssetMapper. Installez les dépendances JS :
```bash ```bash
php bin/console importmap:install php bin/console importmap:install
``` ```
### 5. Lancement du serveur ### 6. Lancement du serveur
Utilisez le serveur Symfony pour lancer le projet localement : Utilisez le serveur Symfony pour lancer le projet localement :
```bash ```bash
symfony serve -d symfony serve -d

View File

@@ -1,81 +0,0 @@
const nameCheck = /^[-_a-zA-Z0-9]{4,22}$/;
const tokenCheck = /^[-_/+a-zA-Z0-9]{24,}$/;
// Generate and double-submit a CSRF token in a form field and a cookie, as defined by Symfony's SameOriginCsrfTokenManager
// Use `form.requestSubmit()` to ensure that the submit event is triggered. Using `form.submit()` will not trigger the event
// and thus this event-listener will not be executed.
document.addEventListener('submit', function (event) {
generateCsrfToken(event.target);
}, true);
// When @hotwired/turbo handles form submissions, send the CSRF token in a header in addition to a cookie
// The `framework.csrf_protection.check_header` config option needs to be enabled for the header to be checked
document.addEventListener('turbo:submit-start', function (event) {
const h = generateCsrfHeaders(event.detail.formSubmission.formElement);
Object.keys(h).map(function (k) {
event.detail.formSubmission.fetchRequest.headers[k] = h[k];
});
});
// When @hotwired/turbo handles form submissions, remove the CSRF cookie once a form has been submitted
document.addEventListener('turbo:submit-end', function (event) {
removeCsrfToken(event.detail.formSubmission.formElement);
});
export function generateCsrfToken (formElement) {
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');
if (!csrfField) {
return;
}
let csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
let csrfToken = csrfField.value;
if (!csrfCookie && nameCheck.test(csrfToken)) {
csrfField.setAttribute('data-csrf-protection-cookie-value', csrfCookie = csrfToken);
csrfField.defaultValue = csrfToken = btoa(String.fromCharCode.apply(null, (window.crypto || window.msCrypto).getRandomValues(new Uint8Array(18))));
}
csrfField.dispatchEvent(new Event('change', { bubbles: true }));
if (csrfCookie && tokenCheck.test(csrfToken)) {
const cookie = csrfCookie + '_' + csrfToken + '=' + csrfCookie + '; path=/; samesite=strict';
document.cookie = window.location.protocol === 'https:' ? '__Host-' + cookie + '; secure' : cookie;
}
}
export function generateCsrfHeaders (formElement) {
const headers = {};
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');
if (!csrfField) {
return headers;
}
const csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
if (tokenCheck.test(csrfField.value) && nameCheck.test(csrfCookie)) {
headers[csrfCookie] = csrfField.value;
}
return headers;
}
export function removeCsrfToken (formElement) {
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');
if (!csrfField) {
return;
}
const csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
if (tokenCheck.test(csrfField.value) && nameCheck.test(csrfCookie)) {
const cookie = csrfCookie + '_' + csrfField.value + '=0; path=/; samesite=strict; max-age=0';
document.cookie = window.location.protocol === 'https:' ? '__Host-' + cookie + '; secure' : cookie;
}
}
/* stimulusFetch: 'lazy' */
export default 'csrf-protection-controller';

View File

@@ -1,16 +0,0 @@
import { Controller } from '@hotwired/stimulus';
/*
* This is an example Stimulus controller!
*
* Any element with a data-controller="hello" attribute will cause
* this controller to be executed. The name "hello" comes from the filename:
* hello_controller.js -> "hello"
*
* Delete this file or adapt it for your use!
*/
export default class extends Controller {
connect() {
this.element.textContent = 'Hello Stimulus! Edit me in assets/controllers/hello_controller.js';
}
}

View File

@@ -1,18 +0,0 @@
services:
###> doctrine/doctrine-bundle ###
database:
ports:
- "5432"
###< doctrine/doctrine-bundle ###
###> symfony/mailer ###
mailer:
image: axllent/mailpit
ports:
- "1025"
- "8025"
environment:
MP_SMTP_AUTH_ACCEPT_ANY: 1
MP_SMTP_AUTH_ALLOW_INSECURE: 1
###< symfony/mailer ###

View File

@@ -1,13 +1,14 @@
# see https://symfony.com/doc/current/reference/configuration/framework.html
framework: framework:
secret: '%env(APP_SECRET)%' secret: '%env(APP_SECRET)%'
# Note that the session will be started ONLY if you read or write from it.
session: true session: true
http_client:
scoped_clients:
kaz_api.client:
base_uri: '%env(KAZ_API_BASE_URL)%'
headers:
Accept: 'application/json'
#esi: true # Section pour les tests
#fragments: true
when@test: when@test:
framework: framework:
test: true test: true

View File

@@ -1,21 +1,14 @@
# yaml-language-server: $schema=../vendor/symfony/dependency-injection/Loader/schema/services.schema.json
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# See also https://symfony.com/doc/current/service_container/import.html
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
services: services:
# default configuration for services in *this* file # configuration par défaut pour les services
_defaults: _defaults:
autowire: true # Automatically injects dependencies in your services. autowire: true # Injecte automatiquement les dépendances dans vos services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. autoconfigure: true # Enregistre automatiquement vos services en tant que commandes, abonnés d'événements, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\: App\:
resource: '../src/' resource: '../src/'
# add more service definitions when explicit configuration is needed App\Service\KazApiClient:
# please note that last definitions always *replace* previous ones arguments:
$kazApiClient: '@kaz_api.client'
$apiUser: '%env(KAZ_API_USER)%'
$apiPassword: '%env(KAZ_API_PASSWORD)%'

View File

@@ -11,7 +11,7 @@ class HomeController extends AbstractController
#[Route('/hello')] #[Route('/hello')]
public function hello(): Response public function hello(): Response
{ {
return $this->render('hello.html.twig', [ return $this->render('home/hello.html.twig', [
'name' => 'Melvin' 'name' => 'Melvin'
]); ]);
} }

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Controller;
use App\Service\KazApiService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
class UserController extends AbstractController
{
/**
* Permet de vérifier si un utilisateur existe dans le ldap.
*
* @param string $email L'adresse e-mail de l'utilisateur.
* @param KazApiService $apiClient Le service utilisé pour récupérer les données utilisateur.
*
* @return Response La page index utilisateur rendue.
* @throws ClientExceptionInterface
* @throws DecodingExceptionInterface
* @throws RedirectionExceptionInterface
* @throws ServerExceptionInterface
* @throws TransportExceptionInterface
*/
#[Route('/user/{email}')]
public function index(string $email, KazApiService $apiClient): Response
{
$exist = $apiClient->getUserData($email);
return $this->render('user/index.html.twig', [
'exist' => $exist,
]);
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace App\Service;
use Exception;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class KazApiService
{
private ?string $token = null;
public function __construct(
private readonly HttpClientInterface $kazApiClient,
private readonly string $apiUser,
private readonly string $apiPassword
) {}
/**
* Récupère le token JWT via l'authentification Basic
*
* @return string
* @throws ClientExceptionInterface
* @throws DecodingExceptionInterface
* @throws RedirectionExceptionInterface
* @throws ServerExceptionInterface
* @throws TransportExceptionInterface
*/
private function getToken(): string
{
if ($this->token) {
return $this->token;
}
$response = $this->kazApiClient->request('POST', '/get_token', [
'auth_basic' => [$this->apiUser, $this->apiPassword]
]);
if ($response->getStatusCode() !== 200) {
throw new Exception('Impossible de récupérer le token JWT');
}
$data = $response->toArray();
$this->token = $data['token']; // Ajustez la clé selon le format de votre API
return $this->token;
}
/**
* Permet de vérifier si un utilisateur existe dans le ldap.
*
* @param string $email L'adresse e-mail de l'utilisateur à rechercher.
*
* @return array Les données utilisateur renvoyées par l'API.
*
* @throws ClientExceptionInterface
* @throws DecodingExceptionInterface
* @throws RedirectionExceptionInterface
* @throws ServerExceptionInterface
* @throws TransportExceptionInterface
* @throws Exception
*/
public function getUserData(string $email): array
{
$options['headers']['Authorization'] = 'Bearer ' . $this->getToken();
$response = $this->kazApiClient->request('GET', "/ldap/user/$email", $options);
if ($response->getStatusCode() !== 200) {
throw new Exception('Erreur lors de l\'appel API : ' . $response->getStatusCode());
}
return $response->toArray();
}
}

View File

@@ -0,0 +1 @@
error404.html.twig

View File