<?php
declare(strict_types=1);

$ALLOWED_SYMBOLS = array('#', '_', '-', '!', '[', ']', '=', '~', '*', '@', '/', '\\', '$', '%', '?', '&', '(', ')');
// --------
// Sécurité
// --------
if (isValidConstant() != 1) {
    fermeSessionUtilisateur();
}

use chillerlan\QRCode\QRCode;
use chillerlan\QRCode\QROptions;

require_once("lib/Password.php");
require_once("lib/lib2fa.inc.php");

// Style spécial pour le profil
echo "<link type=\"text/css\" href=\"css/style_viewer.css\" rel=\"stylesheet\" />\n";

// Gestionnaire de mot de passe JavaScript
// https://www.cssscript.com/check-strength-passwords-pwstrength/
// https://www.netwrix.com/password_best_practice.html
// Retenir les 10 derniers mots de passe pour éviter une réutilisation.
// Rotation au 6 mois
// Notification par courriel de l'expiration du mot de passe.
// En cas de doute, changer le mot de passe pour votre sécurité.
// Possibilité d'utiliser une phrase comme mot de passe.
// Proposer un générateur de mot de passe pour navigateur.
// Historique des mots de passe
// https://phpgurukul.com/maintain-password-history-using-php-mysql/
// https://huboftutorials.com/how-to-maintain-password-history-using-php-and-mysql/
// ------------------------------
// Caractéristiques du module
// ------------------------------
unset($module);
$module = array(
    "titre" => "Profil de l'utilisateur",
    "urldest" => $SCRIPT_NAME . "?page=profil",
    "recherche" => array("engin" => false),
    "jeton" => $jeton,
    "compte" => 0,
    "taille" => "800px",
    "msg" => array(
        "maj_ok" => "Mise à jour du mot de passe effectuée avec succès.",
        "maj_echec" => "L'opération de mise à jour du mot de passe a échouée.",
        "maj_info_ok" => "Mise à jour des informations personnelles effectuée avec succès.",
        "maj_info_echec" => "L'opération de mise à jour des informations personnelles a échouée.",
        "maj_jeton_ok" => "Mise à jour du jeton de sécurité effectuée avec succès.",
        "maj_jeton_echec" => "L'opération de mise à jour du jeton de sécurité a échouée."
    )
);

$sqlparam["connexion"] = "maitre";
$sqlparam["table"][] = "administration";
$sqlparam["where"][] = "refutilisateur =" . $_SESSION["noutilisateur"];

$result = executerRequeteSql($sqlparam);
if (isset($result) && !empty($result)) {
    $lcellulaire = $result[0]["cellulaire"];
    $lfournisseur = $result[0]["fournisseur"];
}

