blob: a4057bc64a3948475ea09b06ca2369b0bca2c644 [file] [log] [blame]
Copybara botbe50d492023-11-30 00:16:42 +01001<?php
Adrià Vilanova Martínez5af86512023-12-02 20:44:16 +01002/*
3 * hores
4 * Copyright (c) 2023 Adrià Vilanova Martínez
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public
17 * License along with this program.
18 * If not, see http://www.gnu.org/licenses/.
19 */
20
Copybara botbe50d492023-11-30 00:16:42 +010021class recovery {
22 const TOKEN_BYTES = 32;
23 const EXPIRATION_TIME = 60*60*24;
24 const WELCOME_EXPIRATION_TIME = 60*60*24*90;
25
26 const EMAIL_TYPE_RECOVERY = 0;
27 const EMAIL_TYPE_WELCOME = 1;
28
29 const EMAIL_NOT_SET = 0;
30
31 public static function getUser($email, $dni) {
32 global $con;
33
34 $semail = db::sanitize($email);
35 $sdni = db::sanitize($dni);
36
37 $query = mysqli_query($con, "SELECT id FROM people WHERE email = '$semail' AND dni = '$sdni' LIMIT 1");
38
39 if (!mysqli_num_rows($query)) return false;
40
41 return mysqli_fetch_assoc($query)["id"];
42 }
43
44 private static function tokenIsUnique($token) {
45 global $con;
46
47 $stoken = db::sanitize($token);
48
49 $query = mysqli_query($con, "SELECT 1 FROM recovery WHERE token = '$stoken' LIMIT 1");
50
51 return !mysqli_num_rows($query);
52 }
53
54 private static function generateRandomToken() {
55 do {
56 $token = bin2hex(random_bytes(self::TOKEN_BYTES));
57 } while (!self::tokenIsUnique($token));
58
59 return $token;
60 }
61
62 private static function invalidateAllRecoveries($id) {
63 global $con;
64
65 $sid = (int)$id;
66
67 return mysqli_query($con, "UPDATE recovery SET used = 1 WHERE user = $sid");
68 }
69
70 public static function sendRecoveryMail($user, $token, $expires, $emailType = self::EMAIL_TYPE_RECOVERY) {
71 global $conf;
72
73 $to = [
74 ["email" => $user["email"]]
75 ];
76
77 $url = security::htmlsafe($conf["fullPath"]."recovery.php?token=".$token);
78
79 switch ($emailType) {
80 case self::EMAIL_TYPE_WELCOME:
81 $subject = "Datos de acceso de ".security::htmlsafe($user["name"]);
82 $body = mail::bodyTemplate("<p>Hola ".security::htmlsafe($user["name"]).",</p>
83 <p>Para poder acceder al aplicativo de control horario, adjuntamos un enlace donde podrás configurar tu contraseña:</p>
84 <ul>
85 <li><a href=\"$url\">$url</a></li>
86 </ul>
87 <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>
88 <p>Reciba un cordial saludo.</p>");
89 break;
90
91 default:
92 $subject = "Recuperar contraseña de ".security::htmlsafe($user["name"]);
93 $body = mail::bodyTemplate("<p>Hola ".security::htmlsafe($user["name"]).",</p>
94 <p>Alguien (seguramente tú) ha rellenado el formulario de recuperación de contraseñas para el aplicativo de registro horario.</p>
95 <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>
96 <p>Si has sido tú, puedes restablecer tu contraseña haciendo clic en el siguiente enlace:</p>
97 <ul>
98 <li><a href=\"$url\">$url</a></li>
99 </ul>
100 <p>Además, aprovechamos para recordarte que tu usuario en el web es <b><code>".security::htmlsafe($user["username"])."</code></b></p>
101 <p>Reciba un cordial saludo.</p>");
102 }
103
104 return mail::send($to, [], $subject, $body);
105 }
106
107 public static function recover($id, $emailType = self::EMAIL_TYPE_RECOVERY) {
108 global $con;
109
110 $user = people::get($id);
111 if ($user === false) return false;
112 if (!isset($user["email"]) || empty($user["email"])) return self::EMAIL_NOT_SET;
113
114 $token = self::generateRandomToken();
115 $stoken = db::sanitize($token);
116 $sid = (int)$id;
117 $stime = (int)time();
118 $sexpires = (int)($stime + ($emailType === self::EMAIL_TYPE_WELCOME ? self::WELCOME_EXPIRATION_TIME : self::EXPIRATION_TIME));
119
120 if (!self::invalidateAllRecoveries($id)) return false;
121
122 if (!mysqli_query($con, "INSERT INTO recovery (user, token, timecreated, expires) VALUES ($sid, '$stoken', $stime, $sexpires)")) return false;
123
124 return self::sendRecoveryMail($user, $token, $sexpires, $emailType);
125 }
126
127 public static function getRecovery($token) {
128 global $con;
129
130 $stoken = db::sanitize($token);
131
132 $query = mysqli_query($con, "SELECT * FROM recovery WHERE token = '$stoken' LIMIT 1");
133
134 if (!mysqli_num_rows($query)) return false;
135
136 return mysqli_fetch_assoc($query);
137 }
138
139 public static function getUnusedRecovery($token) {
140 $recovery = self::getRecovery($token);
141 if ($recovery === false || $recovery["used"] != 0 || $recovery["expires"] < time()) return false;
142
143 return $recovery;
144 }
145
146 public static function finishRecovery($token, $password) {
147 global $con;
148
149 $stoken = db::sanitize($token);
150 $spassword = db::sanitize(password_hash($password, PASSWORD_DEFAULT));
151
152 $recovery = recovery::getUnusedRecovery($token);
153 if ($recovery === false) return false;
154
155 if (!self::invalidateAllRecoveries($recovery["user"])) return false;
156
157 return people::updatePassword($recovery["user"], $spassword);
158 }
159}