Forráskód Böngészése

Merge branch 'master' of git.unistra.fr:W31/W31

Clément Krebs 6 éve
szülő
commit
7e980299ad

+ 66 - 0
LARAVEL-Installation.md

@@ -0,0 +1,66 @@
+Laravel
+=======
+
+Installation sur webetu
+------------------------
+
+**Attention : cette installation n'est possible que sur webetu.**
+
+1. Initialisation d'une un projet `[NAME]` :
+
+```
+$ composer create-project --prefer-dist laravel/laravel [NAME]
+```
+
+2. Lancement d'un serveur local :
+
+```
+$ cd [NAME]
+$ php artisan serve
+Laravel development server started: http://127.0.0.1:8000
+```
+
+Si tout s'est bien passé, votre navigateur affiche une page blanche avec le contenu suivant avec l'URL `http://127.0.0.1:8000` :
+
+```
+                Laravel
+Docs Laracasts News Blog Nova Forge Vapor GitHub
+```
+
+
+Installation sur votre ordi personnel
+--------------------------------------
+
+### Dépendances
+
+- php-fpm
+- php-zip
+- php-xml
+- php-mbstring
+
+### Procédure
+
+1. Installation manuelle de Composer
+
+```
+$ sudo apt install composer
+```
+
+2. Initialisation d'une application `[NAME]` basée sur laravel
+
+```
+$ composer create-project --prefer-dist laravel/laravel [NAME]
+```
+
+3. Exposition du site sur le serveur web
+
+```
+$ php artisan serve
+```
+
+Si tout s'est bien passé, votre navigateur affiche une page blanche avec le contenu suivant avec l'URL `http://127.0.0.1:8000` :
+
+```
+                Laravel
+Docs Laracasts News Blog Nova Forge Vapor GitHub
+```

+ 32 - 0
LARAVEL-arborescence.md

