Project import generated by Copybara.
GitOrigin-RevId: 63746295f1a5ab5a619056791995793d65529e62
diff --git a/src/inc/recovery.php b/src/inc/recovery.php
new file mode 100644
index 0000000..1f8307b
--- /dev/null
+++ b/src/inc/recovery.php
@@ -0,0 +1,140 @@
+<?php
+class recovery {
+ const TOKEN_BYTES = 32;
+ const EXPIRATION_TIME = 60*60*24;
+ const WELCOME_EXPIRATION_TIME = 60*60*24*90;
+
+ const EMAIL_TYPE_RECOVERY = 0;
+ const EMAIL_TYPE_WELCOME = 1;
+
+ const EMAIL_NOT_SET = 0;
+
+ public static function getUser($email, $dni) {
+ global $con;
+
+ $semail = db::sanitize($email);
+ $sdni = db::sanitize($dni);
+
+ $query = mysqli_query($con, "SELECT id FROM people WHERE email = '$semail' AND dni = '$sdni' LIMIT 1");
+
+ if (!mysqli_num_rows($query)) return false;
+
+ return mysqli_fetch_assoc($query)["id"];
+ }
+
+ private static function tokenIsUnique($token) {
+ global $con;
+
+ $stoken = db::sanitize($token);
+
+ $query = mysqli_query($con, "SELECT 1 FROM recovery WHERE token = '$stoken' LIMIT 1");
+
+ return !mysqli_num_rows($query);
+ }
+
+ private static function generateRandomToken() {
+ do {
+ $token = bin2hex(random_bytes(self::TOKEN_BYTES));
+ } while (!self::tokenIsUnique($token));
+
+ return $token;
+ }
+
+ private static function invalidateAllRecoveries($id) {
+ global $con;
+
+ $sid = (int)$id;
+
+ return mysqli_query($con, "UPDATE recovery SET used = 1 WHERE user = $sid");
+ }
+
+ public static function sendRecoveryMail($user, $token, $expires, $emailType = self::EMAIL_TYPE_RECOVERY) {
+ global $conf;
+
+ $to = [
+ ["email" => $user["email"]]
+ ];
+
+ $url = security::htmlsafe($conf["fullPath"]."recovery.php?token=".$token);
+
+ switch ($emailType) {
+ case self::EMAIL_TYPE_WELCOME:
+ $subject = "Datos de acceso de ".security::htmlsafe($user["name"]);
+ $body = mail::bodyTemplate("<p>Hola ".security::htmlsafe($user["name"]).",</p>
+ <p>Para poder acceder al aplicativo de control horario, adjuntamos un enlace donde podrás configurar tu contraseña:</p>
+ <ul>
+ <li><a href=\"$url\">$url</a></li>
+ </ul>
+ <p>Una vez establezcas tu contraseña, podrás iniciar sesión con la contraseña que has rellenado y tu usuario: <b><code>".security::htmlsafe($user["username"])."</code></b></p>
+ <p>Reciba un cordial saludo.</p>");
+ break;
+
+ default:
+ $subject = "Recuperar contraseña de ".security::htmlsafe($user["name"]);
+ $body = mail::bodyTemplate("<p>Hola ".security::htmlsafe($user["name"]).",</p>
+ <p>Alguien (seguramente tú) ha rellenado el formulario de recuperación de contraseñas para el aplicativo de registro horario.</p>
+ <p>Si no has sido tú, puede que alguien esté intentando entrar en tu zona de trabajador del aplicativo, y te agredeceríamos que lo comunicaras en la mayor brevedad posible a recursos humanos.</p>
+ <p>Si has sido tú, puedes restablecer tu contraseña haciendo clic en el siguiente enlace:</p>
+ <ul>
+ <li><a href=\"$url\">$url</a></li>
+ </ul>
+ <p>Además, aprovechamos para recordarte que tu usuario en el web es <b><code>".security::htmlsafe($user["username"])."</code></b></p>
+ <p>Reciba un cordial saludo.</p>");
+ }
+
+ return mail::send($to, [], $subject, $body);
+ }
+
+ public static function recover($id, $emailType = self::EMAIL_TYPE_RECOVERY) {
+ global $con;
+
+ $user = people::get($id);
+ if ($user === false) return false;
+ if (!isset($user["email"]) || empty($user["email"])) return self::EMAIL_NOT_SET;
+
+ $token = self::generateRandomToken();
+ $stoken = db::sanitize($token);
+ $sid = (int)$id;
+ $stime = (int)time();
+ $sexpires = (int)($stime + ($emailType === self::EMAIL_TYPE_WELCOME ? self::WELCOME_EXPIRATION_TIME : self::EXPIRATION_TIME));
+
+ if (!self::invalidateAllRecoveries($id)) return false;
+
+ if (!mysqli_query($con, "INSERT INTO recovery (user, token, timecreated, expires) VALUES ($sid, '$stoken', $stime, $sexpires)")) return false;
+
+ return self::sendRecoveryMail($user, $token, $sexpires, $emailType);
+ }
+
+ public static function getRecovery($token) {
+ global $con;
+
+ $stoken = db::sanitize($token);
+
+ $query = mysqli_query($con, "SELECT * FROM recovery WHERE token = '$stoken' LIMIT 1");
+
+ if (!mysqli_num_rows($query)) return false;
+
+ return mysqli_fetch_assoc($query);
+ }
+
+ public static function getUnusedRecovery($token) {
+ $recovery = self::getRecovery($token);
+ if ($recovery === false || $recovery["used"] != 0 || $recovery["expires"] < time()) return false;
+
+ return $recovery;
+ }
+
+ public static function finishRecovery($token, $password) {
+ global $con;
+
+ $stoken = db::sanitize($token);
+ $spassword = db::sanitize(password_hash($password, PASSWORD_DEFAULT));
+
+ $recovery = recovery::getUnusedRecovery($token);
+ if ($recovery === false) return false;
+
+ if (!self::invalidateAllRecoveries($recovery["user"])) return false;
+
+ return people::updatePassword($recovery["user"], $spassword);
+ }
+}