// ------------------
// Requête sur le tri
// ------------------
if (isset($action)) {
    $msg = "";
    //Cueillette de données du formulaire
    if ($action == "Changer votre mot de passe") {

        $password = new PHPPassword\Password(array(
            'minLength' => 12,
            'minNumbers' => 3,
            'minLetters' => 6,
            'minLowerCase' => 3,
            'minUpperCase' => 3,
            'minSymbols' => 2,
            'maxSymbols' => 3,
            'allowedSymbols' => $ALLOWED_SYMBOLS
        ));

        $pattern = "/[^a-zA-Z]/";
        
        if (!empty(filter_input(INPUT_POST, 'motDePasseActuel'))) {
            $motDePasseActuel = antiInjection(filter_input(INPUT_POST, 'motDePasseActuel'));
        }

        if (!empty(filter_input(INPUT_POST, 'motDePasseNouveau'))) {
            $motDePasseNouveau = antiInjection(filter_input(INPUT_POST, 'motDePasseNouveau'));
        }

        if (!empty(filter_input(INPUT_POST, 'confirmation'))) {
            $confirmation = antiInjection(filter_input(INPUT_POST, 'confirmation'));
        }

        $badResult = false;

        //Mot de passe actuel vide
        if (empty($motDePasseActuel)) {
            $badResult = true;
            $msg = "Le mot de passe actuel est vide.";
        }

        //Nouveau mot de passe actuel vide
        if (!$badResult && empty($motDePasseNouveau)) {
            $badResult = true;
            $msg = "Le nouveau mot de passe est vide.";
        }

        //confirmation actuel vide
        if (!$badResult && empty($confirmation)) {
            $badResult = true;
            $msg = "La confirmation est vide.";
        }

        // Le même que celui actuellement utilisé ?
        if (!$badResult && $motDePasseNouveau === $motDePasseActuel) {
            $badResult = true;
            $msg = "Le nouveau mot de passe est identique à celui actuellement utilisé.";
        }

        // Mot de passe différent ?
        if (!$badResult && $motDePasseNouveau !== $confirmation) {
            $badResult = true;
            $msg = "Le nouveau mot de passe diffère de la confirmation.";
        }

        // Mot de passe presque identique ou similaire
        if (!$badResult && levenshtein($motDePasseActuel, $motDePasseNouveau) <= 4) {
            $badResult = true;
            $msg = "Le nouveau mot de passe est trop similaire au mot de passe actuel.";
        }

        // Respecte la politique de création des mots de passe ?
        //TODO en faire une négation pour validatePassword.
        if (!$badResult && $password->validatePassword($motDePasseNouveau)) {
            $badResult = true;
            $msg = "Le nouveau mot de passe ne respecte pas la politique de création des mots de passe.";
        }

        // Prévention contre les attaques de dictionnaire
        if (!$badResult) {
            unset($sqlparam);
            $sqlparam["connexion"] = "maitre";
            $sqlparam["table"][] = "dictionnaire_mdp";
            $sqlparam["where"][] = "entree like '%" . $motDePasseNouveau . "%'";

            $result = executerRequeteSql($sqlparam);
            if (isset($result) && !empty($result)) {
                $badResult = true;
                $msg = "Le nouveau mot de passe est sujet à des attaques. Veuillez le changer";
            }

            if (!$badResult) {
                $replacement = '';
                $valeur = preg_replace($pattern, $replacement, $motDePasseNouveau);
                unset($sqlparam);
                $sqlparam["connexion"] = "maitre";
                $sqlparam["table"][] = "dictionnaire_mdp";
                $sqlparam["where"][] = "entree like '%" . $valeur . "%'";

                $result = executerRequeteSql($sqlparam);
                if (isset($result) && count($result) > 0) {
                    $badResult = true;
                    $msg = "Le nouveau mot de passe est sujet à des attaques. Veuillez le changer";
                }
            }
        }

        // Le nouveau mot de passe ne peut pas être un mot de passe déjà utilisé
        if (!$badResult) {
            unset($sqlparam);
            $sqlparam["connexion"] = "maitre";
            $sqlparam["table"][] = "historique_mdp";
            $sqlparam["where"][] = "noutilisateur = " . $_SESSION["noutilisateur"];
            $sqlparam["champs"][] = "hashloginmps";
            $sqlparam["limite"] = 10;

            $result = executerRequeteSql($sqlparam);
            if (isset($result)) {
                foreach ($result as &$valeur) {
                    if (password_verify($motDePasseNouveau, $valeur["hashloginmps"])) {
                        $badResult = true;
                        $msg = "Vous avez déjà utilisé ce mot de passe auparavant.";
                        break;
                    }
                }
            }
        }

        // Mise à jour du mot de passe si tout est ok.
        if (!$badResult) {
            unset($sqlparam);
            $sqlparam["connexion"] = "maitre";
            $sqlparam["type"] = "UPDATE";
            $sqlparam["table"][] = "administration";
            $sqlparam["champs"]["motdepasse"] = "'$motDePasseNouveau'";
            $sqlparam["champs"]["hashloginmps"] = sprintf("'%s'", password_hash($motDePasseNouveau, PASSWORD_DEFAULT));
            $sqlparam["champs"]["temporaire"] = 0;
            $sqlparam["where"][] = "refutilisateur =" . $_SESSION["noutilisateur"];

            $result = executerRequeteSql($sqlparam);
            if (isset($result)) {
                $module["resultatSQL"] = true;
                $module["message"] = $module["msg"]["maj_ok"];

                unset($sqlparam);
                $sqlparam["connexion"] = "maitre";
                $sqlparam["table"][] = "administration";
                $sqlparam["champs"][] = "hashloginmps";
                $sqlparam["where"][] = "refutilisateur =" . $_SESSION["noutilisateur"];

                $result = executerRequeteSql($sqlparam);

                unset($sqlparam);
                $sqlparam["connexion"] = "maitre";
                $sqlparam["type"] = "INSERT";
                $sqlparam["table"][] = "historique_mdp";
                $sqlparam["champs"]["noutilisateur"] = "'" . $_SESSION["noutilisateur"] . "'";
                $sqlparam["champs"]["hashloginmps"] = sprintf("'%s'", $result[0]["hashloginmps"]);

                executerRequeteSql($sqlparam);
                $_SESSION["temporaire"] = 0;
            } else {

                $module["resultatSQL"] = false;
                $module["message"] = $module["msg"]["maj_echec"];
            }
        } else {
            $module["resultatSQL"] = false;
            $module["message"] = $msg;
        }
    } elseif ($action == "Appliquer votre jeton de sécurité") {

        if (!empty($jetonsec)) {
            $secret = $_SESSION["temporary2fa"];
            unset($sqlparam);
            $sqlparam["connexion"] = "maitre";
            $sqlparam["type"] = "UPDATE";
            $sqlparam["table"][] = "administration";
            $sqlparam["champs"]["secret2fa"] = "'$secret'";
            $sqlparam["where"][] = "refutilisateur =" . $_SESSION["noutilisateur"];
            $result = executerRequeteSql($sqlparam);

            if (isset($result) && check2fa($secret, $jetonsec)) {
                $module["resultatSQL"] = true;
                $module["message"] = $module["msg"]["maj_jeton_ok"];
                $_SESSION["2fa_set"] = true;
                unset($_SESSION["temporary2fa"]);
            } else {
                //En cas d'erreur, vider le champ
                unset($sqlparam);
                $sqlparam["connexion"] = "maitre";
                $sqlparam["type"] = "UPDATE";
                $sqlparam["table"][] = "administration";
                $sqlparam["champs"]["secret2fa"] = "NULL";
                $sqlparam["where"][] = "refutilisateur =" . $_SESSION["noutilisateur"];

                executerRequeteSql($sqlparam);

                $module["resultatSQL"] = false;
                $module["message"] = $module["msg"]["maj_jeton_echec"];
                $_SESSION["2fa_set"] = false;
            }
        } else {
            $module["resultatSQL"] = false;
            $module["message"] = "Jeton de sécurité vide";
        }
    } elseif ($action == "Mettre à jour vos informations personnelles") {
        unset($sqlparam);
        $sqlparam["connexion"] = "maitre";
        $sqlparam["type"] = "UPDATE";
        $sqlparam["table"][] = "administration";
        $sqlparam["champs"]["cellulaire"] = "'$cellulaire'";
        $sqlparam["champs"]["fournisseur"] = "'$fournisseur'";
        $sqlparam["where"][] = "refutilisateur =" . $_SESSION["noutilisateur"];

        $result = executerRequeteSql($sqlparam);

        if (isset($result)) {
            $module["resultatSQL"] = true;
            $module["message"] = $module["msg"]["maj_info_ok"];
        } else {
            $module["resultatSQL"] = false;
            $module["message"] = $module["msg"]["maj_info_echec"];
        }
    }
}

