1.21.04 sécurisation de la méthode de changement de mot de passe.

This commit is contained in:
Fred Tempez 2025-01-31 18:13:00 +01:00
parent e7e0fef37e
commit cbc7a1ad19
3 changed files with 44 additions and 52 deletions

View File

@ -1,4 +1,4 @@
# ZwiiCampus 1.20.03 # ZwiiCampus 1.21.00
ZwiiCampus (Learning Management System) est logiciel auteur destiné à mettre en ligne des tutoriels. Il dispose de plusieurs modalités d'ouverture et d'accès des contenus. Basé sur la version 13 du CMS Zwii, la structure logicielle est solide, le framework de Zwii est éprouvé. ZwiiCampus (Learning Management System) est logiciel auteur destiné à mettre en ligne des tutoriels. Il dispose de plusieurs modalités d'ouverture et d'accès des contenus. Basé sur la version 13 du CMS Zwii, la structure logicielle est solide, le framework de Zwii est éprouvé.

View File

@ -51,7 +51,7 @@ class common
const ACCESS_TIMER = 1800; const ACCESS_TIMER = 1800;
// Numéro de version // Numéro de version
const ZWII_VERSION = '1.20.03'; const ZWII_VERSION = '1.21.00';
// URL autoupdate // URL autoupdate
const ZWII_UPDATE_URL = 'https://forge.chapril.org/ZwiiCMS-Team/campus-update/raw/branch/master/'; const ZWII_UPDATE_URL = 'https://forge.chapril.org/ZwiiCMS-Team/campus-update/raw/branch/master/';

View File

