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