//Afficher les symboles permis dans le mot de passe.
function afficheSymboles($ALLOWED_SYMBOLS) {
    $symboles = "";
    foreach ($ALLOWED_SYMBOLS as $clef => $valeur) {
        $symboles .= "&nbsp;" . $valeur . "&nbsp;";
    }
    return $symboles;
}

// Barre de menu
print preparerModule($module);
?>
<form Method="Post" Action="index2.php?page=profil" name="formulaire">
    <?php
    if ($_SESSION["temporaire"] == 0 && $_SESSION["2fa_set"]) {
        ?>
        <div class="boitelogin boitelogin2">
            <fieldset>
                <legend>Zone de contrôle</legend>
                <div class='mdp'>
                    <div style='width:200px; float:left;'>
                        <label for="motDePasseActuel">Mot de passe actuel : </label>
                    </div>
                    <input type="password" id="motDePasseActuel" name="motDePasseActuel"/>
                </div>
                <div style='clear:both;'></div>
                <div class='mdp'>
                    <div style='width:200px; float:left;'>
                        <label for="motDePasseNouveau">Nouveau mot de passe : </label>
                    </div>
                    <input type="password" id="motDePasseNouveau" name="motDePasseNouveau"/>
                </div>
                <div style='clear:both;'></div>
                <div class='mdp'>
                    <div style='width:200px; float:left;'>
                        <label for="Confirmation">Confirmation : </label>
                    </div>
                    <input type="password" id="confirmation" name="confirmation"/>
                </div>
                <br />
                <input type="submit" class="button2" name="action" value="Changer votre mot de passe"/>
                <input type="hidden" name="jeton" value="<?php print $jeton; ?>"/>
            </fieldset>
            <br />
            Règles pour la création d'un mot de passe :<br>
            <ul>
                <li>Longueur minimale : 12 caractères</LI>
                <li>Minimum de 6 lettres</li>
                <li>2 nombres présents (0-9)</li>
                <li>3 caractères en minuscule</li>
                <li>3 caractères en majuscule</li>
                <li> Présence de 2 à 3 caractères spéciaux ci-dessous<br>
                    <pre><?php print afficheSymboles($ALLOWED_SYMBOLS); ?></pre>
                </li>
            </ul>
            Truc : L'emploi d'une phrase facile à retenir est aussi possible.<br/>
            <i>Exemple : L'été sera Merveilleux au Chalet en 2030![</i>
        </div>
        <div class="boitelogin boitelogin2">
            <fieldset>
                <legend>Informations personnelles</legend>
                <div class='mdp'>
                    <div style='width:125px; float:left;'>
                        <label for="cellulaire">Cellulaire : </label>
                    </div>
                    <input type="text" id="cellulaire" name="cellulaire" value="<?php print $lcellulaire; ?>" SIZE='14' MAXLENGTH='14'/>
                </div>
                <div style='clear:both;'></div>
                <div class='mdp'>
                    <div style='width:125px; float:left;'>
                        <label for="fournisseur">Fournisseur : </label>
                    </div>
                    <?php
                    unset($result2);
                    $result2 = getFournisseursCellulaire();
                    unset($param);
                    $param["form"] = "menu2";
                    $param["nom"] = "fournisseur";
                    $param["etq"] = "";
                    $param["defaut"] = $lfournisseur;
                    $param["donnees"] = $result2;
                    $param["menu"] = true;
                    //TODO Mettre un évènement vide pour contourner le gestionnaire interne. À corriger.
                    $param["evt"] = "blank";
                    print formListbox2($param);
                    ?>
                </div>
                <div style='clear:both;'></div>
                <br />
                <input type="submit" class="button2" name="action" value="Mettre à jour vos informations personnelles"/>
                <input type="hidden" name="jeton" value="<?php print $jeton; ?>"/>
            </fieldset>
        </div>
        <script type='text/javascript'>
            // Mise en place des masques de saisie
            $(document).ready(function () {
                $("#cellulaire").mask("(999) 999-9999");
            });

        </script>
        <?php
    } else if ($_SESSION["temporaire"] == 1 && (!$_SESSION["2fa_set"] || empty($_SESSION["2fa_set"]))) {
        ?>
        <div class="boitelogin">
            <fieldset>
                <legend>Zone de contrôle2</legend>
                <div class='mdp'>
                    <div style='width:200px; float:left;'>
                        <label for="motDePasse">Mot de passe actuel : </label>
                    </div>
                    <input type="password" id="motDePasseActuel" name="motDePasseActuel"/>
                </div>
                <div style='clear:both;'></div>
                <div class='mdp'>
                    <div style='width:200px; float:left;'>
                        <label for="motDePasseNouveau">Nouveau mot de passe : </label>
                    </div>
                    <input type="password" id="motDePasseNouveau" name="motDePasseNouveau"/>
                </div>
                <div style='clear:both;'></div>
                <div class='mdp'>
                    <div style='width:200px; float:left;'>
                        <label for="Confirmation">Confirmation : </label>
                    </div>
                    <input type="password" id="confirmation" name="confirmation"/>
                </div>
                <br />
                <input type="submit" class="button2" name="action" value="Changer votre mot de passe"/>
                <input type="hidden" name="jeton" value="<?php print $jeton; ?>"/>
            </fieldset>
            <br />
            Règles pour la création d'un mot de passe :<br>
            <ul>
                <li>Longueur minimale : 12 caractères</LI>
                <li>Minimum de 6 lettres</li>
                <li>Minimum de 2 nombres présents (0-9)</li>
                <li>Minimum de 3 lettres en minuscule</li>
                <li>Minimum de 3 lettres en majuscule</li>
                <li>Présence de 2 à 3 caractères spéciaux <br>                    
                    <?php
                    //(&nbsp;'#',&nbsp;&nbsp;'_',&nbsp;&nbsp;'-',&nbsp;&nbsp;'!',&nbsp;&nbsp;'[',&nbsp;&nbsp;']',&nbsp;&nbsp;'=',&nbsp;'~',&nbsp;&nbsp;'*'&nbsp;)
                    print afficheSymboles($ALLOWED_SYMBOLS);
                    ?>
                </li>
            </ul>
            Truc : L'emploi d'une phrase facile à retenir est aussi possible.<br/>
            Exemple : L'été sera Merveilleux AU chalet en 2030!*
        </div>
        <?php
    } else if (!$_SESSION["2fa_set"] || empty($_SESSION["2fa_set"])) {
        $secret = getUser2faSecret();
        $_SESSION["temporary2fa"] = $secret;
        ?>
        <div class = 'authenticator'>
            <center><h1>Nouveau</h1></center>
            <p>
                Ce nouveau processus renforce la sécurité de connexion à l'application et
                s'assure que l'utilisateur est vraiment vous, comme personne.
            </p>
            <p>
                Désormais, l'utilisation d'une application sera requise pour votre sécurité.
                Ce processus est simple.
            </p>
            <ol>
                <li>
                    Installer l'application Microsoft Authenticator sur votre téléphone cellulaire.<br>
                    <a href="https://www.microsoft.com/fr-ca/security/mobile-authenticator-app">Cliquer ici</a> 
                    <br>
                    <img id = "authenticator" src = "images/ms_authenticator.png" alt = "Microsoft Authenticator" width = "400" height = "96" />
                    <br>
                    <span style="color:blue;font-size:11px;">
                        <i>Cliquer sur l'image pour l'agrandir.</i>
                    </span>
                </li>
                <li>
                    <p>
                        Dans l'application Microsoft Authenticator, effectuer ces quelques étapes.
                    </p>
                    <ol>
                        <li>Cliquer sur le symbole «+» pour ajouter un compte</li>
                        <li>Choisissez le type de compte professionnel ou scolaire</li>
                        <li>Lire (scanner) le code QR ci-dessous afin 
                            d'ajouter le site web des libérations comme émetteur de jeton de sécurité</li>
                    </ol>
                    <p>
                        <?php
                        // Voir la librairie lib2fa pour savoir comment créer des code qr avec Microsoft Authenticator.
                        //TODO changer l'émetteur dans l'adresse de l'image
                        // $data = "otpauth://totp/liberations:" . $_SESSION["adressecourriel"] . "?secret=$secret&issuer=https://liberations.magikweb.ca/";
                        $data = "otpauth://totp/liberations:voiesdurables@gmail.com?secret=$secret&issuer=https://liberations.magikweb.ca/";
                        print "<img src='" . (new QRCode)->render($data) . "' alt='Code QR' />";

                        ?>
                    </p>
                </li>
                <li>
                    Inscrire un jeton de sécurité provenant de Microsoft Authenticator afin d'activer la sécurité renforcée
                    et confirmer que la nouvelle sécurité est active.
                    <div class='mdp'>
                        <div style='width:200px; float:left;'>
                            <label for="jeton">Jeton de sécurité : </label>
                        </div>
                        <input type="text" id="jetonsec" name="jetonsec"/>
                    </div>
                    <br />
                    <input type="submit" class="button2" name="action" value="Appliquer votre jeton de sécurité"/>
                </li>
            </ol>
        </div>
        <div id="image-viewer">
            <span class="close">&times;</span>
            <img class="modal-content" id="full-image">
        </div>
        <script type='text/javascript'>
            $("#authenticator").click(function () {
                $("#full-image").attr("src", $(this).attr("src"));
                $('#image-viewer').show();
            });
            $("#image-viewer .close").click(function () {
                $('#image-viewer').hide();
            });
        </script>
        <?php
    }
    ?>
</form>
