]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Core/Login.php
597bb207e255cc2b19b752634190f785c5b18320
[galette.git] / galette / lib / Galette / Core / Login.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Default authentication class for galette
7 *
8 * PHP version 5
9 *
10 * Copyright © 2007-2020 The Galette Team
11 *
12 * This file is part of Galette (http://galette.tuxfamily.org).
13 *
14 * Galette is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * Galette is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with Galette. If not, see <http://www.gnu.org/licenses/>.
26 *
27 * @category Authentication
28 * @package Galette
29 *
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2007-2020 The Galette Team
32 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
33 * @link http://galette.tuxfamily.org
34 * @since Available since 0.7dev - 2007-07-06
35 */
36
37 namespace Galette\Core;
38
39 use Throwable;
40 use Galette\Repository\Groups;
41 use Galette\Repository\Members;
42 use Galette\Entity\Adherent;
43 use Galette\Entity\Status;
44 use Analog\Analog;
45 use RKA\Session;
46
47 /**
48 * Default authentication class for galette
49 *
50 * @category Authentication
51 * @name Login
52 * @package Galette
53 * @author Johan Cwiklinski <johan@x-tnd.be>
54 * @copyright 2007-2014 The Galette Team
55 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
56 * @link http://galette.tuxfamily.org
57 * @since Available since 0.7dev - 2007-07-06
58 */
59 class Login extends Authentication
60 {
61 public const TABLE = Adherent::TABLE;
62 public const PK = 'login_adh';
63
64 private $zdb;
65 private $i18n;
66 private $impersonated = false;
67
68 /**
69 * Instanciate object
70 *
71 * @param Db $zdb Database instance
72 * @param I18n $i18n I18n instance
73 */
74 public function __construct(Db $zdb, I18n $i18n)
75 {
76 $this->zdb = $zdb;
77 $this->i18n = $i18n;
78 }
79
80 /**
81 * Login for the superuser
82 *
83 * @param string $login name
84 * @param Preferences $preferences Preferences instance
85 *
86 * @return void
87 */
88 public function logAdmin($login, Preferences $preferences)
89 {
90 parent::logAdmin($login, $preferences);
91 $this->impersonated = false;
92 }
93
94 /**
95 * Log out user and unset variables
96 *
97 * @return void
98 */
99 public function logOut()
100 {
101 parent::logOut();
102 $this->impersonated = false;
103 }
104
105 /**
106 * Logs in user.
107 *
108 * @param string $user user's login
109 * @param string $passe user's password
110 *
111 * @return boolean
112 */
113 public function logIn($user, $passe)
114 {
115 try {
116 $select = $this->select();
117 $select->where(
118 [
119 self::PK => $user,
120 'email_adh' => $user
121 ],
122 \Laminas\Db\Sql\Predicate\PredicateSet::OP_OR
123 );
124
125 $results = $this->zdb->execute($select);
126 $log_suffix = '';
127 if (isset($_SERVER['REMOTE_ADDR'])) {
128 $log_suffix .= ' Client ' . $_SERVER['REMOTE_ADDR'];
129 }
130 if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
131 $log_suffix .= ' X-Forwarded-For ' . $_SERVER['HTTP_X_FORWARDED_FOR'];
132 }
133
134 if ($results->count() === 0) {
135 Analog::log(
136 'No entry found for login `' . $user . '`' . $log_suffix,
137 Analog::WARNING
138 );
139 return false;
140 } else {
141 $row = $results->current();
142
143 //check if member is active
144 if (!$row->activite_adh == true) {
145 Analog::log(
146 'Member `' . $user . ' is inactive!`' . $log_suffix,
147 Analog::WARNING
148 );
149 return false;
150 }
151
152 //check if passwords matches
153 $pw_checked = password_verify($passe, $row->mdp_adh);
154 if (!$pw_checked) {
155 //if password did not match, we try old md5 method
156 $pw_checked = (md5($passe) === $row->mdp_adh);
157 }
158
159 if ($pw_checked === false) {
160 //Passwords mismatch. Log and return.
161 Analog::log(
162 'Passwords mismatch for login `' . $user . '`' . $log_suffix,
163 Analog::WARNING
164 );
165 return false;
166 }
167
168 $this->logUser($row);
169 return true;
170 }
171 } catch (Throwable $e) {
172 Analog::log(
173 'An error occurred: ' . $e->getMessage(),
174 Analog::WARNING
175 );
176 Analog::log($e->getTrace(), Analog::ERROR);
177 return false;
178 }
179 }
180
181 /**
182 * Get select query without where clause
183 *
184 * @return \Laminas\Db\Sql\Select
185 */
186 private function select()
187 {
188 $select = $this->zdb->select(self::TABLE, 'a');
189 $select->columns(
190 array(
191 'id_adh',
192 'login_adh',
193 'bool_admin_adh',
194 'nom_adh',
195 'prenom_adh',
196 'mdp_adh',
197 'pref_lang',
198 'activite_adh',
199 'bool_exempt_adh',
200 'date_echeance'
201 )
202 )->join(
203 array('b' => PREFIX_DB . Status::TABLE),
204 'a.' . Status::PK . '=b.' . Status::PK,
205 array('priorite_statut')
206 );
207 return $select;
208 }
209
210 /**
211 * Populate object after successfull login
212 *
213 * @param \ArrayObject $row User information
214 *
215 * @return void
216 */
217 private function logUser(\ArrayObject $row)
218 {
219 Analog::log('User `' . $row->login_adh . '` logged in.', Analog::INFO);
220 $this->id = $row->id_adh;
221 $this->login = $row->login_adh;
222 $this->passe = $row->mdp_adh;
223 $this->admin = $row->bool_admin_adh;
224 $this->name = $row->nom_adh;
225 $this->surname = $row->prenom_adh;
226 $this->lang = $row->pref_lang;
227 $this->i18n->changeLanguage($this->lang);
228 $this->active = $row->activite_adh;
229 $this->logged = true;
230 if ($row->priorite_statut < Members::NON_STAFF_MEMBERS) {
231 $this->staff = true;
232 }
233 //check if member is up to date
234 if ($row->bool_exempt_adh == true) {
235 //member is due free, he's up to date.
236 $this->uptodate = true;
237 } else {
238 //let's check from end date, if present
239 if ($row->date_echeance == null) {
240 $this->uptodate = false;
241 } else {
242 $ech = new \DateTime($row->date_echeance);
243 $now = new \DateTime();
244 $now->setTime(0, 0, 0);
245 $this->uptodate = $ech >= $now;
246 }
247 }
248 //staff members and admins are de facto groups managers. For all
249 //others, get managed groups
250 if (
251 !$this->isSuperAdmin()
252 && !$this->isAdmin()
253 && !$this->isStaff()
254 ) {
255 $this->managed_groups = Groups::loadManagedGroups(
256 $this->id,
257 false
258 );
259 }
260 }
261
262 /**
263 * Impersonate user
264 *
265 * @param int $id Member ID
266 *
267 * @return boolean
268 */
269 public function impersonate($id)
270 {
271 if (!$this->isSuperAdmin()) {
272 throw new \RuntimeException(
273 'Only superadmin can impersonate!'
274 );
275 }
276
277 Analog::log('Impersonating `' . $id . '`...', Analog::INFO);
278 try {
279 $select = $this->select();
280 $select->where(array(Adherent::PK => $id));
281
282 $results = $this->zdb->execute($select);
283 if ($results->count() == 0) {
284 Analog::log(
285 'No entry found for id `' . $id . '`',
286 Analog::WARNING
287 );
288 return false;
289 } else {
290 $this->impersonated = true;
291 $this->superadmin = false;
292 $row = $results->current();
293 $this->logUser($row);
294 return true;
295 }
296 } catch (Throwable $e) {
297 Analog::log(
298 'An error occurred: ' . $e->getMessage(),
299 Analog::WARNING
300 );
301 Analog::log($e->getTraceAsString(), Analog::ERROR);
302 return false;
303 }
304 }
305
306 /**
307 * Does this login already exists ?
308 * These function should be used for setting admin login into Preferences
309 *
310 * @param string $user the username
311 *
312 * @return true if the username already exists, false otherwise
313 */
314 public function loginExists($user)
315 {
316 try {
317 $select = $this->zdb->select(self::TABLE);
318 $select->where(array(self::PK => $user));
319 $results = $this->zdb->execute($select);
320
321 if ($results->count() > 0) {
322 /* We got results, user already exists */
323 return true;
324 } else {
325 /* No results, user does not exists yet :) */
326 return false;
327 }
328 } catch (Throwable $e) {
329 Analog::log(
330 'Cannot check if login exists | ' . $e->getMessage(),
331 Analog::WARNING
332 );
333 /* If an error occurs, we consider that username already exists */
334 return true;
335 }
336 }
337
338 /**
339 * Is impersonated
340 *
341 * @return boolean
342 */
343 public function isImpersonated()
344 {
345 return $this->impersonated;
346 }
347
348 /**
349 * Set id
350 *
351 * @param int $id ID to set
352 *
353 * @return $this
354 */
355 public function setId(int $id): self
356 {
357 $this->id = $id;
358 return $this;
359 }
360 }