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 | |
794d92ca AE |
52 | private $query_discussion_process_donations; |
53 | private $query_discussion_process_gift_receipt; | |
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 | ||
113 | 'query_discussion_process_donations', | |
114 | 'query_discussion_process_gift_receipt', | |
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 | |
f38130e1 | 343 | $compare_res = function ($result, $amount) { |
29854532 | 344 | foreach ($result[0] as $key => $value) { |
f38130e1 AE |
345 | if (intval($value) >= $amount) { |
346 | return true; | |
347 | } | |
348 | } | |
349 | return false; | |
350 | }; | |
351 | ||
352 | // looks for memberships / comparable donations in time window. also | |
353 | // looks for a membership or donation (included as a param) that | |
354 | // occurred up to a year before, and that would have carried over into | |
355 | // the time window with a single donation. this approximates whether | |
356 | // the person was, or would have been, a member during the configured | |
357 | // time window. | |
0782e0aa | 358 | $nomination_process_analyze_history = function ($selective_donations_history) |
f38130e1 AE |
359 | use ($start_date, $end_date) { |
360 | ||
361 | $eligible = false; | |
362 | ||
363 | $start_date_obj = new \DateTime($start_date); | |
364 | $end_date_obj = new \DateTime($end_date); | |
365 | ||
366 | foreach ($selective_donations_history as $row) { | |
367 | ||
368 | $amount = intval($row['amount']); | |
369 | $member_type_id = $row['member_type_id']; | |
370 | $receive_date_obj = new \DateTime($row['receive_date']); | |
371 | ||
372 | if ($amount < 5) { | |
373 | continue; | |
374 | ||
375 | } elseif ($receive_date_obj >= $start_date_obj and $receive_date_obj <= $end_date_obj) { | |
376 | return true; | |
377 | ||
378 | } elseif ($receive_date_obj < $start_date_obj) { | |
379 | switch ($member_type_id) { | |
380 | case '1': | |
381 | case '2': | |
382 | $rate = intval($this->student_membership_monthly_rate); | |
383 | break; | |
384 | case '8': | |
385 | case '9': | |
386 | case null: | |
387 | default: | |
388 | $rate = intval($this->membership_monthly_rate); | |
389 | break; | |
390 | } | |
391 | $membership_end_date_obj = new \DateTime($row['receive_date']); | |
392 | $membership_end_date_obj->add(new \DateInterval("P" . ceil($amount / $rate) . "M")); | |
393 | ||
394 | if ($membership_end_date_obj >= $start_date_obj) { | |
395 | return true; | |
396 | } | |
397 | } | |
78e03f98 | 398 | } |
78e03f98 AE |
399 | return false; |
400 | }; | |
401 | ||
794d92ca AE |
402 | $discussion_process_analyze_history = function ($selective_donations_history) |
403 | use ($start_date, $end_date) { | |
404 | ||
405 | $eligible = false; | |
406 | $total = 0; | |
407 | ||
408 | $start_date_obj = new \DateTime($start_date); | |
409 | $end_date_obj = new \DateTime($end_date); | |
410 | ||
411 | foreach ($selective_donations_history as $row) { | |
412 | ||
413 | $amount = intval($row['amount']); | |
414 | $member_type_id = $row['member_type_id']; | |
415 | $receive_date_obj = new \DateTime($row['receive_date']); | |
416 | ||
417 | if ($receive_date_obj < $start_date_obj && $receive_date > $end_date_obj) { | |
418 | $total += $amount; | |
419 | } | |
420 | } | |
421 | ||
422 | if ($total >= 120) { | |
423 | return true; | |
424 | } else { | |
425 | return false; | |
426 | } | |
427 | }; | |
428 | ||
0782e0aa AE |
429 | // |
430 | // nomination form participation specific checks | |
431 | // | |
432 | ||
433 | $start_date = $this->nomination_process_contrib_start_date; | |
434 | $end_date = $this->nomination_process_contrib_end_date; | |
435 | ||
f38130e1 | 436 | $donation_params = ['start_date' => $start_date, 'end_date' => $end_date]; |
ccf1684d | 437 | $gift_member_params = ['start_date' => $start_date, 'end_date' => $end_date, 'gift_redeem_page_id' => intval($this->gift_redeem_page_id)]; |
e9db6ecd | 438 | $adhoc_params = ['adhoc_access_group_id' => intval($this->nomination_process_adhoc_access_group_id)]; |
3fa64def | 439 | |
2d61361e | 440 | if ($this->nomination_process_active == 'true' ) { |
0782e0aa AE |
441 | if ($compare_res($donation_query('query_nomination_process_adhoc', $adhoc_params), 1)) { |
442 | $attributes['nomination_process'] = ['true']; | |
443 | ||
444 | } elseif ($attributes['is_member'] == ['true'] | |
445 | && ($nomination_process_analyze_history($donation_query('query_nomination_process_donations', $donation_params)) | |
446 | || $compare_res($donation_query('query_nomination_process_gift_receipt', $gift_member_params), 1))) { | |
29854532 | 447 | |
2d61361e AE |
448 | $attributes['nomination_process'] = ['true']; |
449 | } else { | |
450 | Logger::debug('fsfdrupalauth:'.$this->authId. | |
451 | ': Not a member / comparable donor during window for board process.'); | |
452 | $attributes['nomination_process'] = ['false']; | |
453 | } | |
78e03f98 AE |
454 | } else { |
455 | $attributes['nomination_process'] = ['false']; | |
456 | } | |
c0d116a9 | 457 | |
794d92ca AE |
458 | // |
459 | // discussion form participation specific checks | |
460 | // | |
461 | ||
462 | $start_date = $this->discussion_process_contrib_start_date; | |
463 | $end_date = $this->discussion_process_contrib_end_date; | |
464 | ||
465 | $donation_params = ['start_date' => $start_date, 'end_date' => $end_date]; | |
466 | $gift_member_params = ['start_date' => $start_date, 'end_date' => $end_date, 'gift_redeem_page_id' => intval($this->gift_redeem_page_id)]; | |
467 | $adhoc_params = ['adhoc_access_group_id' => intval($this->discussion_process_adhoc_access_group_id)]; | |
468 | $adhoc_params_no = ['adhoc_access_group_id' => intval($this->discussion_process_adhoc_no_access_group_id)]; | |
469 | ||
470 | if ($this->discussion_process_active == 'true' ) { | |
471 | if ($compare_res($donation_query('query_discussion_process_adhoc', $adhoc_params_no), 1)) { | |
472 | Logger::debug('fsfdrupalauth:'.$this->authId. | |
473 | ': Not eligible for board nominee discussion process.'); | |
474 | $attributes['discussion_process'] = ['false']; | |
475 | ||
476 | } elseif ($compare_res($donation_query('query_discussion_process_adhoc', $adhoc_params), 1)) { | |
477 | $attributes['discussion_process'] = ['true']; | |
478 | ||
479 | } elseif ($attributes['is_member'] == ['true'] | |
480 | && ($discussion_process_analyze_history($donation_query('query_discussion_process_donations', $donation_params)) | |
481 | || $compare_res($donation_query('query_discussion_process_gift_receipt', $gift_member_params), 1))) { | |
482 | ||
483 | $attributes['discussion_process'] = ['true']; | |
484 | ||
485 | } else { | |
486 | Logger::debug('fsfdrupalauth:'.$this->authId. | |
487 | ': Not eligible for board nominee discussion process.'); | |
488 | $attributes['discussion_process'] = ['false']; | |
489 | } | |
490 | } else { | |
491 | $attributes['discussion_process'] = ['false']; | |
492 | } | |
493 | ||
f33432a6 AE |
494 | // |
495 | // query on staff | |
496 | // | |
395539d7 | 497 | |
e12acfe1 | 498 | $staff_data = $this->query_db('query_staff', ['username' => $username, 'fsf_org_id' => $this->fsf_org_id]); |
395539d7 AE |
499 | |
500 | if (count($staff_data) === 0) { | |
501 | // No rows returned - invalid username | |
cf44092c | 502 | Logger::debug('fsfdrupalauth:'.$this->authId. |
395539d7 AE |
503 | ': No rows in result set. Probably not FSF staff.'); |
504 | } | |
505 | ||
f33432a6 | 506 | $attributes['is_fsf_staff'] = ['false']; |
395539d7 AE |
507 | |
508 | foreach ($staff_data as $row) { | |
509 | foreach ($row as $key => $value) { | |
510 | ||
511 | if ($value === null) { | |
512 | continue; | |
513 | } | |
514 | $value = (string) $value; | |
515 | ||
bdc16e06 | 516 | if (strtolower($value) === strtolower($username)) { |
395539d7 | 517 | // they are staff |
bdc16e06 | 518 | $attributes['is_fsf_staff'] = ['true']; |
395539d7 AE |
519 | break; |
520 | } | |
521 | } | |
522 | } | |
429471b4 AE |
523 | |
524 | // | |
525 | // aggregate attribute | |
526 | // | |
527 | ||
528 | $groups_list = ''; | |
529 | $first = true; | |
530 | foreach ($attributes as $key => $value) { | |
531 | if ($value == ['true']) { | |
532 | if (!$first) { | |
533 | $groups_list .= ', '; | |
534 | } | |
535 | $groups_list .= $key; | |
536 | $first = false; | |
537 | } | |
538 | } | |
539 | ||
540 | $attributes['groups_list'] = [$groups_list]; | |
395539d7 AE |
541 | } |
542 | ||
543 | /** | |
544 | * Attempt to log in using the given username and password. | |
545 | * | |
546 | * On a successful login, this function should return the users attributes. On failure, | |
547 | * it should throw an exception. If the error was caused by the user entering the wrong | |
cf44092c | 548 | * username or password, a Error\Error('WRONGUSERPASS') should be thrown. |
395539d7 AE |
549 | * |
550 | * Note that both the username and the password are UTF-8 encoded. | |
551 | * | |
552 | * @param string $username The username the user wrote. | |
553 | * @param string $password The password the user wrote. | |
554 | * @return array Associative array with the users attributes. | |
555 | */ | |
556 | protected function login($username, $password) | |
557 | { | |
558 | assert(is_string($username)); | |
559 | assert(is_string($password)); | |
560 | ||
561 | //// keep this commented when it's not in use. it prints user passwords to the log file | |
cf44092c | 562 | //Logger::debug('fsfdrupalauth:'.$this->authId.': entered password: '.$password); |
395539d7 AE |
563 | |
564 | ||
2e644466 | 565 | $user_data = $this->query_db('query_main', ['username' => $username]); |
395539d7 AE |
566 | |
567 | ||
568 | if (count($user_data) === 0) { | |
569 | // No rows returned - invalid username | |
cf44092c | 570 | Logger::error('fsfdrupalauth:'.$this->authId. |
395539d7 | 571 | ': No rows in result set. Probably wrong username.'); |
cf44092c | 572 | throw new Error\Error('WRONGUSERPASS'); |
395539d7 AE |
573 | } |
574 | ||
575 | /* Extract attributes. We allow the resultset to consist of multiple rows. Attributes | |
576 | * which are present in more than one row will become multivalued. null values and | |
577 | * duplicate values will be skipped. All values will be converted to strings. | |
578 | */ | |
579 | $attributes = []; | |
580 | ||
f33432a6 AE |
581 | // use the entered user name so we don't forcibly change it to all |
582 | // lower case. this is to preserve the behavior of the old cas server, | |
583 | // and to remain compatible with our MW and Discourse sites that are | |
584 | // case sensitive. | |
585 | $attributes['name'][] = $username; | |
395539d7 AE |
586 | |
587 | foreach ($user_data as $row) { | |
588 | foreach ($row as $key => $value) { | |
589 | if ($value === null) { | |
590 | continue; | |
591 | } | |
592 | ||
593 | $value = (string) $value; | |
594 | ||
595 | if (!array_key_exists($key, $attributes)) { | |
596 | $attributes[$key] = []; | |
597 | } | |
598 | ||
599 | if (in_array($value, $attributes[$key], true)) { | |
600 | // Value already exists in attribute | |
601 | continue; | |
602 | } | |
603 | ||
604 | $attributes[$key][] = $value; | |
605 | } | |
606 | } | |
607 | ||
608 | if (!$this->check_password($password, $attributes['pass'][0])) { | |
cf44092c | 609 | throw new Error\Error('WRONGUSERPASS'); |
395539d7 AE |
610 | } |
611 | ||
612 | unset($attributes['pass']); | |
613 | ||
614 | ||
615 | $this->add_more_attributes($attributes, $username); | |
616 | ||
617 | ||
cf44092c | 618 | Logger::info('fsfdrupalauth:'.$this->authId.': Attributes: '. |
395539d7 AE |
619 | implode(',', array_keys($attributes))); |
620 | ||
621 | return $attributes; | |
622 | } | |
623 | } |