blob: 1f8307bde9a9816c42f123c834ca8bcb12cfbbb2 [file] [log] [blame]
Copybara botbe50d492023-11-30 00:16:42 +01001<?php
2class recovery {
3 const TOKEN_BYTES = 32;
4 const EXPIRATION_TIME = 60*60*24;
5 const WELCOME_EXPIRATION_TIME = 60*60*24*90;
6
7 const EMAIL_TYPE_RECOVERY = 0;
8 const EMAIL_TYPE_WELCOME = 1;
9
10 const EMAIL_NOT_SET = 0;
11
12 public static function getUser($email, $dni) {
13 global $con;
14
15 $semail = db::sanitize($email);
16 $sdni = db::sanitize($dni);
17
18 $query = mysqli_query($con, "SELECT id FROM people WHERE email = '$semail' AND dni = '$sdni' LIMIT 1");
19
20 if (!mysqli_num_rows($query)) return false;
21
22 return mysqli_fetch_assoc($query)["id"];
23 }
24
25 private static function tokenIsUnique($token) {
26 global $con;
27
28 $stoken = db::sanitize($token);
29
30 $query = mysqli_query($con, "SELECT 1 FROM recovery WHERE token = '$stoken' LIMIT 1");
31
32 return !mysqli_num_rows($query);
33 }
34
35 private static function generateRandomToken() {
36 do {
37 $token = bin2hex(random_bytes(self::TOKEN_BYTES));
38 } while (!self::tokenIsUnique($token));
39
40 return $token;
41 }
42
43 private static function invalidateAllRecoveries($id) {
44 global $con;
45
46 $sid = (int)$id;
47
48 return mysqli_query($con, "UPDATE recovery SET used = 1 WHERE user = $sid");
49 }
50
51 public static function sendRecoveryMail($user, $token, $expires, $emailType = self::EMAIL_TYPE_RECOVERY) {
52 global $conf;
53
54 $to = [
55 ["email" => $user["email"]]
56 ];
57
58 $url = security::htmlsafe($conf["fullPath"]."recovery.php?token=".$token);
59
60 switch ($emailType) {
61 case self::EMAIL_TYPE_WELCOME:
62 $subject = "Datos de acceso de ".security::htmlsafe($user["name"]);
63 $body = mail::bodyTemplate("<p>Hola ".security::htmlsafe($user["name"]).",</p>
64 <p>Para poder acceder al aplicativo de control horario, adjuntamos un enlace donde podrás configurar tu contraseña:</p>
65 <ul>
66 <li><a href=\"$url\">$url</a></li>
67 </ul>
68 <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>
69 <p>Reciba un cordial saludo.</p>");
70 break;
71
72 default:
73 $subject = "Recuperar contraseña de ".security::htmlsafe($user["name"]);
74 $body = mail::bodyTemplate("<p>Hola ".security::htmlsafe($user["name"]).",</p>
75 <p>Alguien (seguramente tú) ha rellenado el formulario de recuperación de contraseñas para el aplicativo de registro horario.</p>
76 <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>
77 <p>Si has sido tú, puedes restablecer tu contraseña haciendo clic en el siguiente enlace:</p>
78 <ul>
79 <li><a href=\"$url\">$url</a></li>
80 </ul>
81 <p>Además, aprovechamos para recordarte que tu usuario en el web es <b><code>".security::htmlsafe($user["username"])."</code></b></p>
82 <p>Reciba un cordial saludo.</p>");
83 }
84
85 return mail::send($to, [], $subject, $body);
86 }
87
88 public static function recover($id, $emailType = self::EMAIL_TYPE_RECOVERY) {
89 global $con;
90
91 $user = people::get($id);
92 if ($user === false) return false;
93 if (!isset($user["email"]) || empty($user["email"])) return self::EMAIL_NOT_SET;
94
95 $token = self::generateRandomToken();
96 $stoken = db::sanitize($token);
97 $sid = (int)$id;
98 $stime = (int)time();
99 $sexpires = (int)($stime + ($emailType === self::EMAIL_TYPE_WELCOME ? self::WELCOME_EXPIRATION_TIME : self::EXPIRATION_TIME));
100
101 if (!self::invalidateAllRecoveries($id)) return false;
102
103 if (!mysqli_query($con, "INSERT INTO recovery (user, token, timecreated, expires) VALUES ($sid, '$stoken', $stime, $sexpires)")) return false;
104
105 return self::sendRecoveryMail($user, $token, $sexpires, $emailType);
106 }
107
108 public static function getRecovery($token) {
109 global $con;
110
111 $stoken = db::sanitize($token);
112
113 $query = mysqli_query($con, "SELECT * FROM recovery WHERE token = '$stoken' LIMIT 1");
114
115 if (!mysqli_num_rows($query)) return false;
116
117 return mysqli_fetch_assoc($query);
118 }
119
120 public static function getUnusedRecovery($token) {
121 $recovery = self::getRecovery($token);
122 if ($recovery === false || $recovery["used"] != 0 || $recovery["expires"] < time()) return false;
123
124 return $recovery;
125 }
126
127 public static function finishRecovery($token, $password) {
128 global $con;
129
130 $stoken = db::sanitize($token);
131 $spassword = db::sanitize(password_hash($password, PASSWORD_DEFAULT));
132
133 $recovery = recovery::getUnusedRecovery($token);
134 if ($recovery === false) return false;
135
136 if (!self::invalidateAllRecoveries($recovery["user"])) return false;
137
138 return people::updatePassword($recovery["user"], $spassword);
139 }
140}