@@ -0,0 +1,32 @@
+L'arborescence de Laravel
+=========================
+
+Voici les principaux fichiers et répertoires de Laravel à connaître pour W31.
+Pour plus de détails, voir [la page de Laravel dédiée](https://laravel.com/docs/6.x/structure) :
+
+- `app/Http/Controllers`
+    - contient l'ensemble des contrôleurs que l'on crée
+    - est accessible depuis le routage
+
+- `app/Http/Middleware`
+    - contient l'ensemble des middlewares que l'on crée
+    - est accessible depuis le routage et les contrôleurs
+
+- `app/Http/kernel.php`
+    - fichier où l'on spécifie les middlewares utilisables dans notre application
+
+- `public/index.php`
+    - fichier d'entrée de l'application
+    - récupère l'instance de l'application Laravel définie dans `bootstrap/app.php` et l'exécute
+
+- `resources/views`
+    - contient l'ensemble des vues Blade de votre application
+    - est accessible depuis le routage et les contrôleurs
+
+- `routes/web.php`
+    - défini l'ensemble des routes pouvant être atteintes par les utilisateurs
+    - fait appel aux middleware et contrôleurs lorsque cela est nécessaire
+
+- `.env`
+    - fichier de configuration de l'application
+    - contient les variables pour l'accès à la BDD

+ 1 - 0
README.md

@@ -13,6 +13,7 @@ Ensuite, pour **utiliser** de votre dépôt, regardez la page [HOWTO.md](HOWTO.m
 
 - [CM n°1 : Introduction à PHP](http://adrien.krahenbuhl.fr/courses/IUTRS/W31/CM1)
 - [CM n°2 : POO et PDO](http://adrien.krahenbuhl.fr/courses/IUTRS/W31/CM2)
+- [CM n°3 : Laravel](http://adrien.krahenbuhl.fr/courses/IUTRS/W31/CM3)
 
 1 - Créer un **clône distant** sur Gitlab
 -----------------------------------------

+ 1 - 1
TP4.md

@@ -112,7 +112,7 @@ Exercice 6 : Passage en POO - Changement de mot de passe
 Exercice 7 : Passage en POO - Suppression d'un utilisateur
 ----------------------------------------------------------
 
-1. Dans la classe `User`, écrivez une méthode publique `delete`, avec un mot de passe en paramètre, qui effectue les étapes du fichier `deleteuser.php` relatives à la BDD, c'est-à-dire :
+1. Dans la classe `User`, écrivez une méthode publique `delete` qui effectue les étapes du fichier `deleteuser.php` relatives à la BDD, c'est-à-dire :
     - faire une requête vers la BDD pour supprimer l'utilisateur `$this`
     - déclenche une exception PHP si la requête échoue
 2. Modifiez le fichier `deleteuser.php` en remplaçant tout ce qui concerne les requêtes vers la BDD par :

+ 89 - 0
TP5.md

@@ -0,0 +1,89 @@
+TP5 - Prise en main de Laravel
+==============================
+
+Objectif
+--------
+
+Intégrer votre application d'authentification au framework Laravel.
+Vous pouvez vous baser au choix :
+
+- sur de vos propres sources résultant du TP4
+- sur la proposition du répertoire [correction/TP4](correction/TP4)
+
+Noter que le but de ce TP est d'obtenir une application fonctionnelle **mais qui ne respectera pas encore le découpage MVC**.
+
+Les TPs suivants permettront de séparer progressivement les parties Modèle, Vue et Contrôleur en utilisant à bon escient les fonctionnalités offertes par Laravel.
+
+
+Exercice 1 : Installation
+-------------------------
+
+Suivez les indications donnés dans [LARAVEL-installation.md](LARAVEL-installation.md).
+
+**Attention : `composer` n'est installé que sur webetu, pas sur troglo.**
+
+
+Exercice 2 : Préparation des vues
+---------------------------------
+
+1. Copiez les fichiers suivants du TP4 dans `resources/views/` :
+    - `adduser.php`
+    - `authenticate.php`
+    - `changepassword.php`
+    - `deleteuser.php`
+    - `formpassword.php`
+    - `signin.php`
+    - `signout.php`
+    - `signup.php`
+    - `welcome.php`
+
+1. Dans `routes/web.php` :
+    - Écrivez les routes GET et POST pour les fichiers ci-dessus (ne pas utiliser `Route::view`)
+    - Faites en sorte qu'une requête vers la racine du site propose la vue `signin.php`
+    - Testez toutes les routes GET. Regardez ce qu'il se passe lorsqu'on demande une route qui n'a pas été prévue dans `routes/web.php`
+
+1. Dans chaque fichier remplacer toutes les références à des fichiers PHP par des références à des routes.
+
+1. Dans le fichier `app/Http/Kernel.php`, commenter les lignes qui font référence au middleware `VerifyCsrfToken`.
+
+
+Exercice 3 : Controleur et PDO
+------------------------------
+
+1. Dans le fichier `.env`,  modifiez les champs `DB_HOST`, `DB_DATABASE`, `DB_USERNAME` et `DB_PASSWORD` avec les informations de l'ancien fichier `bdd.php`.
+
+1. Copier le fichier `models/User.php` du TP4 dans le répertoire `app/` de votre application et le renommer `MyUser.php`. **Attention, ne pas écraser le fichier `User.php` existant déjà dans ce répertoire.**
+
+1. Dans `MyUser.php` :
+    - déclarer le namespace `App`
+    - remplacer les `User::USER_TABLE` par `MyUser::USER_TABLE`
+    - remplacer tous les `MyPDO::pdo()` par `DB::connection()->getPdo()`
+    - ajouter en entête les deux `use` suivants :
+        ```php
+        use use Illuminate\Support\Facades\DB;
+        use PDO;
+        ```
+
+1. Dans chacun des fichiers `adduser.php`, `authenticate.php`, `changepassword.php`, `deleteuser.php` et `signout.php` :
+    - remplacer les `require_once('models/User');` par des `use App\MyUser;`
+    - remplacer les `new User` par des `new MyUser`
+
+**À ce stade, l'application devrait être entièrement fonctionnelle. Solutionner les problèmes avant de passer à la suite.**
+
+
+Exercice 4 : Le grand nettoyage
+-------------------------------
+
+1. Supprimer toutes les vérifications de `REQUEST_METHOD` : c'est maintenant le routage qui le gère.
+
+1. Supprimer tous les `session_start()` de tous vos fichiers.
+
+1. Dans `routes/web.php`, mettre toutes les routes dans un groupe. Dans la fonction qui contient maintenant toutes les routes, ajouter en première instruction un `session_start()`.
+
+1. Dans `routes/web.php`, remplacer l'appel à la vue `signout` par le code du fichier `signout.php` et modifier l'appel à `header()` par un appel à la méthode `redirect()` de Laravel. Supprimer `signout.php`.
+
+1. Dans le même esprit que la question précédente, déplacer le code de vérification de connexion situé au début des fichiers `changepassword.php`, `deleteuser.php`, `formpassword.php` et `welcome.php` dans les fonctions de routage correspondantes. Remplacer les appels à `header()` par des appels à la méthode `redirect()` de Laravel.
+
+1. Dans `routes/web.php`, placer les routes de la question précédente (+ la route `signout`) dans un groupe préfixé par `admin`. Ajouter `admin/` à ces routes dans tous les `header()` de tous vos fichiers de vues.
+
+**Vérifier que tout fonctionne.**

+ 71 - 0
correction/TP4/adduser.php

@@ -0,0 +1,71 @@
+<?php
+/******************************************************************************
+ * On démarre la session
+ */
+session_start();
+
+// On reset les messages
+unset($_SESSION['message']);
+
+/******************************************************************************
+ * On vérifie que la méthode HTTP utilisée est bien POST
+ */
+if ($_SERVER['REQUEST_METHOD'] != 'POST')
+{
+    header('Location: signup.php');
+    exit();
+}
+
+// On vérifie qu'on a bien reçu les données en POST
+if ( !isset($_POST['login'],$_POST['password'],$_POST['confirm']) )
+{
+    $_SESSION['message'] = "Some POST data are missing.";
+    header('Location: signup.php');
+    exit();
+}
+
+// On les sécurise les données POST.
+$login = htmlspecialchars($_POST['login']);
+$password = htmlspecialchars($_POST['password']);
+$confirm = htmlspecialchars($_POST['confirm']);
+
+if ( $password !== $confirm )
+{
+    $_SESSION['message'] = "The two passwords differ.";
+    header('Location: signup.php');
+    exit();
+}
+
+/******************************************************************************
+ * On inclut le fichier contenant la définition de la classe User
+ */
+require_once('models/User.php');
+
+//On crée l'utilisateur
+$user = new User($login,$password);
+
+try {
+    // On crée l'utilisateur dans la BDD
+    $user->create();
+}
+catch (PDOException $e) {
+    // Si erreur lors de la création de l'objet PDO
+    // (déclenchée par MyPDO::pdo())
+    $_SESSION['message'] = $e->getMessage();
+    header('Location: signup.php');
+    exit();
+}
+catch (Exception $e) {
+    // Si erreur durant l'exécution de la requête
+    // (déclenchée par le throw de $user->create())
+    $_SESSION['message'] = $e->getMessage();
+    header('Location: signup.php');
+    exit();
+}
+
+/******************************************************************************
+ * Si tout est ok, on indique que le compte est crée et on se rend sur signin.php
+ */
+$_SESSION['message'] = "Account created! Now, signin.";
+header('Location: signin.php');
+exit();

+ 68 - 0
correction/TP4/authenticate.php

@@ -0,0 +1,68 @@
+<?php
+/******************************************************************************
+ * On démarre la session
+ */
+session_start();
+
+// On reset les messages
+unset($_SESSION['message']);
+
+/******************************************************************************
+ * On vérifie que la méthode HTTP utilisée est bien POST
+ */
+if ($_SERVER['REQUEST_METHOD'] != 'POST')
+{
+    header('Location: signin.php');
+    exit();
+}
+
+// On vérifie qu'on a bien reçu les données en POST
+if ( !isset($_POST['login'],$_POST['password']) )
+{
+    $_SESSION['message'] = "Some POST data are missing.";
+    header('Location: signin.php');
+    exit();
+}
+
+// On les sécurise les données POST.
+$login = htmlspecialchars($_POST['login']);
+$password = htmlspecialchars($_POST['password']);
+
+/******************************************************************************
+ * On inclut le fichier contenant la définition de la classe User
+ */
+require_once('models/User.php');
+
+//On crée l'utilisateur
+$user = new User($login,$password);
+
+try {
+    // On vérifie qu'il existe dans la BDD
+    if ( !$user->exists() )
+    {
+        $_SESSION['message'] = 'Wrong login/password.';
+        header('Location: signin.php');
+        exit();
+    }
+}
+catch (PDOException $e) {
+    // Si erreur lors de la création de l'objet PDO
+    // (déclenchée par MyPDO::pdo())
+    $_SESSION['message'] = $e->getMessage();
+    header('Location: signin.php');
+    exit();
+}
+catch (Exception $e) {
+    // Si erreur durant l'exécution de la requête
+    // (déclenchée par le throw de $user->exists())
+    $_SESSION['message'] = $e->getMessage();
+    header('Location: signin.php');
+    exit();
+}
+
+/******************************************************************************
+ * Si tout est ok, on se connecte et se rend sur welcome.php
+ */
+$_SESSION['user'] = $login;
+header('Location: welcome.php');
+exit();

+ 80 - 0
correction/TP4/changepassword.php

@@ -0,0 +1,80 @@
+<?php
+/******************************************************************************
+ * On démarre la session
+ */
+session_start();
+
+// On reset les messages
+unset($_SESSION['message']);
+
+/******************************************************************************
+ * On vérifie que l'utilisateur est connecté
+ */
+if ( !isset($_SESSION['user']) )
+{
+    header('Location: signin.php');
+    exit();
+}
+
+/******************************************************************************
+ * On vérifie que la méthode HTTP utilisée est bien POST
+ */
+if ($_SERVER['REQUEST_METHOD'] != 'POST')
+{
+    header('Location: formpassword.php');
+    exit();
+}
+
+// On vérifie qu'on a bien reçu les données en POST
+if ( !isset($_POST['newpassword'],$_POST['confirmpassword']) )
+{
+    $_SESSION['message'] = "Some POST data are missing.";
+    header('Location: formpassword.php');
+    exit();
+}
+
+// On les sécurise les données POST.
+$login           = $_SESSION['user'];
+$newpassword     = htmlspecialchars($_POST['newpassword']);
+$confirmpassword = htmlspecialchars($_POST['confirmpassword']);
+
+// On s'assure que les 2 mts de passes corrspondent
+if ( $newpassword != $confirmpassword )
+{
+    $_SESSION['message'] = "Error: passwords are different.";
+    header('Location: formpassword.php');
+    exit();
+}
+
+/******************************************************************************
+ * On inclut le fichier contenant les informations de connexion à la BDD
+ */
+ require_once('models/User.php');
+
+ //On crée l'utilisateur
+ $user = new User($login);
+
+try {
+    $user->changePassword($newpassword);
+}
+catch (PDOException $e) {
+    // Si erreur lors de la création de l'objet PDO
+    // (déclenchée par MyPDO::pdo())
+    $_SESSION['message'] = $e->getMessage();
+    header('Location: formpassword.php');
+    exit();
+}
+catch (Exception $e) {
+    // Si erreur durant l'exécution de la requête
+    // (déclenchée par le throw de $user->changePassword())
+    $_SESSION['message'] = $e->getMessage();
+    header('Location: formpassword.php');
+    exit();
+}
+
+/******************************************************************************
+ * Si tout est ok, on retourne sur welcome.php
+ */
+$_SESSION['message'] = "Password successfully updated.";
+header('Location: welcome.php');
+exit();

+ 56 - 0
correction/TP4/deleteuser.php

@@ -0,0 +1,56 @@
+<?php
+/******************************************************************************
+ * On démarre la session
+ */
+session_start();
+
+// On reset les messages
+unset($_SESSION['message']);
+
+/******************************************************************************
+ * On vérifie que l'utilisateur est connecté
+ */
+if ( !isset($_SESSION['user']) )
+{
+    header('Location: signin.php');
+    exit();
+}
+
+$login = $_SESSION['user'];
+
+/******************************************************************************
+ * On inclut le fichier contenant la définition de la classe User
+ */
+require_once('models/User.php');
+
+//On crée l'utilisateur
+$user = new User($login);
+
+// Création de l'objet PDO
+try {
+    // On crée l'utilisateur dans la BDD
+    $user->delete();
+}
+catch (PDOException $e) {
+    // Si erreur lors de la création de l'objet PDO
+    // (déclenchée par MyPDO::pdo())
+    $_SESSION['message'] = $e->getMessage();
+    header('Location: welcome.php');
+    exit();
+}
+catch (Exception $e) {
+    // Si erreur durant l'exécution de la requête
+    // (déclenchée par le throw de $user->create())
+    $_SESSION['message'] = $e->getMessage();
+    header('Location: welcome.php');
+    exit();
+}
+
+/******************************************************************************
+ * Si tout est ok, on détruit la session et retourne sur signin.php
+ */
+session_destroy();
+session_start();
+$_SESSION['message'] = "Account successfully deleted.";
+header('Location: signin.php');
+exit();

+ 31 - 0
correction/TP4/formpassword.php

@@ -0,0 +1,31 @@
+<?php
+    session_start();
+    if ( !isset($_SESSION['user']) )
+    {
+        header('Location: signin.php');
+        exit();
+    }
+?>
+<!DOCTYPE html>
+<html>
+	<head>
+		<meta charset="utf-8">
+		<title>Change password</title>
+	</head>
+	<body>
+		<h1>Change password</h1>
+		<form action="changepassword.php" method="post">
+			<label for="newpassword">New password</label>         <input type="password" id="newpassword"     name="newpassword"     required>
+			<label for="confirmpassword">Confirm password</label> <input type="password" id="confirmpassword" name="confirmpassword" required>
+			<input type="submit" value="Change my password">
+		</form>
+		<p>
+			Go back to <a href="welcome.php">Home</a>.
+		</p>
+        <?php if ( isset($_SESSION['message']) && !empty($_SESSION['message']) ) { ?>
+        		<section>
+        			<p><?= $_SESSION['message']; ?></p>
+        		</section>
+        <?php } ?>
+	</body>
+</html>

+ 89 - 0
correction/TP4/models/User.php

@@ -0,0 +1,89 @@
+<?php
+
+require_once("MyPDO.php");
+
+class User
+{
+	private $_login;
+	private $_password;
+
+	private const USER_TABLE = "Users";
+
+	public function __construct( string $login, string $password = null )
+	{
+		$this->setLogin($login);
+		$this->setPassword($password);
+	}
+
+	public function login() : string
+	{
+		return $this->_login;
+	}
+
+	public function setLogin( string $login ) : void
+	{
+		$this->_login = $login;
+	}
+
+	public function password() : string
+	{
+		return $this->_password;
+	}
+
+	public function setPassword( ?string $password ) : void
+	{
+		$this->_password = $password;
+	}
+
+	public function exists() : bool
+	{
+		// 1. On prépare la requête $result
+		$result = MyPDO::pdo()->prepare('SELECT password FROM '.User::USER_TABLE.' WHERE user = :user');
+		// 2. On assigne $login au paramêtre :user
+	    $ok = $result->bindValue( ":user", $this->_login, PDO::PARAM_STR );
+		// 3. On exécute la requête $result
+	    $ok &= $result->execute();
+
+		if (!$ok)
+			throw new Exception("Error: user access in DB failed.");
+
+		// 4. On vérifie que l'utilisateur a été trouvé et que son mot de passe
+		//    correspond à celui de l'attribut $this->_password
+		$user = $result->fetch(PDO::FETCH_ASSOC);
+		return $user && password_verify($this->_password,$user['password']);
+	}
+
+	public function create() : void
+	{
+		$result = MyPDO::pdo()->prepare('INSERT INTO '.User::USER_TABLE.'(user,password) VALUES (:user,:password)');
+	    $ok =  $result->bindValue( ":user", $this->_login, PDO::PARAM_STR );
+	    $ok &= $result->bindValue( ":password", password_hash($this->_password,PASSWORD_DEFAULT), PDO::PARAM_STR );
+	    $ok &= $result->execute();
+
+		if ( !$ok )
+			throw new Exception("Error: user creation in DB failed.");
+	}
+
+	public function changePassword( string $newpassword ) : void
+	{
+		$result = MyPDO::pdo()->prepare('UPDATE '.User::USER_TABLE.' SET password=:password WHERE user=:login');
+		$ok =  $result->bindValue(':login',    $this->_login, PDO::PARAM_STR);
+		$ok &= $result->bindValue(':password', password_hash($newpassword,PASSWORD_DEFAULT), PDO::PARAM_STR);
+		$ok &= $result->execute();
+
+		if ( !$ok || $result->rowCount() != 1 )
+			throw new Exception("Error: password updating failed.");
+
+		$this->setPassword($newpassword);
+	}
+
+	public function delete() : void
+	{
+		$result = MyPDO::pdo()->prepare('DELETE FROM '.User::USER_TABLE.' WHERE user = :login');
+		$ok =  $result->bindValue(':login', $this->_login, PDO::PARAM_STR);
+		$ok &= $result->execute();
+
+		if ( !$ok || $result->rowCount() != 1 )
+			throw new Exception("Error while deleting your account.");
+	}
+}

+ 5 - 0
correction/TP4/models/bdd.php

@@ -0,0 +1,5 @@
+<?php
+
+const SQL_DSN      = 'mysql:host=mysql.iutrs.unistra.fr;dbname=W31;charset=utf8';
+const SQL_USERNAME = '';
+const SQL_PASSWORD = '';

+ 26 - 0
correction/TP4/signin.php

@@ -0,0 +1,26 @@
+<?php
+	session_start();
+?>
+<!DOCTYPE html>
+<html>
+	<head>
+		<meta charset="utf-8">
+		<title>Signin</title>
+	</head>
+	<body>
+		<h1>Signin</h1>
+		<form action="authenticate.php" method="post">
+			<label for="login">Login</label>       <input type="text"     id="login"    name="login"    required autofocus>
+			<label for="password">Password</label> <input type="password" id="password" name="password" required>
+			<input type="submit" value="Signin">
+		</form>
+		<p>
+			If you don't have an account, <a href="signup.php">signup</a> first.
+		</p>
+<?php if ( isset($_SESSION['message']) && !empty($_SESSION['message']) ) { ?>
+		<section>
+			<p><?= $_SESSION['message']; ?></p>
+		</section>
+<?php } ?>
+	</body>
+</html>

+ 4 - 0
correction/TP4/signout.php

@@ -0,0 +1,4 @@
+<?php
+    session_start();
+    session_destroy();
+    header('Location: signin.php');

+ 27 - 0
correction/TP4/signup.php

@@ -0,0 +1,27 @@
+<?php
+	session_start();
+?>
+<!DOCTYPE html>
+<html>
+	<head>
+		<meta charset="utf-8">
+		<title>Signup</title>
+	</head>
+	<body>
+		<h1>Signup</h1>
+		<form action="adduser.php" method="post">
+			<label for="login">Login</label>              <input type="text"     id="login"    name="login"    required autofocus>
+			<label for="password">Password</label>        <input type="password" id="password" name="password" required>
+			<label for="confirm">Confirm password</label> <input type="password" id="confirm"  name="confirm"  required>
+			<input type="submit" value="Signup">
+		</form>
+		<p>
+			If you already have an account, <a href="signin.php">signin</a>.
+		</p>
+<?php if ( isset($_SESSION['message']) && !empty($_SESSION['message']) ) { ?>
+		<section>
+			<p><?= $_SESSION['message']; ?></p>
+		</section>
+<?php } ?>
+	</body>
+</html>

+ 31 - 0
correction/TP4/welcome.php

@@ -0,0 +1,31 @@
+<?php
+	session_start();
+	if ( !isset($_SESSION['user']) )
+	{
+        header('Location: signin.php');
+        exit();
+	}
+?>
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>My account</title>
+    </head>
+    <body>
+        <p>
+			Hello <?= $_SESSION['user']; ?> !<br>
+			Welcome on your account.
+		</p>
+		<ul>
+			<li><a href="formpassword.php">Change password.</a></li>
+			<li><a href="deleteuser.php">Delete my account.</a></li>
+		</ul>
+        <p><a href="signout.php">Sign out.</a></p>
+    </body>
+	<?php if ( isset($_SESSION['message']) && !empty($_SESSION['message']) ) { ?>
+			<section>
+				<p><?= $_SESSION['message']; ?></p>
+			</section>
+	<?php } ?>
+</html>