]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Core/Password.php
Improve coding standards
[galette.git] / galette / lib / Galette / Core / Password.php
1 <?php
2
3 /**
4 * Copyright © 2003-2024 The Galette Team
5 *
6 * This file is part of Galette (https://galette.eu).
7 *
8 * Galette is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * Galette is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with Galette. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 namespace Galette\Core;
23
24 use Throwable;
25 use Analog\Analog;
26 use Galette\Entity\Adherent;
27
28 /**
29 * Temporary password management
30 *
31 * @author Frédéric Jacquot <gna@logeek.com>
32 * @author Georges Khaznadar (password encryption, images) <georges@unknow.org>
33 * @author Johan Cwiklinski <johan@x-tnd.be>
34 */
35
36 class Password extends AbstractPassword
37 {
38 public const TABLE = 'tmppasswds';
39 public const PK = Adherent::PK;
40
41 /** @var integer Overrides default password size */
42 public const DEFAULT_SIZE = 50;
43 /** @var string Overrides default character set */
44 protected string $chars = 'abcdefghjkmnpqrstuvwxyz0123456789&@{[]}%#+*:ABCDEFGHIJKLMNOPQRSTUVWXYZ';
45
46 private Db $zdb;
47
48 /**
49 * Default constructor
50 *
51 * @param Db $zdb Database instance:
52 * @param boolean $clean Whether we should clean expired passwords in database
53 */
54 public function __construct(Db $zdb, bool $clean = true)
55 {
56 $this->zdb = $zdb;
57 if ($clean === true) {
58 $this->cleanExpired();
59 }
60 }
61
62 /**
63 * Remove all old password entries
64 *
65 * @param int $id_adh Member identifier
66 *
67 * @return boolean
68 */
69 private function removeOldEntries(int $id_adh): bool
70 {
71 try {
72 $delete = $this->zdb->delete(self::TABLE);
73 $delete->where([self::PK => $id_adh]);
74
75 $this->zdb->execute($delete);
76 Analog::log(
77 'Temporary passwords for `' . $id_adh . '` has been removed.',
78 Analog::DEBUG
79 );
80 return true;
81 } catch (Throwable $e) {
82 Analog::log(
83 'An error has occurred removing old tmppasswords ' .
84 $e->getMessage(),
85 Analog::ERROR
86 );
87 return false;
88 }
89 }
90
91 /**
92 * Generates a new password for specified member
93 *
94 * @param int $id_adh Member identifier
95 *
96 * @return boolean
97 */
98 public function generateNewPassword(int $id_adh): bool
99 {
100 //first of all, we'll remove all existant entries for specified id
101 $this->removeOldEntries($id_adh);
102
103 //second, generate a new password and store it in the database
104 $password = $this->makeRandomPassword();
105 $hash = password_hash($password, PASSWORD_BCRYPT);
106
107 try {
108 $values = array(
109 self::PK => $id_adh,
110 'tmp_passwd' => $hash,
111 'date_crea_tmp_passwd' => date('Y-m-d H:i:s')
112 );
113
114 $insert = $this->zdb->insert(self::TABLE);
115 $insert->values($values);
116
117 $this->zdb->execute($insert);
118 Analog::log(
119 'New passwords temporary set for `' . $id_adh . '`.',
120 Analog::DEBUG
121 );
122 $this->setPassword($password);
123 $this->setHash($hash);
124 return true;
125 } catch (Throwable $e) {
126 Analog::log(
127 "An error occurred trying to add temporary password entry. " .
128 $e->getMessage(),
129 Analog::ERROR
130 );
131 return false;
132 }
133 }
134
135 /**
136 * Remove expired passwords queries (older than 24 hours)
137 *
138 * @return boolean
139 */
140 public function cleanExpired(): bool
141 {
142 $date = new \DateTime();
143 $date->sub(new \DateInterval('PT24H'));
144
145 try {
146 $delete = $this->zdb->delete(self::TABLE);
147 $delete->where->lessThan(
148 'date_crea_tmp_passwd',
149 $date->format('Y-m-d H:i:s')
150 );
151 $this->zdb->execute($delete);
152 Analog::log(
153 'Old Temporary passwords have been deleted.',
154 Analog::DEBUG
155 );
156 return true;
157 } catch (Throwable $e) {
158 Analog::log(
159 'An error occurred deleting expired temporary passwords. ' .
160 $e->getMessage(),
161 Analog::WARNING
162 );
163 return false;
164 }
165 }
166
167 /**
168 * Check if requested hash is valid
169 *
170 * @param string $hash the hash
171 *
172 * @return false|int false if hash is not valid, member id otherwise
173 */
174 public function isHashValid(string $hash): false|int
175 {
176 try {
177 $select = $this->zdb->select(self::TABLE);
178 $select->columns(
179 array(self::PK)
180 )->where(array('tmp_passwd' => $hash));
181
182 $results = $this->zdb->execute($select);
183
184 if ($results->count() > 0) {
185 $result = $results->current();
186 $pk = self::PK;
187 return (int)$result->$pk;
188 } else {
189 return false;
190 }
191 } catch (Throwable $e) {
192 Analog::log(
193 'An error occurred getting requested hash. ' . $e->getMessage(),
194 Analog::WARNING
195 );
196 return false;
197 }
198 }
199
200 /**
201 * Remove a hash that has been used (ie. once password has been updated)
202 *
203 * @param string $hash hash
204 *
205 * @return boolean
206 */
207 public function removeHash(string $hash): bool
208 {
209 try {
210 $delete = $this->zdb->delete(self::TABLE);
211 $delete->where(
212 array('tmp_passwd' => $hash)
213 );
214
215 $this->zdb->execute($delete);
216 Analog::log(
217 'Used hash has been successfully remove',
218 Analog::DEBUG
219 );
220 return true;
221 } catch (Throwable $e) {
222 Analog::log(
223 'An error occurred attempting to delete used hash' .
224 $e->getMessage(),
225 Analog::WARNING
226 );
227 return false;
228 }
229 }
230 }