Commit | Line | Data |
---|---|---|
395539d7 AE |
1 | <?php |
2 | ||
3 | namespace SimpleSAML\Module\fsfdrupalauth\Auth\Source; | |
4 | ||
cf44092c AE |
5 | use Exception; |
6 | use PDO; | |
7 | use PDOException; | |
8 | use SimpleSAML\Error; | |
9 | use SimpleSAML\Logger; | |
10 | ||
395539d7 AE |
11 | /** |
12 | * Extension of simple SQL authentication source | |
13 | * | |
14 | * @package SimpleSAMLphp | |
15 | */ | |
16 | ||
17 | class FSFDrupalAuth extends \SimpleSAML\Module\core\Auth\UserPassBase | |
18 | { | |
19 | /** | |
20 | * The DSN we should connect to. | |
21 | */ | |
22 | private $dsn; | |
23 | ||
24 | /** | |
25 | * The username we should connect to the database with. | |
26 | */ | |
27 | private $username; | |
28 | ||
29 | /** | |
30 | * The password we should connect to the database with. | |
31 | */ | |
32 | private $password; | |
33 | ||
34 | /** | |
35 | * The options that we should connect to the database with. | |
36 | */ | |
37 | private $options; | |
38 | ||
39 | /** | |
40 | * The query we should use to retrieve the attributes for the user. | |
41 | * | |
42 | * The username and password will be available as :username and :password. | |
43 | */ | |
44 | private $query_main; | |
45 | private $query_membership; | |
46 | private $query_staff; | |
794d92ca | 47 | |
78e03f98 | 48 | private $query_nomination_process_donations; |
29854532 | 49 | private $query_nomination_process_gift_receipt; |
3fa64def | 50 | private $query_nomination_process_adhoc; |
395539d7 | 51 | |
1ae1ff15 | 52 | private $query_discussion_process_old_membership; |
794d92ca | 53 | private $query_discussion_process_donations; |
794d92ca AE |
54 | private $query_discussion_process_adhoc; |
55 | ||
c0d116a9 | 56 | /** |
e12acfe1 AE |
57 | * SQL query parameters, or variables that help determine which attributes |
58 | * someone has | |
c0d116a9 | 59 | */ |
e12acfe1 | 60 | private $fsf_org_id; |
e9db6ecd AE |
61 | private $gift_redeem_page_id; |
62 | ||
2d61361e | 63 | private $nomination_process_active; |
78e03f98 AE |
64 | private $nomination_process_contrib_start_date; |
65 | private $nomination_process_contrib_end_date; | |
e9db6ecd | 66 | private $nomination_process_adhoc_access_group_id; |
f38130e1 AE |
67 | private $membership_monthly_rate; |
68 | private $student_membership_monthly_rate; | |
c0d116a9 | 69 | |
794d92ca AE |
70 | private $discussion_process_active; |
71 | private $discussion_process_contrib_start_date; | |
72 | private $discussion_process_contrib_end_date; | |
73 | private $discussion_process_adhoc_access_group_id; | |
74 | private $discussion_process_adhoc_no_access_group_id; | |
75 | ||
395539d7 AE |
76 | /** |
77 | * Constructor for this authentication source. | |
78 | * | |
79 | * @param array $info Information about this authentication source. | |
80 | * @param array $config Configuration. | |
81 | */ | |
82 | public function __construct($info, $config) | |
83 | { | |
84 | assert(is_array($info)); | |
85 | assert(is_array($config)); | |
86 | ||
87 | // Call the parent constructor first, as required by the interface | |
88 | parent::__construct($info, $config); | |
89 | ||
90 | // Make sure that all required parameters are present. | |
e9db6ecd AE |
91 | foreach (['dsn', |
92 | 'username', | |
93 | 'password', | |
94 | ||
95 | 'query_main', | |
96 | 'query_membership', | |
97 | 'query_staff', | |
98 | ||
e12acfe1 | 99 | 'query_nomination_process_donations', |
3fa64def | 100 | 'query_nomination_process_gift_receipt', |
e9db6ecd AE |
101 | 'query_nomination_process_adhoc', |
102 | ||
103 | 'fsf_org_id', | |
104 | 'gift_redeem_page_id', | |
105 | ||
2d61361e | 106 | 'nomination_process_active', |
e12acfe1 | 107 | 'nomination_process_contrib_start_date', |
e9db6ecd AE |
108 | 'nomination_process_contrib_end_date', |
109 | 'nomination_process_adhoc_access_group_id', | |
110 | 'membership_monthly_rate', | |
794d92ca AE |
111 | 'student_membership_monthly_rate', |
112 | ||
1ae1ff15 | 113 | 'query_discussion_process_old_membership', |
794d92ca | 114 | 'query_discussion_process_donations', |
794d92ca AE |
115 | 'query_discussion_process_adhoc', |
116 | ||
117 | 'discussion_process_active', | |
118 | 'discussion_process_contrib_start_date', | |
119 | 'discussion_process_contrib_end_date', | |
120 | 'discussion_process_adhoc_access_group_id', | |
121 | 'discussion_process_adhoc_no_access_group_id',] | |
3fa64def | 122 | as $param) { |
e12acfe1 | 123 | |
395539d7 | 124 | if (!array_key_exists($param, $config)) { |
cf44092c | 125 | throw new Exception('Missing required attribute \''.$param. |
395539d7 AE |
126 | '\' for authentication source '.$this->authId); |
127 | } | |
128 | ||
129 | if (!is_string($config[$param])) { | |
cf44092c | 130 | throw new Exception('Expected parameter \''.$param. |
395539d7 AE |
131 | '\' for authentication source '.$this->authId. |
132 | ' to be a string. Instead it was: '. | |
133 | var_export($config[$param], true)); | |
134 | } | |
e12acfe1 AE |
135 | |
136 | $this->$param = $config[$param]; | |
395539d7 AE |
137 | } |
138 | ||
395539d7 AE |
139 | if (isset($config['options'])) { |
140 | $this->options = $config['options']; | |
141 | } | |
142 | } | |
143 | ||
144 | ||
145 | /** | |
146 | * Create a database connection. | |
147 | * | |
f58b2b6b | 148 | * @return PDO The database connection. |
395539d7 AE |
149 | */ |
150 | private function connect() | |
151 | { | |
152 | try { | |
cf44092c AE |
153 | $db = new PDO($this->dsn, $this->username, $this->password, $this->options); |
154 | } catch (PDOException $e) { | |
155 | // Obfuscate the password if it's part of the dsn | |
156 | $obfuscated_dsn = preg_replace('/(user|password)=(.*?([;]|$))/', '${1}=***', $this->dsn); | |
157 | ||
158 | throw new Exception('fsfdrupalauth:' . $this->authId . ': - Failed to connect to \'' . | |
159 | $obfuscated_dsn . '\': ' . $e->getMessage()); | |
395539d7 AE |
160 | } |
161 | ||
cf44092c | 162 | $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
395539d7 AE |
163 | |
164 | $driver = explode(':', $this->dsn, 2); | |
165 | $driver = strtolower($driver[0]); | |
166 | ||
167 | // Driver specific initialization | |
168 | switch ($driver) { | |
169 | case 'mysql': | |
170 | // Use UTF-8 | |
171 | $db->exec("SET NAMES 'utf8mb4'"); | |
172 | break; | |
173 | case 'pgsql': | |
174 | // Use UTF-8 | |
175 | $db->exec("SET NAMES 'UTF8'"); | |
176 | break; | |
177 | } | |
178 | ||
179 | return $db; | |
180 | } | |
181 | ||
182 | /* | |
183 | * Check the password against a Drupal hash | |
184 | * | |
185 | */ | |
186 | private function check_password($password, $hash) { | |
187 | ||
188 | // | |
189 | // The reason for running a separate process is so that the PHP global | |
190 | // env doesn't get clobbered by include / require. | |
191 | // | |
192 | ||
193 | // pipes code based off of https://www.php.net/manual/en/function.proc-open.php | |
194 | // CC-BY 3.0 or later | |
195 | $descriptorspec = array( | |
59f90414 AE |
196 | 0 => array("pipe", "r"), // stdin is a pipe that the child may read from |
197 | 1 => array("pipe", "w"), // stdout is a pipe that the child may write to | |
198 | 2 => array("pipe", "w") // stderr is a pipe that the child may write to | |
395539d7 AE |
199 | ); |
200 | ||
201 | $cwd = "../modules/fsfdrupalauth/extlib"; | |
202 | //$env = array('some_option' => 'aeiou'); | |
203 | $env = array(); | |
204 | ||
205 | $process = proc_open('php drupal-pw-check.php', $descriptorspec, $pipes, $cwd, $env); | |
206 | ||
207 | if (is_resource($process)) { | |
208 | // $pipes now looks like this: | |
209 | // 0 => writeable handle connected to child stdin | |
210 | // 1 => readable handle connected to child stdout | |
211 | ||
212 | fwrite($pipes[0], json_encode([$password, $hash])); | |
213 | fclose($pipes[0]); | |
214 | ||
215 | $result = stream_get_contents($pipes[1]); | |
216 | fclose($pipes[1]); | |
217 | ||
218 | $errors = stream_get_contents($pipes[2]); | |
219 | fclose($pipes[2]); | |
220 | ||
221 | // It is important that you close any pipes before calling | |
222 | // proc_close in order to avoid a deadlock | |
223 | $return_value = proc_close($process); | |
224 | ||
cf44092c | 225 | //Logger::debug('fsfdrupalauth:'.$this->authId.': authenticator stdout: '.$result); |
3fa64def | 226 | |
e57e2393 | 227 | $errors_found_yet = false; |
395539d7 | 228 | if ($errors != "") { |
cf44092c | 229 | Logger::error('fsfdrupalauth:'.$this->authId.': authenticator stderr: '.$errors); |
e57e2393 | 230 | $errors_found_yet = true; |
395539d7 | 231 | } |
3fa64def | 232 | |
395539d7 | 233 | if ($return_value != 0) { |
cf44092c | 234 | Logger::error('fsfdrupalauth:'.$this->authId.': authenticator non-zero return code: '.$return_value); |
e57e2393 | 235 | $errors_found_yet = true; |
395539d7 | 236 | } |
3fa64def | 237 | |
6921b9d4 | 238 | return (!$errors_found_yet && is_string($result) && rtrim($result) == "true"); |
395539d7 AE |
239 | |
240 | } else { | |
241 | ||
cf44092c | 242 | Logger::error('fsfdrupalauth:'.$this->authId.': unable to launch authenticator'); |
395539d7 AE |
243 | |
244 | return false; | |
245 | } | |
246 | } | |
247 | ||
248 | /** | |
249 | * | |
250 | * query the database with arbitrary queries that only require a user name. | |
251 | * | |
252 | */ | |
2e644466 | 253 | private function query_db($queryname, $query_params) |
395539d7 AE |
254 | { |
255 | assert(is_string($queryname)); | |
256 | assert(is_string($username)); | |
257 | ||
258 | $db = $this->connect(); | |
259 | ||
260 | try { | |
261 | $sth = $db->prepare($this->$queryname); | |
cf44092c AE |
262 | } catch (PDOException $e) { |
263 | throw new Exception('fsfdrupalauth:'.$this->authId. | |
395539d7 AE |
264 | ': - Failed to prepare queryname: '.$queryname.': '.$e->getMessage()); |
265 | } | |
266 | ||
267 | try { | |
2e644466 | 268 | $sth->execute($query_params); |
cf44092c AE |
269 | } catch (PDOException $e) { |
270 | throw new Exception('fsfdrupalauth:'.$this->authId. | |
395539d7 AE |
271 | ': - Failed to execute queryname: '.$queryname.': '.$e->getMessage()); |
272 | } | |
273 | ||
274 | try { | |
f58b2b6b | 275 | $data = $sth->fetchAll(PDO::FETCH_ASSOC); |
cf44092c AE |
276 | } catch (PDOException $e) { |
277 | throw new Exception('fsfdrupalauth:'.$this->authId. | |
395539d7 AE |
278 | ': - Failed to fetch result set: '.$e->getMessage()); |
279 | } | |
280 | ||
cf44092c | 281 | Logger::info('fsfdrupalauth:'.$this->authId.': Got '.count($data). |
395539d7 AE |
282 | ' rows from database'); |
283 | ||
284 | return $data; | |
285 | } | |
286 | ||
287 | /** | |
288 | * add more CAS attributes to user, such as is_staff and is_member | |
289 | */ | |
290 | private function add_more_attributes(&$attributes, $username) { | |
291 | ||
f33432a6 AE |
292 | // |
293 | // query on membership | |
294 | // | |
395539d7 | 295 | |
2e644466 | 296 | $membership_data = $this->query_db('query_membership', ['username' => $username]); |
395539d7 AE |
297 | |
298 | if (count($membership_data) === 0) { | |
299 | // No rows returned - invalid username | |
cf44092c | 300 | Logger::debug('fsfdrupalauth:'.$this->authId. |
395539d7 AE |
301 | ': No rows in result set. Probably no membership.'); |
302 | } | |
303 | ||
f33432a6 | 304 | $attributes['is_member'] = ['false']; |
395539d7 AE |
305 | $attributes['was_member'] = ['false']; |
306 | ||
307 | foreach ($membership_data as $row) { | |
308 | foreach ($row as $key => $value) { | |
309 | if ($value === null) { | |
310 | continue; | |
311 | } | |
312 | $value = (string) $value; | |
313 | ||
f33432a6 | 314 | if ($value === '1' || $value === '2' || $value === '3') { |
395539d7 AE |
315 | $attributes['is_member'] = ['true']; |
316 | $attributes['was_member'] = ['true']; | |
f33432a6 | 317 | } elseif ($value === '4') { |
395539d7 | 318 | $attributes['was_member'] = ['true']; |
f33432a6 | 319 | } |
395539d7 AE |
320 | } |
321 | } | |
322 | ||
c0d116a9 | 323 | // |
0782e0aa | 324 | // helper functions for access to board nomination / discussion process |
c0d116a9 AE |
325 | // |
326 | ||
29854532 AE |
327 | /** |
328 | * @param string $query_name Name of query in authsources | |
f38130e1 | 329 | * @param array $extra_params Associative array of parameters to include in query |
29854532 | 330 | */ |
f38130e1 AE |
331 | $donation_query = function ($query_name, $extra_params) |
332 | use ($username) { | |
29854532 | 333 | |
3fa64def | 334 | $parameters = ['username' => $username]; |
29854532 | 335 | |
3fa64def AE |
336 | foreach ($extra_params as $key => $value) { |
337 | $parameters[$key] = $value; | |
29854532 | 338 | } |
78e03f98 | 339 | |
f38130e1 AE |
340 | return $this->query_db($query_name, $parameters); |
341 | }; | |
78e03f98 | 342 | |
1ae1ff15 AE |
343 | $old_membership_query = $donation_query; |
344 | ||
f38130e1 | 345 | $compare_res = function ($result, $amount) { |
29854532 | 346 | foreach ($result[0] as $key => $value) { |
f38130e1 AE |
347 | if (intval($value) >= $amount) { |
348 | return true; | |
349 | } | |
350 | } | |
351 | return false; | |
352 | }; | |
353 | ||
32c7abac AE |
354 | // set dates here, used by helper functions below |
355 | $nomination_process_start_date = $this->nomination_process_contrib_start_date; | |
356 | $nomination_process_end_date = $this->nomination_process_contrib_end_date; | |
357 | $discussion_process_start_date = $this->discussion_process_contrib_start_date; | |
358 | $discussion_process_end_date = $this->discussion_process_contrib_end_date; | |
359 | ||
360 | ||
f38130e1 AE |
361 | // looks for memberships / comparable donations in time window. also |
362 | // looks for a membership or donation (included as a param) that | |
363 | // occurred up to a year before, and that would have carried over into | |
364 | // the time window with a single donation. this approximates whether | |
365 | // the person was, or would have been, a member during the configured | |
366 | // time window. | |
0782e0aa | 367 | $nomination_process_analyze_history = function ($selective_donations_history) |
32c7abac | 368 | use ($nomination_process_start_date, $nomination_process_end_date) { |
f38130e1 AE |
369 | |
370 | $eligible = false; | |
371 | ||
32c7abac AE |
372 | $start_date_obj = new \DateTime($nomination_process_start_date); |
373 | $end_date_obj = new \DateTime($nomination_process_end_date); | |
f38130e1 AE |
374 | |
375 | foreach ($selective_donations_history as $row) { | |
376 | ||
377 | $amount = intval($row['amount']); | |
378 | $member_type_id = $row['member_type_id']; | |
379 | $receive_date_obj = new \DateTime($row['receive_date']); | |
380 | ||
381 | if ($amount < 5) { | |
382 | continue; | |
383 | ||
384 | } elseif ($receive_date_obj >= $start_date_obj and $receive_date_obj <= $end_date_obj) { | |
385 | return true; | |
386 | ||
387 | } elseif ($receive_date_obj < $start_date_obj) { | |
388 | switch ($member_type_id) { | |
389 | case '1': | |
390 | case '2': | |
391 | $rate = intval($this->student_membership_monthly_rate); | |
392 | break; | |
393 | case '8': | |
394 | case '9': | |
395 | case null: | |
396 | default: | |
397 | $rate = intval($this->membership_monthly_rate); | |
398 | break; | |
399 | } | |
400 | $membership_end_date_obj = new \DateTime($row['receive_date']); | |
401 | $membership_end_date_obj->add(new \DateInterval("P" . ceil($amount / $rate) . "M")); | |
402 | ||
403 | if ($membership_end_date_obj >= $start_date_obj) { | |
404 | return true; | |
405 | } | |
406 | } | |
78e03f98 | 407 | } |
78e03f98 AE |
408 | return false; |
409 | }; | |
410 | ||
794d92ca | 411 | $discussion_process_analyze_history = function ($selective_donations_history) |
32c7abac | 412 | use ($discussion_process_start_date, $discussion_process_end_date) { |
794d92ca AE |
413 | |
414 | $eligible = false; | |
415 | $total = 0; | |
416 | ||
32c7abac AE |
417 | $start_date_obj = new \DateTime($discussion_process_start_date); |
418 | $end_date_obj = new \DateTime($discussion_process_end_date); | |
794d92ca AE |
419 | |
420 | foreach ($selective_donations_history as $row) { | |
421 | ||
422 | $amount = intval($row['amount']); | |
423 | $member_type_id = $row['member_type_id']; | |
424 | $receive_date_obj = new \DateTime($row['receive_date']); | |
425 | ||
32c7abac | 426 | if (($receive_date_obj > $start_date_obj) && ($receive_date_obj < $end_date_obj)) { |
794d92ca AE |
427 | $total += $amount; |
428 | } | |
429 | } | |
430 | ||
431 | if ($total >= 120) { | |
432 | return true; | |
433 | } else { | |
434 | return false; | |
435 | } | |
436 | }; | |
437 | ||
0782e0aa AE |
438 | // |
439 | // nomination form participation specific checks | |
440 | // | |
441 | ||
32c7abac AE |
442 | $donation_params = ['start_date' => $nomination_process_start_date, 'end_date' => $nomination_process_end_date]; |
443 | $gift_member_params = ['start_date' => $nomination_process_start_date, 'end_date' => $nomination_process_end_date, 'gift_redeem_page_id' => intval($this->gift_redeem_page_id)]; | |
e9db6ecd | 444 | $adhoc_params = ['adhoc_access_group_id' => intval($this->nomination_process_adhoc_access_group_id)]; |
3fa64def | 445 | |
16858322 AE |
446 | if ($this->nomination_process_active != 'true' ) { |
447 | $attributes['nomination_process'] = ['false']; | |
0782e0aa | 448 | |
16858322 AE |
449 | } elseif ($compare_res($donation_query('query_nomination_process_adhoc', $adhoc_params), 1)) { |
450 | $attributes['nomination_process'] = ['true']; | |
451 | ||
452 | } elseif ($attributes['is_member'] != ['true']) { | |
453 | Logger::debug('fsfdrupalauth:'.$this->authId. | |
454 | ': Not a member / comparable donor during window for board process.'); | |
455 | $attributes['nomination_process'] = ['false']; | |
456 | ||
457 | } elseif ($nomination_process_analyze_history($donation_query('query_nomination_process_donations', $donation_params)) | |
458 | || $compare_res($donation_query('query_nomination_process_gift_receipt', $gift_member_params), 1)) { | |
459 | ||
460 | $attributes['nomination_process'] = ['true']; | |
29854532 | 461 | |
78e03f98 | 462 | } else { |
16858322 AE |
463 | Logger::debug('fsfdrupalauth:'.$this->authId. |
464 | ': Not a member / comparable donor during window for board process.'); | |
78e03f98 AE |
465 | $attributes['nomination_process'] = ['false']; |
466 | } | |
c0d116a9 | 467 | |
794d92ca AE |
468 | // |
469 | // discussion form participation specific checks | |
470 | // | |
471 | ||
32c7abac | 472 | $donation_params = ['start_date' => $discussion_process_start_date, 'end_date' => $discussion_process_end_date]; |
1ae1ff15 | 473 | $old_member_params = $donation_params; |
794d92ca AE |
474 | $adhoc_params = ['adhoc_access_group_id' => intval($this->discussion_process_adhoc_access_group_id)]; |
475 | $adhoc_params_no = ['adhoc_access_group_id' => intval($this->discussion_process_adhoc_no_access_group_id)]; | |
476 | ||
16858322 AE |
477 | if ($this->discussion_process_active != 'true' ) { |
478 | $attributes['discussion_process'] = ['false']; | |
794d92ca | 479 | |
16858322 AE |
480 | } elseif ($compare_res($donation_query('query_discussion_process_adhoc', $adhoc_params_no), 1)) { |
481 | Logger::debug('fsfdrupalauth:'.$this->authId. | |
482 | ': Nominee not eligible for board nominee discussion process.'); | |
483 | $attributes['discussion_process'] = ['false']; | |
794d92ca | 484 | |
16858322 AE |
485 | } elseif ($compare_res($donation_query('query_discussion_process_adhoc', $adhoc_params), 1)) { |
486 | $attributes['discussion_process'] = ['true']; | |
794d92ca | 487 | |
16858322 AE |
488 | } elseif ($attributes['is_member'] != ['true']) { |
489 | Logger::debug('fsfdrupalauth:'.$this->authId. | |
490 | ': Not eligible for board nominee discussion process.'); | |
491 | $attributes['discussion_process'] = ['false']; | |
492 | ||
1ae1ff15 | 493 | } elseif ($compare_res($old_membership_query('query_discussion_process_old_membership', $old_member_params), 1) |
5ba93058 | 494 | || $discussion_process_analyze_history($donation_query('query_discussion_process_donations', $donation_params))) { |
16858322 AE |
495 | |
496 | $attributes['discussion_process'] = ['true']; | |
794d92ca | 497 | |
794d92ca | 498 | } else { |
16858322 AE |
499 | Logger::debug('fsfdrupalauth:'.$this->authId. |
500 | ': Not eligible for board nominee discussion process.'); | |
794d92ca AE |
501 | $attributes['discussion_process'] = ['false']; |
502 | } | |
503 | ||
f33432a6 AE |
504 | // |
505 | // query on staff | |
506 | // | |
395539d7 | 507 | |
e12acfe1 | 508 | $staff_data = $this->query_db('query_staff', ['username' => $username, 'fsf_org_id' => $this->fsf_org_id]); |
395539d7 AE |
509 | |
510 | if (count($staff_data) === 0) { | |
511 | // No rows returned - invalid username | |
cf44092c | 512 | Logger::debug('fsfdrupalauth:'.$this->authId. |
395539d7 AE |
513 | ': No rows in result set. Probably not FSF staff.'); |
514 | } | |
515 | ||
f33432a6 | 516 | $attributes['is_fsf_staff'] = ['false']; |
395539d7 AE |
517 | |
518 | foreach ($staff_data as $row) { | |
519 | foreach ($row as $key => $value) { | |
520 | ||
521 | if ($value === null) { | |
522 | continue; | |
523 | } | |
524 | $value = (string) $value; | |
525 | ||
bdc16e06 | 526 | if (strtolower($value) === strtolower($username)) { |
395539d7 | 527 | // they are staff |
bdc16e06 | 528 | $attributes['is_fsf_staff'] = ['true']; |
395539d7 AE |
529 | break; |
530 | } | |
531 | } | |
532 | } | |
429471b4 AE |
533 | |
534 | // | |
535 | // aggregate attribute | |
536 | // | |
537 | ||
538 | $groups_list = ''; | |
539 | $first = true; | |
540 | foreach ($attributes as $key => $value) { | |
541 | if ($value == ['true']) { | |
542 | if (!$first) { | |
543 | $groups_list .= ', '; | |
544 | } | |
545 | $groups_list .= $key; | |
546 | $first = false; | |
547 | } | |
548 | } | |
549 | ||
550 | $attributes['groups_list'] = [$groups_list]; | |
395539d7 AE |
551 | } |
552 | ||
553 | /** | |
554 | * Attempt to log in using the given username and password. | |
555 | * | |
556 | * On a successful login, this function should return the users attributes. On failure, | |
557 | * it should throw an exception. If the error was caused by the user entering the wrong | |
cf44092c | 558 | * username or password, a Error\Error('WRONGUSERPASS') should be thrown. |
395539d7 AE |
559 | * |
560 | * Note that both the username and the password are UTF-8 encoded. | |
561 | * | |
562 | * @param string $username The username the user wrote. | |
563 | * @param string $password The password the user wrote. | |
564 | * @return array Associative array with the users attributes. | |
565 | */ | |
566 | protected function login($username, $password) | |
567 | { | |
568 | assert(is_string($username)); | |
569 | assert(is_string($password)); | |
570 | ||
571 | //// keep this commented when it's not in use. it prints user passwords to the log file | |
cf44092c | 572 | //Logger::debug('fsfdrupalauth:'.$this->authId.': entered password: '.$password); |
395539d7 AE |
573 | |
574 | ||
2e644466 | 575 | $user_data = $this->query_db('query_main', ['username' => $username]); |
395539d7 AE |
576 | |
577 | ||
578 | if (count($user_data) === 0) { | |
579 | // No rows returned - invalid username | |
cf44092c | 580 | Logger::error('fsfdrupalauth:'.$this->authId. |
395539d7 | 581 | ': No rows in result set. Probably wrong username.'); |
cf44092c | 582 | throw new Error\Error('WRONGUSERPASS'); |
395539d7 AE |
583 | } |
584 | ||
585 | /* Extract attributes. We allow the resultset to consist of multiple rows. Attributes | |
586 | * which are present in more than one row will become multivalued. null values and | |
587 | * duplicate values will be skipped. All values will be converted to strings. | |
588 | */ | |
589 | $attributes = []; | |
590 | ||
f33432a6 AE |
591 | // use the entered user name so we don't forcibly change it to all |
592 | // lower case. this is to preserve the behavior of the old cas server, | |
593 | // and to remain compatible with our MW and Discourse sites that are | |
594 | // case sensitive. | |
595 | $attributes['name'][] = $username; | |
395539d7 AE |
596 | |
597 | foreach ($user_data as $row) { | |
598 | foreach ($row as $key => $value) { | |
599 | if ($value === null) { | |
600 | continue; | |
601 | } | |
602 | ||
603 | $value = (string) $value; | |
604 | ||
605 | if (!array_key_exists($key, $attributes)) { | |
606 | $attributes[$key] = []; | |
607 | } | |
608 | ||
609 | if (in_array($value, $attributes[$key], true)) { | |
610 | // Value already exists in attribute | |
611 | continue; | |
612 | } | |
613 | ||
614 | $attributes[$key][] = $value; | |
615 | } | |
616 | } | |
617 | ||
618 | if (!$this->check_password($password, $attributes['pass'][0])) { | |
cf44092c | 619 | throw new Error\Error('WRONGUSERPASS'); |
395539d7 AE |
620 | } |
621 | ||
622 | unset($attributes['pass']); | |
623 | ||
624 | ||
625 | $this->add_more_attributes($attributes, $username); | |
626 | ||
627 | ||
cf44092c | 628 | Logger::info('fsfdrupalauth:'.$this->authId.': Attributes: '. |
395539d7 AE |
629 | implode(',', array_keys($attributes))); |
630 | ||
631 | return $attributes; | |
632 | } | |
633 | } |