Copybara bot | be50d49 | 2023-11-30 00:16:42 +0100 | [diff] [blame] | 1 | <?php |
Adrià Vilanova Martínez | 5af8651 | 2023-12-02 20:44:16 +0100 | [diff] [blame^] | 2 | /* |
| 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 bot | be50d49 | 2023-11-30 00:16:42 +0100 | [diff] [blame] | 21 | class 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 | } |