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