Envoyer un nouveau mot de passe par email
Créez site16 en copiant site15.
- /cms
- ...
- site15
- site16
Dans ce chapitre, nous allons programmer l'envoi à un utilisateur enregistré d'un nouveau mot de passe par email dans un cryptogramme en pièce jointe.
Pour tester le résultat en ligne, entrez http://www.frasq.org/cms/site16 dans la barre d'adresse de votre navigateur.
Allez sur la page d'identification. Cliquez sur le lien Si vous avez oublié votre mot de passe sous le champ de saisie du mot de passe.
Entrez foobar
comme nom, cochez la case de confirmation, tapez le code de vérification et appuyez sur Envoyer.
Un nouveau mot de passe dans un crytogramme a été envoyé par email à l'adresse nobody@frasq.org :
Votre nouveau mot de passe est dans le cryptogramme en pièce jointe.
Si vous n'avez pas demandé à en changer, veuillez ignorer ce message.
NOTE : Votre ancien mot de passe est toujours valide.
Connectez-vous avec le nouveau mot de passe pour l'activer.
À bientôt.
frasq.org - http://www.frasq.org
--------------------------------------------------------------
Commencez par créer l'action password
en ajoutant le fichier password.php dans le dossier actions avec le contenu suivant :
- /cms/site16
- actions
- password.php
- actions
- function password($lang) {
- head('title', translate('password:title', $lang));
- head('description', false);
- head('keywords', false);
- head('robots', 'noindex, nofollow');
- $banner = build('banner', $lang);
- $remindme = build('remindme', $lang);
- $content = view('password', $lang, compact('remindme'));
- $output = layout('standard', compact('banner', 'content'));
- return $output;
- }
L'action password
retourne une page avec un bandeau simple et le formulaire construit par le bloc remindme
.
Pour donner accès à l'action password
, ajoutez un alias par langue dans le fichier includes/aliases.inc :
- 'mot-de-passe' => 'password',
- 'password' => 'password',
Ajoutez le titre de la page d'envoi d'un mot de passe dans le fichier includes/strings.inc :
En français dans le tableau 'fr' :
- 'password:title' => 'Mot de passe',
En anglais dans le tableau 'en' :
- 'password:title' => 'Password',
Ajoutez la vue de la page d'envoi d'un mot de passe dans les dossiers views/fr pour la version en français et views/en pour la version en anglais :
- /cms/site16
- views
- fr
- password.phtml
- en
- password.phtml
- fr
- views
- <h3>Nouvelle clé d'accès</h3>
- <?php echo $remindme; ?>
- <h3>New access key</h3>
- <?php echo $remindme; ?>
Le formulaire d'envoi d'un mot de passe est dans un bloc. Commencez par écrire la vue, d'abord dans une langue :
- /cms/site16
- views
- fr
- password.phtml
- remindme.phtml
- fr
- views
- <div class="form">
- <form action="" method="post">
- <input type="hidden" name="remindme_token" value="<?php echo $token; ?>" />
- <div class="fields">
- <p class="label">Quel est votre nom de connexion ?</p>
- <p class="input"><input type="text" name="remindme_login" id="remindme_login" size="40" maxlength="100" title="Identifiant" onkeypress="return focusonenter(event, 'remindme_code')" value="<?php echo htmlspecialchars($login, ENT_COMPAT, 'UTF-8'); ?>" /></p>
- <p class="info">Vous pouvez aussi entrer votre adresse d'email.</p>
- <p class="input"><input name="remindme_confirmed" id="remindme_confirmed" type="checkbox" title="Confirmation" <?php if ($confirmed) echo 'checked="checked"'; ?> /> Je veux recevoir un nouveau mot de passe</p>
- <?php if ($with_captcha): ?>
- <p class="input">
- <img src="<?php echo $base_path; ?>/captcha/remindme" alt="" title="Code de vérification" />
- :
- <input type="text" name="remindme_code" id="remindme_code" size="4" maxlength="4" title="4 lettres" onkeypress="return submitonenter(event, 'remindme_submit')" value="" />
- </p>
- <?php endif; ?>
- <p class="submit"><button type="submit" name="remindme_submit" id="remindme_submit">Envoyer</button></p>
- </div>
- </form>
Le formulaire comprend 3 champs de saisie : identifiant, confirmation et code de vérification. En plus de $token
, 2 variables sont nécessaires : $login
et $confirmed
. Le paramètre $captcha
détermine si un captcha est affiché. Tous les noms des champs sont préfixés par remindme_
. Le formulaire a un seul bouton appelé remindme_submit
.
Le reste de la vue gère tous les messages d'erreur.
Les erreurs possibles sont $missing_code
, $bad_code
, $missing_login
, $bad_login
, $missing_confirmation
, $internal_error
et $contact_page
.
Si $email_sent
est true
, on signale à l'utilisateur que le message a bien été envoyé et on lui propose un lien sur la page d'identification $user_page
.
- <?php
- if ($errors):
- extract($errors);
- ?>
- <div class="errors">
- <?php if ($missing_code): ?>
- <p>Entrez le code de vérification affiché dans l'image.</p>
- <?php elseif ($bad_code): ?>
- <p>Le code de vérification est incorrect.</p>
- <?php endif; ?>
- <?php if ($missing_login): ?>
- <p>Vous n'avez pas saisi votre identifiant.</p>
- <?php elseif ($bad_login): ?>
- <p>L'identifiant n'est pas valide.</p>
- <?php endif; ?>
- <?php if ($missing_confirmation): ?>
- <p>Cochez la case de confirmation.</p>
- <?php endif; ?>
- <?php if ($internal_error): ?>
- <p>Une erreur interne s'est produite. Si vous pouvez décrire le problème, merci de <a href="<?php echo $contact_page; ?>">nous contacter</a>.
- <?php endif; ?>
- </div>
- <?php endif; ?>
- <?php
- if ($infos):
- extract($infos);
- ?>
- <div class="infos">
- <?php if ($email_sent): ?>
- <p>Un nouveau mot de passe vous a été envoyé par email.<br />
- Pour vous identifier, <a href="<?php echo $user_page; ?>">cliquez ici</a>.</p>
- <?php endif; ?>
- </div>
- <?php endif; ?>
- </div>
Pour valider la vue, écrivez une première version de la fonction remindme
qui se limite à l'affichage du formulaire :
- /cms/site16
- blocks
- remindme.php
- blocks
- require_once 'tokenid.php';
- function remindme($lang, $login=false) {
- $login=$confirmed=$code=$token=false;
- $missing_code=false;
- $bad_code=false;
- $bad_token=false;
- $missing_login=false;
- $bad_login=false;
- $missing_confirmation=false;
- $email_sent=false;
- $user_page=url('user', $lang);
- $internal_error=false;
- $contact_page=false;
- $with_captcha=true;
- $_SESSION['remindme_token'] = $token = token_id();
- $errors = compact('missing_login', 'bad_login', 'missing_confirmation', 'missing_code', 'bad_code', 'internal_error', 'contact_page');
- $infos = compact('email_sent', 'user_page');
- $output = view('remindme', $lang, compact('token', 'with_captcha', 'login', 'confirmed', 'errors', 'infos'));
- return $output;
- }
Entrez http://localhost/cms/site16/fr/mot-de-passe dans la barre d'adresse de votre navigateur.
Éditez la fonction remindme
. Donnez des valeurs aux variables des champs de saisie. Mettez toutes les variables d'erreur à true
. Vérifiez les liens sur la page de contact et sur la page d'identification.
Ajoutez la vue en anglais :
- /cms/site16
- views
- en
- password.phtml
- remindme.phtml
- en
- views
- <div class="form">
- <form action="" method="post">
- <input type="hidden" name="remindme_token" value="<?php echo $token; ?>" />
- <div class="fields">
- <p class="label">What is your connection name?</p>
- <p class="input"><input type="text" name="remindme_login" id="remindme_login" size="40" maxlength="100" title="Identifier" onkeypress="return focusonenter(event, 'remindme_code')" value="<?php echo htmlspecialchars($login, ENT_COMPAT, 'UTF-8'); ?>" /></p>
- <p class="info">You may also enter your email address.</p>
- <p class="input"><input name="remindme_confirmed" id="remindme_confirmed" type="checkbox" title="Confirmation" <?php if ($confirmed) echo 'checked="checked"'; ?> /> I want to receive a new password</p>
- <?php if ($with_captcha): ?>
- <p class="input">
- <img src="<?php echo $base_path; ?>/captcha/remindme" alt="" title="Verification code" />
- :
- <input type="text" name="remindme_code" id="remindme_code" size="4" maxlength="4" title="4 letters" onkeypress="return submitonenter(event, 'remindme_submit')" value="" />
- </p>
- <?php endif; ?>
- <p class="submit"><button type="submit" name="remindme_submit" id="remindme_submit">Send</button></p>
- </div>
- </form>
- <?php
- if ($errors):
- extract($errors);
- ?>
- <div class="errors">
- <?php if ($missing_code): ?>
- <p>Enter the verification code displayed in the image.</p>
- <?php elseif ($bad_code): ?>
- <p>The verification code is incorrect.</p>
- <?php endif; ?>
- <?php if ($missing_login): ?>
- <p>You haven't typed your identifier.</p>
- <?php elseif ($bad_login): ?>
- <p>The identifier is not valid.</p>
- <?php endif; ?>
- <?php if ($missing_confirmation): ?>
- <p>Check the confirmation box.</p>
- <?php endif; ?>
- <?php if ($internal_error): ?>
- <p>An internal error has occurred. If you can describe the problem, please <a href="<?php echo $contact_page; ?>">contact us</a>.</p>
- <?php endif; ?>
- </div>
- <?php endif; ?>
- <?php
- if ($infos):
- extract($infos);
- ?>
- <div class="infos">
- <?php if ($email_sent): ?>
- <p>A new password has been sent to you by email.<br />
- To identify yourself, <a href="<?php echo $user_page; ?>">click here</a>.</p>
- <?php endif; ?>
- </div>
- <?php endif; ?>
- </div>
Entrez http://localhost/cms/site16/en/password dans la barre d'adresse de votre navigateur pour valider la version anglaise.
Une fois la mise au point des vues terminée, complétez remindme
avec le code suivant :
- require_once 'readarg.php';
- require_once 'strflat.php';
- require_once 'validateusername.php';
- require_once 'validatemail.php';
- require_once 'isusernameallowed.php';
- require_once 'ismailallowed.php';
- require_once 'tokenid.php';
Charge le code des fonctions readarg
, strflat
, validate_user_name
, validate_mail
, is_user_name_allowed
, is_mail_allowed
et token_id
.
Ajoutez les fichiers isusernameallowed.php et ismailallowed.php dans le dossier library avec les contenus suivants :
- /cms/site16
- library
- isusernameallowed.php
- ismailallowed.php
- library
- function is_user_name_allowed($name) {
- static $blacklist = array('frasq');
- return !in_array($name, $blacklist);
- }
is_user_name_allowed
retourne false
si $name
est un nom qui est interdit par la liste $blacknamelist
, true
si $name
est autorisé.
- function is_mail_allowed($mail) {
- static $blacklist = array('frasq@frasq.org', 'webmaster@frasq.org', 'keymaster@frasq.org');
- return !in_array($mail, $blacklist);
- }
is_mail_allowed
retourne false
si $mail
est une adresse d'email qui est interdite par la liste $blacknamelist
, true
si an email à $mail
est autorisé.
- function remindme($lang, $login=false) {
- $action='init';
- if (isset($_POST['remindme_submit'])) {
- $action='remindme';
- }
- $login=$confirmed=$code=$token=false;
- switch($action) {
- case 'remindme':
- if (isset($_POST['remindme_login'])) {
- $login=strtolower(strflat(readarg($_POST['remindme_login'], true)));
- }
- if (isset($_POST['remindme_confirmed'])) {
- $confirmed=$_POST['remindme_confirmed'] ? true : false;
- }
- if (isset($_POST['remindme_code'])) {
- $code=readarg($_POST['remindme_code'], true);
- }
- if (isset($_POST['remindme_token'])) {
- $token=readarg($_POST['remindme_token']);
- }
- break;
- default:
- break;
- }
Lit le formulaire.
- $missing_code=false;
- $bad_code=false;
- $bad_token=false;
- $missing_login=false;
- $bad_login=false;
- $missing_confirmation=false;
- $email_sent=false;
- $user_page=false;
- $internal_error=false;
- $contact_page=false;
- $with_captcha=true;
- switch($action) {
- case 'remindme':
- if (!isset($_SESSION['remindme_token']) or $token != $_SESSION['remindme_token']) {
- $bad_token=true;
- break;
- }
- if ($with_captcha) {
- if (!$code) {
- $missing_code=true;
- break;
- }
- $captcha=isset($_SESSION['captcha']) ? $_SESSION['captcha'] : false;
- if (!$captcha or $captcha != strtoupper($code)) {
- $bad_code=true;
- break;
- }
- }
- if (!$login) {
- $missing_login=true;
- }
- else if ((!validate_user_name($login) or !is_user_name_allowed($login)) and (!validate_mail($login) or !is_mail_allowed($login))) {
- $bad_login=true;
- }
- if (!$confirmed) {
- $missing_confirmation=true;
- }
- break;
- default:
- break;
- }
Contrôle les données saisies, en particulier si le nom de connexion est autorisé.
- switch($action) {
- case 'remindme':
- if ($bad_token or $missing_code or $bad_code or $missing_login or $bad_login or $missing_confirmation) {
- break;
- }
- require_once 'models/user.inc';
- $user_id = user_find($login);
- if (!$user_id) {
- $bad_login=true;
- require_once 'log.php';
- write_log('password.err', substr($login, 0, 40));
- break;
- }
- $user = user_get($user_id);
- if (!$user) {
- $internal_error=true;
- break;
- }
Vérifie qu'aucune erreur n'a été détectée, charge le modèle user.inc et récupère le numéro de l'utilisateur avec user_find
.
Si user_find
retourne false
, l'erreur est notée dans le journal password.err.
Sinon le code obtient l'adresse d'email du compte $user_id
avec user_get
.
- require_once 'newpassword.php';
- $newpassword=newpassword();
- if (!user_set_newpassword($user_id, $newpassword)) {
- $internal_error=true;
- break;
- }
Charge la fonction newpassword
puis assigne un nouveau mot de passe au compte de l'utilisateur $user_id
.
- require_once 'emailcrypto.php';
- global $sitename, $webmaster;
- $to=$user['user_mail'];
- $subject = translate('email:new_password_subject', $lang);
- $msg = translate('email:new_password_text', $lang) . "\n\n" . translate('email:salutations', $lang);
- if (!emailcrypto($msg, $newpassword, $to, $subject, $webmaster)) {
- $internal_error=true;
- }
- else {
- $email_sent=$to;
- }
- $confirmed=false;
- break;
- default:
- break;
- }
Charge la fonction emailcrypto
.
Prépare le message d'email selon la langue de l'utilisateur et appelle emailcrypto
avec le nouveau mot de passe, le sujet et le texte du message, l'adresse d'email du destinataire et de l'expéditeur.
- if ($internal_error) {
- $contact_page=url('contact', $lang);
- }
- else if ($email_sent) {
- $user_page=url('user', $lang);
- }
Met $contact_page
à l'URL de la page de contact si $internal_error
est true
ou $user_page
à l'URL de la page d'identification si $email_sent
est true
.
- $_SESSION['remindme_token'] = $token = token_id();
- $errors = compact('missing_login', 'bad_login', 'missing_confirmation', 'missing_code', 'bad_code', 'internal_error', 'contact_page');
- $infos = compact('email_sent', 'user_page');
- $output = view('remindme', $lang, compact('token', 'with_captcha', 'login', 'confirmed', 'errors', 'infos'));
- return $output;
- }
Le reste du code prépare tous les paramètres de la vue, dont le jeton qui est mémorisé dans la variable de session $_SESSION['remindme_token']
, la génère et retourne son contenu.
Définissez les textes du sujet et du message en ajoutant les paramètres email:salutations
, email:new_password_subject
et email:new_password_text
dans le fichier includes/strings.inc, en français et en anglais :
- 'email:salutations' => 'À bientôt.',
- 'email:new_password_subject' => 'frasq.org : Votre mot de passe.',
- 'email:new_password_text' => "Votre nouveau mot de passe est dans le cryptogramme en pièce jointe.\n\nSi vous n'avez pas demandé à en changer, veuillez ignorer ce message.\nNOTE : Votre ancien mot de passe est toujours valide.\n\nConnectez-vous avec le nouveau mot de passe pour l'activer.",
- 'email:salutations' => 'See you soon.',
- 'email:new_password_subject' => 'frasq.org : Your new password.',
- 'email:new_password_text' => "Your new password is in the attached cryptogram.\n\nIf you didn't ask to change it, just ignore this message.\nNOTE: Your old password is still valid.\n\nIdentify yourself with the new password to activate it.",
Ajoutez les fichiers newpassword.php et emailcrypto.php dans le dossier library avec les contenus suivants :
- /cms/site16
- library
- newpassword.php
- emailcrypto.php
- library
- require_once 'strrand.php';
- function newpassword($len=6) {
- $charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
- return strrand($charset, $len);
- }
newpassword
retourne 6 caractères majuscules ou chiffres aléatoires.
La signature, le mailer et l'adresse de retour de l'email sont des variables globales définies dans le fichier includes/config.inc :
- global $sitename, $webmaster, $mailer;
- $sitename = 'frasq.org';
- $webmaster = 'nobody@frasq.org';
- $mailer = 'Frasq';
- global $signature;
- $signature='frasq.org - http://www.frasq.org';
- require_once 'strtag.php';
- function emailcrypto($text, $tag, $to, $subject, $sender) {
- global $signature, $mailer, $webmaster;
emailcrypto
envoie le message $text
avec en pièce jointe un cryptogramme de $tag
à $to
avec le sujet $subject
de la part de $sender
.
- $img=strtag($tag);
- ob_start();
- imagepng($img);
- imagedestroy($img);
- $imgdata=ob_get_contents();
- ob_end_clean();
- $sep=md5(uniqid('sep'));
- $data=chunk_split(base64_encode($imgdata));
Fabrique un cryptogramme à partir de la chaine de caractères $tag
avec strtag
puis convertit l'image obtenue en PNG. Le PNG est ensuite encodé en base64.
- $headers = <<<_SEP_
- From: $sender
- Return-Path: $webmaster
- Content-Type: multipart/mixed; boundary="$sep"
- X-Mailer: $mailer
- _SEP_;
Génère l'en-tête MIME de l'email avec les champs From:
, Return-Path:
, Content-Type:
et X-Mailer:
avec le paramètre $sender
et les variables globales $webmaster
et $mailer
.
Le champ Content-Type:
indique que le message est constitué de 2 parties, le texte de l'email et le cryptogramme en pièce jointe, séparées par $sep
.
- $body = '';
- if ($text) {
- $body .= <<<_SEP_
- --$sep
- Content-Type: text/plain; charset=utf-8
- $text
- $signature
- _SEP_;
- }
- $body .= <<<_SEP_
- --$sep
- Content-Type: image/png; name="crypto.png"
- Content-Transfer-Encoding: base64
- Content-Disposition: inline; filename="crypto.png"
- $data
- --$sep--
- _SEP_;
- return @mail($to, $subject, $body, $headers);
- }
Fabrique le corps de l'email, d'abord le texte puis l'image en pièce jointe, puis envoie l'email avec la fonction PHP mail
.
Ajoutez une colonne dans la table user
pour mémoriser le nouveau mot de passe :
mysql> ALTER TABLE user ADD newpassword VARCHAR( 32 ) NULL AFTER password;
Éditez le fichier user.inc dans le dossier models/user.inc et ajoutez la fonction user_set_newpassword
:
- function user_set_newpassword($user_id, $password) {
- if (!is_numeric($user_id)) {
- return false;
- }
- $sqlnewpassword=$password ? '\'' . md5($password) . '\'' : NULL;
- $tabuser=db_prefix_table('user');
- $sql="UPDATE $tabuser SET newpassword=$sqlnewpassword WHERE user_id=$user_id LIMIT 1";
- $r = db_update($sql);
- return $r;
- }
Modifiez la fonction user_get
pour retourner le champ user_password
:
- $sql="SELECT name AS user_name, password AS user_password, newpassword AS user_newpassword, mail AS user_mail, UNIX_TIMESTAMP(created) AS user_created, UNIX_TIMESTAMP(access) AS user_access, locale AS user_locale, active AS user_active, banned AS user_banned FROM $tabuser WHERE user_id=$user_id LIMIT 1";
Modifiez la fonction user_login
pour prendre en compte un éventuel nouveau mot de passe :
- if ($user_newpassword) {
- if ($password == $user_newpassword) {
- // use the new one
- $sql="UPDATE $tabuser SET access=FROM_UNIXTIME($now), password='$user_newpassword', newpassword=NULL WHERE user_id=$user_id LIMIT 1";
- }
- else {
- // keep the old one
- $sql="UPDATE $tabuser SET access=FROM_UNIXTIME($now), newpassword=NULL WHERE user_id=$user_id LIMIT 1";
- }
- }
- else {
- // just keep track
- $sql="UPDATE $tabuser SET access=FROM_UNIXTIME($now) WHERE user_id=$user_id LIMIT 1";
- }
- db_update($sql);
Si un nouveau mot de passe a été défini et si le mot de passe donné est le nouveau mot de passe, user_login
remplace l'ancien mot de passe avec le nouveau, sinon, l'ancien est conservé.
Le nouveau mot de passe est toujours effacé.
Dans tous les cas, user_login
note la date et l'heure de la connexion.
Ajoutez un lien sur la page d'envoi d'un nouveau mot de passe dans la vue de la page d'identification :
- <p class="info link">Si vous avez oublié votre mot de passe, <a href="<?php echo $password_page; ?>">cliquez ici</a>.</p>
- <p class="info link">If you have forgotten your password, <a href="<?php echo $password_page; ?>">click here</a>.</p>
Assignez l'URL de la page d'envoi d'un mot de passe à la variable $password_page
des vues dans la fonction login
:
- $password_page=url('password', $lang);
- $output = view('login', $lang, compact('token', 'with_captcha', 'password_page', 'login', 'errors'));
Assurez-vous que l'adresse d'email du compte foobar
est bien foobar@localnet.net.
Entrez http://localhost/cms/site16 dans la barre d'adresse de votre navigateur.
Allez sur la page d'identification. Cliquez sur le lien Si vous avez oublié votre mot de passe.
Entrez foobar
comme nom, cochez la case de confirmation, tapez le code de vérification et appuyez sur Envoyer.
Lisez le courrier de foobar
.
Essayez de vous identifier avec le nouveau mot de passe.
Déconnectez-vous.
Redemandez un nouveau mot de passe. Identifiez-vous avec l'ancien mot de passe qui est toujours valide.
Commentaires