summaryrefslogtreecommitdiff
path: root/src/controller
diff options
context:
space:
mode:
authorpolo-pc-greta <ordipolo@gmx.fr>2025-03-27 10:13:03 +0100
committerpolo-pc-greta <ordipolo@gmx.fr>2025-03-27 10:13:03 +0100
commitdf3612ed7e6691530503f79483d2fdbc032d01b8 (patch)
tree56d1c68fdc8625f5dad1937a654299d45142c79a /src/controller
downloadcms-df3612ed7e6691530503f79483d2fdbc032d01b8.zip
mise en ligne github
Diffstat (limited to 'src/controller')
-rw-r--r--src/controller/Director.php101
-rw-r--r--src/controller/Security.php111
-rw-r--r--src/controller/URL.php88
-rw-r--r--src/controller/ajax.php104
-rw-r--r--src/controller/installation.php144
-rw-r--r--src/controller/password.php357
-rw-r--r--src/controller/post.php17
7 files changed, 922 insertions, 0 deletions
diff --git a/src/controller/Director.php b/src/controller/Director.php
new file mode 100644
index 0000000..896cde1
--- /dev/null
+++ b/src/controller/Director.php
@@ -0,0 +1,101 @@
1<?php
2// src/controller/Director.php
3
4declare(strict_types=1);
5
6use Doctrine\ORM\EntityManager;
7use App\Entity\Page;
8use App\Entity\Node;
9
10class Director
11{
12 private EntityManager $entityManager;
13 static public Menu $menu_data; // pour NavBuilder
14 static public Path $page_path; // pour BreadcrumbBuilder
15 private Page $page;
16 private Node $root_node;
17
18 public function __construct(EntityManager $entityManager)
19 {
20 $this->entityManager = $entityManager;
21 self::$menu_data = new Menu($entityManager); // Menu est un modèle mais pas une entité
22 self::$page_path = new Path();
23 $this->page = self::$page_path->getLast();
24 $this->root_node = new Node; // instance mère "vide" ne possédant rien d'autre que des enfants
25 }
26
27 public function makeRootNode(string $id = ''): void
28 {
29 // on récupère toutes les entrées
30 $dql = 'SELECT n FROM App\Entity\Node n WHERE n.page = :page OR n.page IS null';
31 if($id == '')
32 {
33 $bulk_data = $this->entityManager
34 ->createQuery($dql)
35 ->setParameter('page', $this->page)
36 ->getResult();
37 }
38 else // avec $_GET['id'] dans l'URL
39 {
40 $dql .= ' OR n.article_timestamp = :id';
41 $bulk_data = $this->entityManager
42 ->createQuery($dql)
43 ->setParameter('page', $this->page)
44 ->setParameter('id', $id)
45 ->getResult();
46 }
47 $this->feedObjects($bulk_data);
48 }
49
50 public function makeArticleNode(string $id = ''): bool
51 {
52 $bulk_data = $this->entityManager
53 ->createQuery('SELECT n FROM App\Entity\Node n WHERE n.article_timestamp = :id')
54 ->setParameter('id', $id)
55 ->getResult();
56
57 if(count($bulk_data) === 0){
58 return false;
59 }
60
61 $this->root_node = $bulk_data[0];
62 return true;
63 }
64
65 public function feedObjects(array $bulk_data): void // $bulk_data = tableau de Node
66 {
67 // puis on les range
68 // (attention, risque de disfonctionnement si les noeuds de 1er niveau ne sont pas récupérés en 1er dans la BDD)
69 foreach($bulk_data as $node)
70 {
71 // premier niveau
72 if($node->getParent() == null)
73 {
74 $this->root_node->addChild($node);
75
76 // spécifique page article
77 if($node->getName() === 'main' && $this->page->getEndOfPath() == 'article'){
78 $main = $node;
79 }
80 }
81 // autres niveaux
82 else
83 {
84 $node->getParent()->addChild($node);
85
86 // spécifique page article
87 if($node->getName() === 'new' && $this->page->getEndOfPath() == 'article'){
88 $new = $node;
89 }
90 }
91 }
92 if(isset($new)){
93 $main->setTempChild($new);
94 }
95 }
96
97 public function getRootNode(): Node
98 {
99 return $this->root_node;
100 }
101}
diff --git a/src/controller/Security.php b/src/controller/Security.php
new file mode 100644
index 0000000..ab59d07
--- /dev/null
+++ b/src/controller/Security.php
@@ -0,0 +1,111 @@
1<?php
2// src/controller/Security.php
3//
4// utilise htmlawed contre les faille XSS
5
6class Security
7{
8 private static $configHtmLawed = array(
9 'safe'=>1, // protection contre les élements et attributs dangereux
10
11 // liste blanche d'éléments HTML
12 'elements'=> 'h1, h2, h3, h4, h5, h6, p, s, em, span, strong, a, ul, ol, li, sup, sub, code, blockquote, div, pre, table, caption, colgroup, col, tbody, tr, th, td, figure, img, figcaption',
13
14 // liste noire d'attributs HTML
15 'deny_attribute'=> 'id, class' // on garde 'style'
16 );
17
18 // faire qu'un certain élément puisse n'avoir que certains attributs, regarder la doc
19 private static $specHtmLawed = '';
20
21 public static function secureString(string $chaine): string
22 {
23 return trim(htmLawed($chaine, self::$configHtmLawed, self::$specHtmLawed));;
24 }
25
26 public static function secureFileName(string $chaine): string
27 {
28 // sécuriser un nom avec chemin avec basename?
29 //$chaine = basename($chaine);
30
31 /*
32 - caractères interdits sous windows / \ : * ? " < > |
33 - mac autorise les /
34 - mac interdit :
35 - linux autorise tout sauf les /
36 - imagemagick ne supporte pas les :
37
38 - 'espace' fonctionne
39 - / remplacé par firefox en :
40 - \ retire ce qui est devant le \
41 - * fonctionne
42 - ? permet le téléchargement mais pas l'affichage
43 - " ne fonctionne pas, remplacé par %22, filtrer %22
44 - < > fonctionnent
45 - | fonctionne
46 - = fonctionne, mais je filtre parce qu'on en trouve dans une URL
47 - ' ` fonctionnent
48 - % fonctionne
49 - (){}[] fonctionnent
50 - ^ fonctionne
51 - # ne fonctionne pas
52 - ~ fonctionne
53 - & fonctionne
54 - ^ pas encore testé
55 */
56
57 // => on remplace tout par des _
58 // filtrer / et \ semble inutile
59
60 $cibles = [' ', '/', '\\', ':', '*', '?', '<', '>', '|', '=', "'", '`', '"', '%22', '#'];
61 $chaine = str_replace($cibles, '_', $chaine); // nécéssite l'extension mbstring
62 $chaine = mb_strtolower($chaine);
63 return($chaine);
64
65 // les problèmes avec \ persistent !!
66 // => javascript
67 // malheureusement document.getElementById('upload').files[0].name = chaine; ne marche pas! interdit!
68 // javascript ne doit pas pouvoir accéder au système de fichiers
69 // solutions:
70 // - au lieu de fournir une chaine (le chemin du fichier), donner un objet à files[0].name
71 // - créer une copie du fichier et l'envoyer à la place
72 // - envoyer le fichier en AJAX
73 // - envoyer le nom du fichier à part puis renommer en PHP
74 }
75}
76
77// erreurs à la création des mots de passe
78function removeSpacesTabsCRLF(string $chaine): string
79{
80 $cibles = [' ', "\t", "\n", "\r"]; // doubles quotes !!
81 return(str_replace($cibles, '', $chaine));
82}
83
84// lien sans http://
85function fixLinks($data)
86{
87 // 1/
88 // si une adresse est de type "domaine.fr" sans le http:// devant, le comportement des navigateurs est de rechercher un fichier comme si mon adresse commençait par file://
89 // tomber ainsi sur une page d'erreur est parfaitement déroutant
90
91 // regex pour détecter les balises <a> et ajouter http:// au début des liens si nécessaire
92 $pattern = '#(<a[^>]+href=")((?!https?://)[^>]+>)#';
93 //$data = preg_replace($pattern, '$1http://$2', $data);
94
95 // 2/
96 // cas où la regex fait mal son boulot:
97 // l'erreur 404 est gérée par le .htaccess
98 // et le visiteur est redirigé à la page "menu"
99 // (ça ne règle pas le problème mais c'est mieux)
100
101 // 3/
102 // quand l'éditeur est ouvert (avant de valider l'article),
103 // le lien qu'on vient de créer apparaît dans l'infobulle,
104 // cliquer dessus ouvre un onglet sur une erreur 404
105 // solution partielle avec le .htaccess
106 //
107 // solution? fermer ce nouvel onglet avec echo '<SCRIPT>javascript:window.close()</SCRIPT>';
108 // comment déclencher le JS? en faisant qu'une erreur 404 causée pour cette raison soit particulière?
109
110 return($data);
111}
diff --git a/src/controller/URL.php b/src/controller/URL.php
new file mode 100644
index 0000000..956d85d
--- /dev/null
+++ b/src/controller/URL.php
@@ -0,0 +1,88 @@
1<?php
2// src/controller/URL.php
3
4declare(strict_types=1);
5
6class URL implements Stringable
7{
8 static private string $protocol = 'http://';
9 static private string $host = 'localhost';
10 static private string $port;
11 static private string $path = '/index.php';
12 private array $params;
13 private string $anchor = '';
14
15 // setters statiques
16 static public function setProtocol(string $protocol = 'http'): void
17 {
18 self::$protocol = $protocol === 'https' ? 'https://' : 'http://';
19 }
20 static public function setPort(int|string $port = 80): void
21 {
22 if((int)$port === 443){
23 self::$protocol = 'https://';
24 self::$port = '';
25 }
26 elseif((int)$port === 80){
27 self::$protocol = 'http://';
28 self::$port = '';
29 }
30 else{
31 self::$port = ':' . (string)$port;
32 }
33 }
34 static public function setHost(string $host): void
35 {
36 self::$host = $host;
37 }
38 static public function setPath(string $path): void
39 {
40 self::$path = '/' . ltrim($path, '/');
41 }
42
43 public function __construct(array $gets = [], string $anchor = ''){
44 $this->params = $gets;
45 if($anchor != ''){
46 $this->setAnchor($anchor);
47 }
48 }
49
50 //setters normaux
51 public function addParams(array $gets): void
52 {
53 // array_merge est préféré à l'opérateur d'union +, si une clé existe déjà la valeur est écrasée
54 $this->params = array_merge($this->params, $gets);
55 }
56 public function setAnchor(string $anchor = ''): void
57 {
58 if($anchor != ''){
59 $this->anchor = '#' . ltrim($anchor, '#');
60 }
61 else{
62 $this->anchor = '';
63 }
64 }
65
66 private function makeParams(): string
67 {
68 $output = '';
69 $first = true;
70
71 foreach($this->params as $key => $value) {
72 if($first){
73 $output .= '?';
74 $first = false;
75 }
76 else{
77 $output .= '&';
78 }
79 $output .= $key . '=' . $value;
80 }
81 return $output;
82 }
83
84 public function __toString(): string
85 {
86 return self::$protocol . self::$host . self::$port . self::$path . $this->makeParams() . $this->anchor;
87 }
88} \ No newline at end of file
diff --git a/src/controller/ajax.php b/src/controller/ajax.php
new file mode 100644
index 0000000..130c4c6
--- /dev/null
+++ b/src/controller/ajax.php
@@ -0,0 +1,104 @@
1<?php
2// src/controller/ajax.php
3
4declare(strict_types=1);
5
6// détection des requêtes de tinymce
7if($_SERVER['CONTENT_TYPE'] === 'application/json' && isset($_GET['action']))
8{
9 // récupération des données
10 $data = file_get_contents('php://input');
11 $json = json_decode($data, true);
12
13 if($_GET['action'] === 'editor_submit' && isset($json['id']) && isset($json['content']))
14 {
15 if(json_last_error() === JSON_ERROR_NONE)
16 {
17 $articleId = $json['id'];
18 $content = Security::secureString($json['content']);
19
20 $director = new Director($entityManager);
21 if($director->makeArticleNode($articleId)) // une entrée est trouvée
22 {
23 $node = $director->getRootNode();
24 $node->getArticle()->setContent($content);
25 $entityManager->flush();
26
27 echo json_encode(['success' => true]);
28 }
29 else{
30 echo json_encode(['success' => false, 'message' => 'Aucune entrée trouvée en BDD']);
31 }
32 }
33 else{
34 echo json_encode(['success' => false, 'message' => 'Erreur de décodage JSON']);
35 }
36 die;
37 }
38 elseif($_GET['action'] === 'delete_article' && isset($json['id']))
39 {
40 $articleId = $json['id'];
41
42 $director = new Director($entityManager);
43 $director->makeArticleNode($articleId);
44 $node = $director->getRootNode();
45 $entityManager->remove($node);
46 $entityManager->flush();
47
48 // test avec une nouvelle requête qui ne devrait rien trouver
49 if(!$director->makeArticleNode($articleId))
50 {
51 echo json_encode(['success' => true]);
52
53 // on pourrait afficher une notification "toast"
54 }
55 else{
56 http_response_code(500);
57 echo json_encode(['success' => false, 'message' => 'Erreur lors de la suppression de l\'article.']);
58 }
59 die;
60 }
61}
62
63// détection des requêtes d'upload d'image de tinymce
64if(strpos($_SERVER['CONTENT_TYPE'], 'multipart/form-data') !== false && isset($_GET['action']) && $_GET['action'] === 'upload_image'){
65 if (isset($_FILES['file'])) {
66 $file = $_FILES['file'];
67 $dest = 'images/';
68 $dest_mini = 'images-mini/';
69
70 // Vérifier si les répertoires existent, sinon les créer
71 if(!is_dir($dest)) {
72 mkdir($dest, 0700, true);
73 }
74 if(!is_dir($dest_mini)) {
75 mkdir($dest_mini, 0700, true);
76 }
77
78 $filePath = $dest . basename($file['name']);
79
80 // créer une miniature de l'image
81
82 if(move_uploaded_file($file['tmp_name'], $filePath)) {
83 $image_url = str_replace(basename($_SERVER['SCRIPT_NAME']), '', $_SERVER['SCRIPT_NAME']);
84 echo json_encode(['location' => $image_url . $filePath]); // renvoyer l'URL de l'image téléchargée
85 }
86 else{
87 http_response_code(500);
88 echo json_encode(['message' => 'Erreur 500: Internal Server Error']);
89 }
90 }
91 else{
92 http_response_code(400);
93 echo json_encode(['message' => 'Erreur 400: Bad Request']);
94 }
95 die;
96}
97
98// détection des requêtes de type XHR, pas d'utilité pour l'instant
99/*elseif(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'){
100 echo "requête XHR reçue par le serveur";
101 die;
102}*/
103
104
diff --git a/src/controller/installation.php b/src/controller/installation.php
new file mode 100644
index 0000000..a692618
--- /dev/null
+++ b/src/controller/installation.php
@@ -0,0 +1,144 @@
1<?php
2// src/controller/installation.php
3
4declare(strict_types=1);
5
6use App\Entity\Page;
7use App\Entity\Node;
8use App\Entity\NodeData;
9use App\Entity\Image;
10use Doctrine\Common\Collections\ArrayCollection;
11use Doctrine\ORM\EntityManager;
12
13function installation(): void
14{
15 /* -- extensions PHP -- */
16 $extensions = [];
17 foreach($extensions as $extension){
18 if(!extension_loaded($extension))
19 {
20 echo("l'extension " . $extension . ' est manquante<br>');
21 }
22 }
23 if(!extension_loaded('imagick') && !extension_loaded('gd')){
24 echo("il manque une de ces extensions au choix: imagick (de préférence) ou gd<br>");
25 }
26
27 /* -- droits des fichiers et dossiers -- */
28 $droits_dossiers = 0700;
29 $droits_fichiers = 0600;
30
31 // accès interdit en HTTP
32 if(!file_exists('../config/.htaccess')){
33 $contenu = <<< HTACCESS
34<Files "config.ini">
35 Order Allow,Deny
36 Deny from all
37</Files>
38HTACCESS;
39
40 $fichier = fopen('../config/.htaccess', 'w');
41 fputs($fichier, $contenu);
42 fclose($fichier);
43 chmod('../config/.htaccess', $droits_fichiers);
44 //echo("danger<br>pas de .htaccess dans config<br>prévenez le respondable du site");
45 //die;
46 }
47
48 // accès limité en local (600) pour config.ini
49 if(substr(sprintf('%o', fileperms('../config/config.ini')), -4) != 600){
50 chmod('../config/config.ini', $droits_fichiers);
51 }
52
53 // création de data et sous-dossiers
54 if(!file_exists('../data')){
55 mkdir('../data/');
56 chmod('../data/', $droits_dossiers);
57 }
58 if(!touch('../data')){
59 echo("dossier data non autorisé en écriture");
60 die;
61 }
62 $sous_dossiers = array('images', 'images-mini', 'videos');
63 foreach ($sous_dossiers as $sous_dossier){
64 if(!file_exists('../data/' . $sous_dossier)){
65 mkdir('../data/' . $sous_dossier);
66 chmod('../data/' . $sous_dossier, $droits_dossiers);
67 }
68 if(!touch('../data/' . $sous_dossier)){
69 echo("dossier data non autorisé en écriture");
70 die;
71 }
72 }
73}
74
75// création de la page d'accueil à la toute 1ère visite du site
76// les informations ici ne sont pas demandées à l'utilisateur pour l'instant (on verra ça plus tard)
77function makeStartPage(EntityManager $entityManager){
78 /* -- table page -- */
79 // paramètres: name_page, end_of_path, reachable, in_menu, parent
80 $accueil = new Page('Accueil', 'accueil', true, true, NULL);
81 $connection = new Page('Connexion', 'connexion', true, false, NULL);
82 $article = new Page('Article', 'article', true, false, NULL);
83 $edit_page = new Page("Modification d'une page", 'modif_page', true, false, NULL);
84 $new_page = new Page('Nouvelle page', 'nouvelle_page', true, false, NULL);
85 $edit_paths = new Page("Menu et chemins", 'menu_chemins', true, false, NULL);
86
87 /* -- table node -- */
88 // paramètres: name_node, article_timestamp, attributes, position, parent, page, article
89 $head_accueil = new Node('head', NULL, ['css_array' => ['body', 'head', 'nav', 'main', 'foot'], 'js_array' => ['main']], 1, NULL, $accueil, NULL);
90 $header = new Node('header', NULL, [], 2, NULL, NULL, NULL);
91 $nav = new Node('nav', NULL, [], 1, $header, NULL, NULL);
92 $main = new Node('main', NULL, [], 3, NULL, NULL, NULL);
93 $footer = new Node('footer', NULL, [], 4, NULL, NULL, NULL);
94 $breadcrumb = new Node('breadcrumb', NULL, [], 1, $footer, NULL, NULL);
95 $head_login = new Node('head', NULL, ["stop" => true, 'css_array' => ['body', 'head', 'nav', 'main'], 'js_array' => ['main']], 1, NULL, $connection, NULL);
96 $login = new Node('login', NULL, [], 1, $main, $connection, NULL);
97 $head_article = new Node('head', NULL, ['css_array' => ['body', 'head', 'nav', 'main', 'foot'], 'js_array' => ['main']], 1, NULL, $article, NULL);
98
99 /* -- table image -- */
100 // paramètres: file_name, file_path, file_path_mini, mime_type, alt
101 $favicon = new Image("favicon48x48.png", NULL, "assets/favicon48x48.png", "image/png", "favicon");
102 $logo = new Image("logo-120x75.jpg", NULL, "assets/logo-120x75.jpg", "image/png", "head_logo");
103 $facebook = new Image("facebook.svg", NULL, "assets/facebook.svg", "image/svg+xml", "facebook");
104 $instagram = new Image("instagram.svg", NULL, "assets/instagram.svg", "image/svg+xml", "instagram");
105 $fond_piscine = new Image("fond-piscine.jpg", "assets/fond-piscine.jpg", NULL, "images/jpg", "fond-piscine");
106
107 /* -- table node_data -- */
108 // paramètres: data, node
109 $head_accueil_data = new NodeData(["description" => "Club, École de natation et Perfectionnement", "title" => "Les Nageurs Bigoudens"], $head_accueil, new ArrayCollection([$favicon]));
110 $header_data = new NodeData(["description" => "Club, École de natation et Perfectionnement", "title" => "Les Nageurs Bigoudens", "facebook_link" => "https://www.facebook.com/nageursbigoudens29120", "instagram_link" => "https://www.instagram.com/nageursbigoudens/"], $header, new ArrayCollection([$logo, $facebook, $instagram, $fond_piscine]));
111 $footer_data = new NodeData(["adresse" => "17, rue Raymonde Folgoas Guillou, 29120 Pont-l’Abbé", "contact_nom" => "Les Nageurs Bigoudens", "e_mail" => "nb.secretariat@orange.fr"], $footer);
112 $head_login_data = new NodeData(["description" => "Connexion", "title" => "Connexion"], $head_login, new ArrayCollection([$favicon]));
113 $head_article_data = new NodeData(["description" => "", "title" => ""], $head_article, new ArrayCollection([$favicon]));
114
115 $entityManager->persist($accueil);
116 $entityManager->persist($connection);
117 $entityManager->persist($article);
118 $entityManager->persist($edit_page);
119 $entityManager->persist($new_page);
120 $entityManager->persist($edit_paths);
121 $entityManager->persist($head_accueil);
122 $entityManager->persist($header);
123 $entityManager->persist($nav);
124 $entityManager->persist($main);
125 $entityManager->persist($footer);
126 $entityManager->persist($breadcrumb);
127 $entityManager->persist($head_login);
128 $entityManager->persist($login);
129 $entityManager->persist($head_article);
130 $entityManager->persist($favicon);
131 $entityManager->persist($logo);
132 $entityManager->persist($facebook);
133 $entityManager->persist($instagram);
134 $entityManager->persist($fond_piscine);
135 $entityManager->persist($head_accueil_data);
136 $entityManager->persist($header_data);
137 $entityManager->persist($footer_data);
138 $entityManager->persist($head_login_data);
139 $entityManager->persist($head_article_data);
140 $entityManager->flush();
141
142 header('Location: ' . new URL);
143 die;
144} \ No newline at end of file
diff --git a/src/controller/password.php b/src/controller/password.php
new file mode 100644
index 0000000..d5e66ff
--- /dev/null
+++ b/src/controller/password.php
@@ -0,0 +1,357 @@
1<?php
2// src/controller/password.php
3//
4// test mot de passe et captcha
5
6declare(strict_types=1);
7
8use Doctrine\ORM\EntityManager;
9use App\Entity\User;
10
11// exécutée dans installation.php à l'ouverture de chaque page
12function existUsers(EntityManager $entityManager)
13{
14 // lecture
15 $users = $entityManager->getRepository(User::class)->findAll();
16
17 // cas particulier table vide
18 if(count($users) === 0)
19 {
20 $_GET = [];
21 $_SESSION['user'] = '';
22 $_SESSION['admin'] = false;
23
24 // création d'un utilisateur, puis rechargement de la page
25 createPassword($entityManager);
26 }
27}
28
29
30function createPassword(EntityManager $entityManager)
31{
32 // fonction exécutée à priori deux fois d'affilée: affichage puis traitement de la saisie
33
34 // II - traitement
35 unset($_SESSION['user']);
36
37 $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0;
38 $captcha = isset($_POST['captcha']) ? controlCaptchaInput($_POST['captcha']) : 0;
39
40 $error = '';
41 if(!isset($_POST['captcha'])) // page rechargée
42 {
43 //$error = '';
44 }
45 elseif($captcha == 0)
46 {
47 $error = 'error_non_valid_captcha';
48 }
49 elseif($captcha_solution == 0)
50 {
51 //$error = '';
52 }
53 elseif($captcha != $captcha_solution) // le test!
54 {
55 $error = 'bad_solution_captcha';
56 }
57 elseif(!isset($_POST['password']) || empty($_POST['password'])
58 || (!isset($_POST['login']) || empty($_POST['login'])))
59 {
60 $error = 'bad_login_or_password';
61 }
62 else
63 {
64 // -> caractères HTML dangereux supprimés
65 $login = Security::secureString($_POST['login']);
66 $password = Security::secureString($_POST['password']);
67
68 // -> prévenir la validation par erreur d'une chaine "vide"
69 $login = removeSpacesTabsCRLF($login);
70 $password = removeSpacesTabsCRLF($password);
71
72 // conformité
73 if(isset($password) && isset($login)
74 && $password == $_POST['password'] && $login == $_POST['login'])
75 {
76 // enregistrement et redirection
77 $password = password_hash($password, PASSWORD_DEFAULT);
78 $user = new App\Entity\User($login, $password);
79 $entityManager->persist($user);
80 $entityManager->flush();
81
82 header('Location: ' . new URL);
83 exit();
84 }
85 else
86 {
87 $error = 'bad_password';
88 }
89 }
90
91 // inséré dans $captchaHtml puis dans $formulaireNouveauMDP
92 $captcha = createCaptcha();
93 // enregistrement de la réponse du captcha pour vérification
94 $_SESSION['captcha'] = $captcha[2]; // int
95
96
97 // I - affichage
98 $title = 'Bienvenue nageur bigouden';
99 $subHeading = 'Veuillez choisir les codes que vous utiliserez pour gérer le site.';
100
101 // même vue que la fonction changerMotDePasse()
102 require('../src/view/password.php');
103
104 echo($header);
105 if($error != '')
106 {
107 sleep(1);
108 echo($error_messages[$error]);
109 }
110 echo($formulaireNouveauMDP);
111 echo($error_messages['forbidden_characters']);
112 echo($footer);
113 die;
114}
115
116
117function connect(LoginBuilder $builder, EntityManager $entityManager)
118{
119 // déjà connecté
120 if($_SESSION['admin'])
121 {
122 header('Location: ' . new URL);
123 die;
124 }
125
126 // II - traitement
127 $_SESSION['user'] = '';
128 $_SESSION['admin'] = false;
129
130 $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0;
131 $captcha = isset($_POST['captcha']) ? controlCaptchaInput($_POST['captcha']) : 0;
132
133 $error = '';
134 if(!isset($_POST['captcha'])) // page rechargée
135 {
136 //$error = '';
137 }
138 elseif($captcha == 0)
139 {
140 $error = 'error_non_valid_captcha';
141 }
142 elseif($captcha_solution == 0)
143 {
144 //$error = '';
145 }
146 elseif($captcha != $captcha_solution) // le test!
147 {
148 $error = 'bad_solution_captcha';
149 }
150 elseif(!isset($_POST['login']) || empty($_POST['login'])
151 || !isset($_POST['password']) || empty($_POST['password']))
152 {
153 $error = 'bad_password';
154 }
155 else // c'est OK
156 {
157 $login = $_POST['login'];
158 $password = $_POST['password'];
159 $user = getUser($login, $entityManager);
160
161 // enregistrement et redirection
162 if(password_verify($password, $user->getPassword()))
163 {
164 session_start();
165 $_SESSION['user'] = $login;
166 $_SESSION['admin'] = true;
167 $link = new URL(isset($_GET['from']) ? ['page' => $_GET['from']] : []);
168 isset($_GET['id']) ? $link->addParams(['id' => $_GET['id']]) : '';
169 header('Location: ' . $link);
170 die;
171 }
172 else
173 {
174 $error = 'bad_password';
175 }
176 }
177
178 // inséré dans $captchaHtml puis dans $formulaireNouveauMDP
179 $captcha = createCaptcha();
180 // enregistrement de la réponse du captcha pour vérification
181 $_SESSION['captcha'] = $captcha[2]; // int
182
183 // I - affichage
184 $title = "Connexion";
185 $subHeading = "Veuillez saisir votre identifiant (e-mail) et votre mot de passe.";
186
187 require('../src/view/password.php');
188
189 //$builder->addHTML($header);
190 if($error != '')
191 {
192 sleep(1);
193 $builder->addHTML($error_messages[$error]);
194 }
195 $builder->addHTML($formulaireConnexion);
196 //$builder->addHTML($warning_messages['message_cookie']);
197 $builder->addHTML($warning_messages['private_browsing']);
198 $builder->addHTML($footer);
199
200 //die;
201}
202
203
204function changePassword(EntityManager $entityManager)
205{
206 // fonction exécutée à priori deux fois d'affilée: affichage puis traitement de la saisie
207
208 // OUT !!
209 if(empty($_SESSION['user']) || !$_SESSION['admin'])
210 {
211 $_SESSION['user'] = '';
212 $_SESSION['admin'] = false;
213 header('Location: index.php');
214 die;
215 }
216
217 // II - traitement
218 $error = '';
219 $success = false;
220 if(empty($_POST)) // première fois ou page rechargée
221 {
222 //
223 }
224 elseif(!isset($_POST['login']) || empty($_POST['login'])
225 || !isset($_POST['old_password']) || empty($_POST['old_password'])
226 || !isset($_POST['new_password']) || empty($_POST['new_password']))
227 {
228 $error = 'bad_login_or_password';
229 }
230 else
231 {
232 // sécurisation de la saisie
233 $new_password = Security::secureString($_POST['new_password']);
234 $login = Security::secureString($_POST['login']);
235 $old_password = Security::secureString($_POST['old_password']);
236
237 // éviter d'enregistrer une chaîne vide
238 $new_password = removeSpacesTabsCRLF($new_password);
239
240 // tests de conformité
241 if($login != $_POST['login'] || $old_password != $_POST['old_password'] || $new_password != $_POST['new_password'])
242 {
243 $error = 'forbidden_characters';
244 }
245 else
246 {
247 $user = getUser($login, $entityManager);
248
249 if(password_verify($old_password, $user->getPassword()))
250 {
251 // enregistrement et redirection
252 $new_password = password_hash($new_password, PASSWORD_DEFAULT);
253 $user->setPassword($new_password);
254 $entityManager->flush();
255 $success = true;
256 }
257 else
258 {
259 $error = 'bad_password';
260 }
261 }
262 }
263
264
265 // I - affichage
266 $title = "Nouveau mot de passe";
267 $subHeading = "Veuillez vous identifier à nouveau puis saisir votre nouveau mot de passe.";
268
269 require('../src/view/password.php');
270
271 echo($header);
272 if($error != '')
273 {
274 sleep(1); // sécurité TRÈS insuffisante à la force brute
275 echo($error_messages[$error]);
276 }
277 elseif($success)
278 {
279 $success = false;
280 echo($alertJSNewPassword);
281 die;
282 }
283 echo($formulaireModifMDP);
284 echo($footer);
285 die;
286}
287
288
289function getUser(string $login, EntityManager $entityManager): User
290{
291 $users = $entityManager->getRepository('App\Entity\User')->findBy(['login' => $login]);
292
293 // détection d'un abus
294 if(count($users) === 0)
295 {
296 $_SESSION['user'] = '';
297 $_SESSION['admin'] = false;
298
299 header('Location: index.php'); // page création d'un mot de passe à l'attérissage
300 die;
301 }
302
303 foreach($users as $user)
304 {
305 if($user->getLogin() === $login)
306 {
307 return $user;
308 }
309 }
310 header('Location: ' . new URL);
311 die;
312}
313
314
315function disconnect(EntityManager $entityManager)
316{
317 // nettoyage complet
318 $_SESSION = []; // mémoire vive
319 session_destroy(); // fichier côté serveur
320 setcookie('PHPSESSID', '', time() - 4200, '/'); // cookie de session
321 $link = new URL(['page' => $_GET['page']]);
322 isset($_GET['id']) ? $link->addParams(['id' => $_GET['id']]) : '';
323 header('Location: ' . $link);
324 die;
325}
326
327
328function createCaptcha(): array
329{
330 $a = rand(2, 9);
331 $b = rand(2, 9);
332 return array(toLettersFrench($a), toLettersFrench($b), $a * $b);
333}
334
335function toLettersFrench(int $number): string
336{
337 return match($number)
338 {
339 2 => 'deux',
340 3 => 'trois',
341 4 => 'quatre',
342 5 => 'cinq',
343 6 => 'six',
344 7 => 'sept',
345 8 => 'huit',
346 9 => 'neuf',
347 default => '', // erreur
348 };
349}
350
351// on veut des chiffres
352function controlCaptchaInput(string $captcha = '0'): int
353{
354 // $captcha est un POST donc une chaîne, '2.3' est acceptés
355 // (int) supprime les décimales
356 return (is_numeric($captcha) && $captcha == (int) $captcha) ? (int) $captcha : 0;
357} \ No newline at end of file
diff --git a/src/controller/post.php b/src/controller/post.php
new file mode 100644
index 0000000..926a5ae
--- /dev/null
+++ b/src/controller/post.php
@@ -0,0 +1,17 @@
1<?php
2// src/controller/post.php
3
4declare(strict_types=1);
5
6if($_SERVER['REQUEST_METHOD'] === 'POST' && $_SESSION['admin'] === true)
7{
8 // requêtes AJAX
9 require '../src/controller/ajax.php';
10
11 // formulaires HTML
12 if(isset($_POST['from']) // page d'où vient la requête
13 && isset($_POST)) // données
14 {
15 echo "requête envoyée en validant un formulaire";
16 }
17}