@ -151,9 +151,9 @@ class user extends common
$userMail, $userMail,
'Compte créé sur ' . $this->getData(['config', 'title']), 'Compte créé sur ' . $this->getData(['config', 'title']),
'Bonjour <strong>' . $userFirstname . ' ' . $userLastname . '</strong>,<br><br>' . 'Bonjour <strong>' . $userFirstname . ' ' . $userLastname . '</strong>,<br><br>' .
'Un administrateur vous a créé un compte sur le site ' . $this->getData(['config', 'title']) . '. Vous trouverez ci-dessous les détails de votre compte.<br><br>' . 'Un administrateur vous a créé un compte sur le site ' . $this->getData(['config', 'title']) . '. Vous trouverez ci-dessous les détails de votre compte.<br><br>' .
'<strong>Identifiant du compte :</strong> ' . $this->getInput('userAddId') . '<br>' . '<strong>Identifiant du compte :</strong> ' . $this->getInput('userAddId') . '<br>' .
'<small>Nous ne conservons pas les mots de passe, en conséquence nous vous conseillons de conserver ce message tant que vous ne vous êtes pas connecté. Vous pourrez modifier votre mot de passe après votre première connexion.</small>', '<small>Nous ne conservons pas les mots de passe, en conséquence nous vous conseillons de conserver ce message tant que vous ne vous êtes pas connecté. Vous pourrez modifier votre mot de passe après votre première connexion.</small>',
null, null,
$this->getData(['config', 'smtp', 'from']) $this->getData(['config', 'smtp', 'from'])
); );
@ -284,7 +284,6 @@ class user extends common
'notification' => sprintf($count > 1 ? $notification . 's' : $notification, $count), 'notification' => sprintf($count > 1 ? $notification . 's' : $notification, $count),
'state' => $success 'state' => $success
]); ]);
} }
// Liste des groupes et des profils // Liste des groupes et des profils
@ -367,7 +366,6 @@ class user extends common
$this->getData(['user', $userId, 'lastname']), $this->getData(['user', $userId, 'lastname']),
$this->getData(['user', $userId, 'tags']), $this->getData(['user', $userId, 'tags']),
]; ];
} }
} }
@ -409,7 +407,7 @@ class user extends common
$this->getData(['user', $this->getUrl(2)]) === null $this->getData(['user', $this->getUrl(2)]) === null
// Droit d'édition // Droit d'édition
and ( and (
// Impossible de s'auto-éditer // Impossible de s'auto-éditer
($this->getUser('id') === $this->getUrl(2) ($this->getUser('id') === $this->getUrl(2)
and $this->getUrl('group') <= self::GROUP_VISITOR and $this->getUrl('group') <= self::GROUP_VISITOR
) )
@ -571,28 +569,29 @@ class user extends common
public function forgot() public function forgot()
{ {
// Soumission du formulaire // Soumission du formulaire
if ( if ($this->isPost()) {
$this->isPost()
) {
$userId = $this->getInput('userForgotId', helper::FILTER_ID, true); $userId = $this->getInput('userForgotId', helper::FILTER_ID, true);
$sent = false; $sent = false;
if ($this->getData(['user', $userId])) { if ($this->getData(['user', $userId])) {
// Enregistre la date de la demande dans le compte utilisateur // Génère une clé unique avec timestamp et partie aléatoire
$this->setData(['user', $userId, 'forgot', time()]); $timestamp = time(); // Timestamp actuel
// Crée un id unique pour la réinitialisation $randomPart = bin2hex(random_bytes(8)); // Partie aléatoire (16 caractères hexadécimaux)
$uniqId = md5(json_encode($this->getData(['user', $userId, 'forgot']))); $uniqId = $timestamp . '_' . $randomPart; // Combine les deux
// Enregistre la clé unique dans le compte utilisateur
$this->setData(['user', $userId, 'forgot', $uniqId]);
// Envoi le mail // Envoi le mail
$sent = $this->sendMail( $sent = $this->sendMail(
$this->getData(['user', $userId, 'mail']), $this->getData(['user', $userId, 'mail']),
'Réinitialisation de votre mot de passe', 'Réinitialisation de votre mot de passe',
'Bonjour <strong>' . $this->getData(['user', $userId, 'firstname']) . ' ' . $this->getData(['user', $userId, 'lastname']) . '</strong>,<br><br>' . 'Bonjour <strong>' . $this->getData(['user', $userId, 'firstname']) . ' ' . $this->getData(['user', $userId, 'lastname']) . '</strong>,<br><br>' .
'Vous avez demandé à changer le mot de passe lié à votre compte. Vous trouverez ci-dessous un lien vous permettant de modifier celui-ci.<br><br>' . 'Vous avez demandé à changer le mot de passe lié à votre compte. Vous trouverez ci-dessous un lien vous permettant de modifier celui-ci.<br><br>' .
'<a href="' . helper::baseUrl() . 'user/reset/' . $userId . '/' . $uniqId . '" target="_blank">' . helper::baseUrl() . 'user/reset/' . $userId . '/' . $uniqId . '</a><br><br>' . '<a href="' . helper::baseUrl() . 'user/reset/' . $userId . '/' . $uniqId . '" target="_blank">' . helper::baseUrl() . 'user/reset/' . $userId . '/' . $uniqId . '</a><br><br>' .
'<small>Si nous n\'avez pas demandé à réinitialiser votre mot de passe, veuillez ignorer ce mail.</small>', '<small>Si nous n\'avez pas demandé à réinitialiser votre mot de passe, veuillez ignorer ce mail.</small>',
null, null,
$this->getData(['config', 'smtp', 'from']) $this->getData(['config', 'smtp', 'from'])
); );
} }
// Valeurs en sortie // Valeurs en sortie
@ -683,7 +682,7 @@ class user extends common
// Formatage de la liste // Formatage de la liste
self::$users[] = [ self::$users[] = [
//$userId, //$userId,
sprintf('%s %s',$userLastNames, $this->getData(['user', $userId, 'firstname'])), sprintf('%s %s', $userLastNames, $this->getData(['user', $userId, 'firstname'])),
helper::translate(self::$groups[(int) $this->getData(['user', $userId, 'group'])]), helper::translate(self::$groups[(int) $this->getData(['user', $userId, 'group'])]),
empty($this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name'])) empty($this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name']))
? helper::translate(self::$groups[(int) $this->getData(['user', $userId, 'group'])]) ? helper::translate(self::$groups[(int) $this->getData(['user', $userId, 'group'])])
@ -706,7 +705,6 @@ class user extends common
'help' => 'Supprimer' 'help' => 'Supprimer'
]) ])
]; ];
} }
} }
@ -1353,7 +1351,7 @@ class user extends common
$this->getData(['user', $userId, 'mail']), $this->getData(['user', $userId, 'mail']),
'Validation de la connexion à votre compte', 'Validation de la connexion à votre compte',
'<p>Clé de validation à saisir dans le formulaire de connexion :</p>' . '<p>Clé de validation à saisir dans le formulaire de connexion :</p>' .
'<h1><center>' . $keyByMail . '</center></h1>', '<h1><center>' . $keyByMail . '</center></h1>',
null, null,
$this->getData(['config', 'smtp', 'from']) $this->getData(['config', 'smtp', 'from'])
); );
@ -1475,7 +1473,7 @@ class user extends common
$inputKey = $this->getInput('userAuthKey', helper::FILTER_INT); $inputKey = $this->getInput('userAuthKey', helper::FILTER_INT);
// Redirection // Redirection
$pageId = $this->getUrl(2); $pageId = $this->getUrl(2);
$redirect = $pageId? helper::baseUrl() . $pageId : helper::baseUrl() ; $redirect = $pageId ? helper::baseUrl() . $pageId : helper::baseUrl();
if ( if (
// La clé est valide ou le message n'ayant pas été expédié, la double authentification est désactivée // La clé est valide ou le message n'ayant pas été expédié, la double authentification est désactivée
$targetKey === $inputKey || $this->getData(['config', 'connect', 'mailAuth', 0]) === 0 $targetKey === $inputKey || $this->getData(['config', 'connect', 'mailAuth', 0]) === 0
@ -1556,44 +1554,44 @@ class user extends common
if ( if (
// L'utilisateur n'existe pas // L'utilisateur n'existe pas
$this->getData(['user', $this->getUrl(2)]) === null $this->getData(['user', $this->getUrl(2)]) === null
// Lien de réinitialisation trop vieux // Lien de réinitialisation trop vieux (24 heures)
or $this->getData(['user', $this->getUrl(2), 'forgot']) + 86400 < time() or $this->getData(['user', $this->getUrl(2), 'forgot']) === null
// Id unique incorrecte or (int) explode('_', $this->getData(['user', $this->getUrl(2), 'forgot']))[0] + 86400 < time()
or $this->getUrl(3) !== md5(json_encode($this->getData(['user', $this->getUrl(2), 'forgot']))) // Clé unique incorrecte
or $this->getUrl(3) !== $this->getData(['user', $this->getUrl(2), 'forgot'])
) { ) {
$this->saveLog( $this->saveLog(
' Erreur de réinitialisation de mot de passe ' . $this->getUrl(2) . ' Erreur de réinitialisation de mot de passe ' . $this->getUrl(2) .
' Compte : ' . $this->getData(['user', $this->getUrl(2)]) . ' Compte : ' . $this->getData(['user', $this->getUrl(2)]) .
' Temps : ' . ($this->getData(['user', $this->getUrl(2), 'forgot']) + 86400 < time()) . ' Temps : ' . ($this->getData(['user', $this->getUrl(2), 'forgot']) === null ? 'Clé manquante' : ((int) explode('_', $this->getData(['user', $this->getUrl(2), 'forgot']))[0] + 86400 < time() ? 'Temps dépassé' : 'Temps valide')) .
' Clé : ' . ($this->getUrl(3) !== md5(json_encode($this->getData(['user', $this->getUrl(2), 'forgot'])))) ' Clé : ' . ($this->getUrl(3) !== $this->getData(['user', $this->getUrl(2), 'forgot']) ? 'Clé invalide' : 'Clé valide')
); );
// Message d'erreur en cas de problème de réinitialisation de mot de passe // Message d'erreur en cas de problème de réinitialisation de mot de passe
$message = $this->getData(['user', $this->getUrl(2)]) === null $message = $this->getData(['user', $this->getUrl(2)]) === null
? ' Utilisateur inconnu ' ? ' Utilisateur inconnu '
: ''; : '';
$message = $this->getData(['user', $this->getUrl(2), 'forgot']) + 86400 < time() $message = $this->getData(['user', $this->getUrl(2), 'forgot']) === null
? ' Clé manquante '
: $message;
$message = (int) explode('_', $this->getData(['user', $this->getUrl(2), 'forgot']))[0] + 86400 < time()
? ' Temps dépassé ' ? ' Temps dépassé '
: $message; : $message;
$message = $this->getUrl(3) !== md5(json_encode($this->getData(['user', $this->getUrl(2)]))) $message = $this->getUrl(3) !== $this->getData(['user', $this->getUrl(2), 'forgot'])
? ' Clé invalide ' ? ' Clé invalide '
: $message; : $message;
// Valeurs en sortie // Valeurs en sortie
$this->addOutput([ $this->addOutput([
'redirect' => helper::baseurl(), 'redirect' => helper::baseurl(),
'notification' => helper::translate('Impossible de réinitialiser le mot de passe de ce compte !') . $message, 'notification' => helper::translate('Impossible de réinitialiser le mot de passe de ce compte !') . $message,
'state' => false 'state' => false
//'access' => false
]); ]);
} }
// Accès autorisé // Accès autorisé
else { else {
// Soumission du formulaire // Soumission du formulaire
if ( if ($this->isPost()) {
// Tous les users peuvent réinitialiser
// $this->getUser('permission', __CLASS__, __FUNCTION__) === true &&
$this->isPost()
) {
// Double vérification pour le mot de passe // Double vérification pour le mot de passe
if ($this->getInput('userResetNewPassword')) { if ($this->getInput('userResetNewPassword')) {
// La confirmation ne correspond pas au mot de passe // La confirmation ne correspond pas au mot de passe
@ -1692,8 +1690,8 @@ class user extends common
$item['prenom'], $item['prenom'],
self::$groups[$item['groupe']], self::$groups[$item['groupe']],
empty($this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name'])) empty($this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name']))
? helper::translate(self::$groups[(int) $this->getData(['user', $userId, 'group'])]) ? helper::translate(self::$groups[(int) $this->getData(['user', $userId, 'group'])])
: $this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name']), : $this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name']),
$item['prenom'], $item['prenom'],
helper::filter($item['email'], helper::FILTER_MAIL), helper::filter($item['email'], helper::FILTER_MAIL),
$item['tags'], $item['tags'],
@ -1737,9 +1735,9 @@ class user extends common
$item['email'], $item['email'],
'Compte créé sur ' . $this->getData(['config', 'title']), 'Compte créé sur ' . $this->getData(['config', 'title']),
'Bonjour <strong>' . $item['prenom'] . ' ' . $item['nom'] . '</strong>,<br><br>' . 'Bonjour <strong>' . $item['prenom'] . ' ' . $item['nom'] . '</strong>,<br><br>' .
'Un administrateur vous a créé un compte sur le site ' . $this->getData(['config', 'title']) . '. Vous trouverez ci-dessous les détails de votre compte.<br><br>' . 'Un administrateur vous a créé un compte sur le site ' . $this->getData(['config', 'title']) . '. Vous trouverez ci-dessous les détails de votre compte.<br><br>' .
'<strong>Identifiant du compte :</strong> ' . $userId . '<br>' . '<strong>Identifiant du compte :</strong> ' . $userId . '<br>' .
'<small>Un mot de passe provisoire vous été attribué, à la première connexion cliquez sur Mot de passe Oublié.</small>', '<small>Un mot de passe provisoire vous été attribué, à la première connexion cliquez sur Mot de passe Oublié.</small>',
null, null,
$this->getData(['config', 'smtp', 'from']) $this->getData(['config', 'smtp', 'from'])
); );
@ -1755,8 +1753,8 @@ class user extends common
$item['prenom'], $item['prenom'],
self::$groups[$item['groupe']], self::$groups[$item['groupe']],
empty($this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name'])) empty($this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name']))
? helper::translate(self::$groups[(int) $this->getData(['user', $userId, 'group'])]) ? helper::translate(self::$groups[(int) $this->getData(['user', $userId, 'group'])])
: $this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name']), : $this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name']),
$item['prenom'], $item['prenom'],
$item['email'], $item['email'],
$item['tags'], $item['tags'],
@ -1764,7 +1762,6 @@ class user extends common
]; ];
} }
} }
} }
// Sauvegarde la base manuellement // Sauvegarde la base manuellement
$this->saveDB('user'); $this->saveDB('user');
@ -1806,7 +1803,6 @@ class user extends common
readfile($path . $file); readfile($path . $file);
exit(); exit();
} }
} }
public function tag() public function tag()
@ -1849,7 +1845,6 @@ class user extends common
'notification' => sprintf($count > 1 ? $notification . 's' : $notification, $count), 'notification' => sprintf($count > 1 ? $notification . 's' : $notification, $count),
'state' => $success 'state' => $success
]); ]);
} }
@ -1933,7 +1928,6 @@ class user extends common
$this->getData(['user', $userId, 'lastname']), $this->getData(['user', $userId, 'lastname']),
$this->getData(['user', $userId, 'tags']), $this->getData(['user', $userId, 'tags']),
]; ];
} }
} }
@ -1954,7 +1948,6 @@ class user extends common
'datatables' 'datatables'
] ]
]); ]);
} }
/** /**
@ -1988,5 +1981,4 @@ class user extends common
closedir($dh); closedir($dh);
return $subdirs; return $subdirs;
} }
}
}