X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=lib%2FAuth%2FSource%2FFSFDrupalAuth.php;h=69f60b473f2b26e4d9550c1bb477822c958bd9f7;hb=79ae195ecad22c0cea89a99276dc6af90ceff4db;hp=669b1ccc9e95058e15db99ae56b4f6346d66389a;hpb=cf44092c16ed68e1db3c98335da288baf88127a9;p=fsfdrupalauth.git diff --git a/lib/Auth/Source/FSFDrupalAuth.php b/lib/Auth/Source/FSFDrupalAuth.php index 669b1cc..69f60b4 100644 --- a/lib/Auth/Source/FSFDrupalAuth.php +++ b/lib/Auth/Source/FSFDrupalAuth.php @@ -45,6 +45,35 @@ class FSFDrupalAuth extends \SimpleSAML\Module\core\Auth\UserPassBase private $query_membership; private $query_staff; + private $query_nomination_process_donations; + private $query_nomination_process_gift_receipt; + private $query_nomination_process_adhoc; + + private $query_discussion_process_old_membership; + private $query_discussion_process_donations; + private $query_discussion_process_adhoc; + + /** + * SQL query parameters, or variables that help determine which attributes + * someone has + */ + private $fsf_org_id; + private $gift_redeem_page_id; + + private $nomination_process_active; + private $nomination_process_contrib_start_date; + private $nomination_process_contrib_end_date; + private $nomination_process_adhoc_access_group_id; + private $membership_monthly_rate; + private $student_membership_monthly_rate; + + private $discussion_process_active; + private $discussion_process_contrib_start_date; + private $discussion_process_contrib_end_date; + private $discussion_process_adhoc_access_group_id; + private $discussion_process_adhoc_no_access_group_id; + private $discussion_process_donation_amount; + /** * Constructor for this authentication source. * @@ -60,7 +89,40 @@ class FSFDrupalAuth extends \SimpleSAML\Module\core\Auth\UserPassBase parent::__construct($info, $config); // Make sure that all required parameters are present. - foreach (['dsn', 'username', 'password', 'query_main', 'query_membership', 'query_staff'] as $param) { + foreach (['dsn', + 'username', + 'password', + + 'query_main', + 'query_membership', + 'query_staff', + + 'query_nomination_process_donations', + 'query_nomination_process_gift_receipt', + 'query_nomination_process_adhoc', + + 'fsf_org_id', + 'gift_redeem_page_id', + + 'nomination_process_active', + 'nomination_process_contrib_start_date', + 'nomination_process_contrib_end_date', + 'nomination_process_adhoc_access_group_id', + 'membership_monthly_rate', + 'student_membership_monthly_rate', + + 'query_discussion_process_old_membership', + 'query_discussion_process_donations', + 'query_discussion_process_adhoc', + + 'discussion_process_active', + 'discussion_process_contrib_start_date', + 'discussion_process_contrib_end_date', + 'discussion_process_adhoc_access_group_id', + 'discussion_process_adhoc_no_access_group_id', + 'discussion_process_donation_amount',] + as $param) { + if (!array_key_exists($param, $config)) { throw new Exception('Missing required attribute \''.$param. '\' for authentication source '.$this->authId); @@ -72,14 +134,10 @@ class FSFDrupalAuth extends \SimpleSAML\Module\core\Auth\UserPassBase ' to be a string. Instead it was: '. var_export($config[$param], true)); } + + $this->$param = $config[$param]; } - $this->dsn = $config['dsn']; - $this->username = $config['username']; - $this->password = $config['password']; - $this->query_main = $config['query_main']; - $this->query_membership = $config['query_membership']; - $this->query_staff = $config['query_staff']; if (isset($config['options'])) { $this->options = $config['options']; } @@ -89,7 +147,7 @@ class FSFDrupalAuth extends \SimpleSAML\Module\core\Auth\UserPassBase /** * Create a database connection. * - * @return \PDO The database connection. + * @return PDO The database connection. */ private function connect() { @@ -137,9 +195,9 @@ class FSFDrupalAuth extends \SimpleSAML\Module\core\Auth\UserPassBase // pipes code based off of https://www.php.net/manual/en/function.proc-open.php // CC-BY 3.0 or later $descriptorspec = array( - 0 => array("pipe", "r"), // stdin is a pipe that the child will read from - 1 => array("pipe", "w"), // stdout is a pipe that the child will write to - 2 => array("pipe", "w") // stderr is a file to write to + 0 => array("pipe", "r"), // stdin is a pipe that the child may read from + 1 => array("pipe", "w"), // stdout is a pipe that the child may write to + 2 => array("pipe", "w") // stderr is a pipe that the child may write to ); $cwd = "../modules/fsfdrupalauth/extlib"; @@ -167,17 +225,19 @@ class FSFDrupalAuth extends \SimpleSAML\Module\core\Auth\UserPassBase $return_value = proc_close($process); //Logger::debug('fsfdrupalauth:'.$this->authId.': authenticator stdout: '.$result); - + + $errors_found_yet = false; if ($errors != "") { Logger::error('fsfdrupalauth:'.$this->authId.': authenticator stderr: '.$errors); + $errors_found_yet = true; } - + if ($return_value != 0) { Logger::error('fsfdrupalauth:'.$this->authId.': authenticator non-zero return code: '.$return_value); - return false; + $errors_found_yet = true; } - - return ($return_value == 0 && rtrim($result) == "true"); + + return (!$errors_found_yet && is_string($result) && rtrim($result) == "true"); } else { @@ -192,7 +252,7 @@ class FSFDrupalAuth extends \SimpleSAML\Module\core\Auth\UserPassBase * query the database with arbitrary queries that only require a user name. * */ - private function query_db($queryname, $username) + private function query_db($queryname, $query_params) { assert(is_string($queryname)); assert(is_string($username)); @@ -207,14 +267,14 @@ class FSFDrupalAuth extends \SimpleSAML\Module\core\Auth\UserPassBase } try { - $sth->execute(['username' => $username]); + $sth->execute($query_params); } catch (PDOException $e) { throw new Exception('fsfdrupalauth:'.$this->authId. ': - Failed to execute queryname: '.$queryname.': '.$e->getMessage()); } try { - $data = $sth->fetchAll(\PDO::FETCH_ASSOC); + $data = $sth->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $e) { throw new Exception('fsfdrupalauth:'.$this->authId. ': - Failed to fetch result set: '.$e->getMessage()); @@ -231,11 +291,11 @@ class FSFDrupalAuth extends \SimpleSAML\Module\core\Auth\UserPassBase */ private function add_more_attributes(&$attributes, $username) { - // - // query on membership - // + // + // query on membership + // - $membership_data = $this->query_db('query_membership', $username); + $membership_data = $this->query_db('query_membership', ['username' => $username]); if (count($membership_data) === 0) { // No rows returned - invalid username @@ -243,7 +303,7 @@ class FSFDrupalAuth extends \SimpleSAML\Module\core\Auth\UserPassBase ': No rows in result set. Probably no membership.'); } - $attributes['is_member'] = ['false']; + $attributes['is_member'] = ['false']; $attributes['was_member'] = ['false']; foreach ($membership_data as $row) { @@ -253,20 +313,201 @@ class FSFDrupalAuth extends \SimpleSAML\Module\core\Auth\UserPassBase } $value = (string) $value; - if ($value === '1' || $value === '2' || $value === '3') { + if ($value === '1' || $value === '2' || $value === '3') { $attributes['is_member'] = ['true']; $attributes['was_member'] = ['true']; - } elseif ($value === '4') { + } elseif ($value === '4') { $attributes['was_member'] = ['true']; - } + } } } + // + // helper functions for access to board nomination / discussion process + // + + /** + * @param string $query_name Name of query in authsources + * @param array $extra_params Associative array of parameters to include in query + */ + $donation_query = function ($query_name, $extra_params) + use ($username) { + + $parameters = ['username' => $username]; + + foreach ($extra_params as $key => $value) { + $parameters[$key] = $value; + } + + return $this->query_db($query_name, $parameters); + }; + + $old_membership_query = $donation_query; + + $compare_res = function ($result, $amount) { + foreach ($result[0] as $key => $value) { + if (intval($value) >= $amount) { + return true; + } + } + return false; + }; + + // set dates here, used by helper functions below + $nomination_process_start_date = $this->nomination_process_contrib_start_date; + $nomination_process_end_date = $this->nomination_process_contrib_end_date; + $discussion_process_start_date = $this->discussion_process_contrib_start_date; + $discussion_process_end_date = $this->discussion_process_contrib_end_date; + + + // looks for memberships / comparable donations in time window. also + // looks for a membership or donation (included as a param) that + // occurred up to a year before, and that would have carried over into + // the time window with a single donation. this approximates whether + // the person was, or would have been, a member during the configured + // time window. + $nomination_process_analyze_history = function ($selective_donations_history) + use ($nomination_process_start_date, $nomination_process_end_date) { + + $eligible = false; + + $start_date_obj = new \DateTime($nomination_process_start_date); + $end_date_obj = new \DateTime($nomination_process_end_date); + + foreach ($selective_donations_history as $row) { + + $amount = intval($row['amount']); + $member_type_id = $row['member_type_id']; + $receive_date_obj = new \DateTime($row['receive_date']); + + if ($amount < 5) { + continue; + + } elseif ($receive_date_obj >= $start_date_obj and $receive_date_obj <= $end_date_obj) { + return true; + + } elseif ($receive_date_obj < $start_date_obj) { + switch ($member_type_id) { + case '1': + case '2': + $rate = intval($this->student_membership_monthly_rate); + break; + case '8': + case '9': + case null: + default: + $rate = intval($this->membership_monthly_rate); + break; + } + $membership_end_date_obj = new \DateTime($row['receive_date']); + $membership_end_date_obj->add(new \DateInterval("P" . ceil($amount / $rate) . "M")); + + if ($membership_end_date_obj >= $start_date_obj) { + return true; + } + } + } + return false; + }; + + $discussion_process_analyze_history = function ($selective_donations_history) + use ($discussion_process_start_date, $discussion_process_end_date) { + + $eligible = false; + $total = 0; + + $start_date_obj = new \DateTime($discussion_process_start_date); + $end_date_obj = new \DateTime($discussion_process_end_date); + + foreach ($selective_donations_history as $row) { + + $amount = intval($row['amount']); + $member_type_id = $row['member_type_id']; + $receive_date_obj = new \DateTime($row['receive_date']); + + if (($receive_date_obj > $start_date_obj) && ($receive_date_obj < $end_date_obj)) { + $total += $amount; + } + } + + if ($total >= $this->discussion_process_donation_amount) { + return true; + } else { + return false; + } + }; + // - // query on staff + // nomination form participation specific checks // - $staff_data = $this->query_db('query_staff', $username); + $donation_params = ['start_date' => $nomination_process_start_date, 'end_date' => $nomination_process_end_date]; + $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)]; + $adhoc_params = ['adhoc_access_group_id' => intval($this->nomination_process_adhoc_access_group_id)]; + + if ($this->nomination_process_active != 'true' ) { + $attributes['nomination_process'] = ['false']; + + } elseif ($compare_res($donation_query('query_nomination_process_adhoc', $adhoc_params), 1)) { + $attributes['nomination_process'] = ['true']; + + } elseif ($attributes['is_member'] != ['true']) { + Logger::debug('fsfdrupalauth:'.$this->authId. + ': Not a member / comparable donor during window for board process.'); + $attributes['nomination_process'] = ['false']; + + } elseif ($nomination_process_analyze_history($donation_query('query_nomination_process_donations', $donation_params)) + || $compare_res($donation_query('query_nomination_process_gift_receipt', $gift_member_params), 1)) { + + $attributes['nomination_process'] = ['true']; + + } else { + Logger::debug('fsfdrupalauth:'.$this->authId. + ': Not a member / comparable donor during window for board process.'); + $attributes['nomination_process'] = ['false']; + } + + // + // discussion form participation specific checks + // + + $donation_params = ['start_date' => $discussion_process_start_date, 'end_date' => $discussion_process_end_date]; + $old_member_params = $donation_params; + $adhoc_params = ['adhoc_access_group_id' => intval($this->discussion_process_adhoc_access_group_id)]; + $adhoc_params_no = ['adhoc_access_group_id' => intval($this->discussion_process_adhoc_no_access_group_id)]; + + if ($this->discussion_process_active != 'true' ) { + $attributes['discussion_process'] = ['false']; + + } elseif ($compare_res($donation_query('query_discussion_process_adhoc', $adhoc_params_no), 1)) { + Logger::debug('fsfdrupalauth:'.$this->authId. + ': Nominee not eligible for board nominee discussion process.'); + $attributes['discussion_process'] = ['false']; + + } elseif ($compare_res($donation_query('query_discussion_process_adhoc', $adhoc_params), 1)) { + $attributes['discussion_process'] = ['true']; + + } elseif ($attributes['is_member'] != ['true']) { + Logger::debug('fsfdrupalauth:'.$this->authId. + ': Not eligible for board nominee discussion process.'); + $attributes['discussion_process'] = ['false']; + + } elseif ($compare_res($old_membership_query('query_discussion_process_old_membership', $old_member_params), 1) + || $discussion_process_analyze_history($donation_query('query_discussion_process_donations', $donation_params))) { + + $attributes['discussion_process'] = ['true']; + + } else { + Logger::debug('fsfdrupalauth:'.$this->authId. + ': Not eligible for board nominee discussion process.'); + $attributes['discussion_process'] = ['false']; + } + + // + // query on staff + // + + $staff_data = $this->query_db('query_staff', ['username' => $username, 'fsf_org_id' => $this->fsf_org_id]); if (count($staff_data) === 0) { // No rows returned - invalid username @@ -274,7 +515,7 @@ class FSFDrupalAuth extends \SimpleSAML\Module\core\Auth\UserPassBase ': No rows in result set. Probably not FSF staff.'); } - $attributes['is_fsf_staff'] = ['false']; + $attributes['is_fsf_staff'] = ['false']; foreach ($staff_data as $row) { foreach ($row as $key => $value) { @@ -284,13 +525,31 @@ class FSFDrupalAuth extends \SimpleSAML\Module\core\Auth\UserPassBase } $value = (string) $value; - if ($value === $username) { + if (strtolower($value) === strtolower($username)) { // they are staff - $attributes[$key] = ['true']; + $attributes['is_fsf_staff'] = ['true']; break; } } } + + // + // aggregate attribute + // + + $groups_list = ''; + $first = true; + foreach ($attributes as $key => $value) { + if ($value == ['true']) { + if (!$first) { + $groups_list .= ', '; + } + $groups_list .= $key; + $first = false; + } + } + + $attributes['groups_list'] = [$groups_list]; } /** @@ -315,7 +574,7 @@ class FSFDrupalAuth extends \SimpleSAML\Module\core\Auth\UserPassBase //Logger::debug('fsfdrupalauth:'.$this->authId.': entered password: '.$password); - $user_data = $this->query_db('query_main', $username); + $user_data = $this->query_db('query_main', ['username' => $username]); if (count($user_data) === 0) { @@ -331,11 +590,11 @@ class FSFDrupalAuth extends \SimpleSAML\Module\core\Auth\UserPassBase */ $attributes = []; - // use the entered user name so we don't forcibly change it to all - // lower case. this is to preserve the behavior of the old cas server, - // and to remain compatible with our MW and Discourse sites that are - // case sensitive. - $attributes['name'][] = $username; + // use the entered user name so we don't forcibly change it to all + // lower case. this is to preserve the behavior of the old cas server, + // and to remain compatible with our MW and Discourse sites that are + // case sensitive. + $attributes['name'][] = $username; foreach ($user_data as $row) { foreach ($row as $key => $value) {