// custom fsf authentication source wrapped by ratelimit auth source
'fsfdrupalauth:FSFDrupalAuth',
+
'dsn' => 'mysql:host=example.com;port=3306;dbname=drupal',
'username' => '$DB_USERNAME',
'password' => '$DB_PASSWORD',
+
'query_main' => 'SELECT pass, mail FROM users WHERE name = :username AND status = "1" limit 1;',
// don't filter with 'and c.is_test = 0' because these may be useful for FSF staff
'query_membership' => "select c.status_id from drupal.users a inner join civicrm.civicrm_uf_match b on a.uid=b.uf_id inner join civicrm.civicrm_membership c on b.contact_id=c.contact_id inner join civicrm.civicrm_contact d on c.contact_id=d.id where a.status = 1 and d.is_deleted = 0 and c.status_id is not NULL and a.name = :username and (c.status_id = 1 or c.status_id = 2 or c.status_id = 3 or c.status_id = 4) order by c.status_id limit 10;",
- 'query_staff' => "select a.name as is_fsf_staff from drupal.users a inner join civicrm.civicrm_uf_match b on a.uid=b.uf_id inner join civicrm.civicrm_contact c on b.contact_id=c.id inner join civicrm.civicrm_relationship d on c.id=d.contact_id_a where a.name=:username and a.status=1 and c.is_deleted=0 and d.relationship_type_id=4 and d.contact_id_b=FOOBAR and d.is_active=1 and (d.end_date>NOW() or d.end_date is NULL) limit 1;",
-
+ 'query_staff' => "select a.name as is_fsf_staff from drupal.users a inner join civicrm.civicrm_uf_match b on a.uid=b.uf_id inner join civicrm.civicrm_contact c on b.contact_id=c.id inner join civicrm.civicrm_relationship d on c.id=d.contact_id_a where a.name=:username and a.status=1 and c.is_deleted=0 and d.relationship_type_id=4 and d.contact_id_b = :fsf_org_id and d.is_active=1 and (d.end_date>NOW() or d.end_date is NULL) limit 1;",
+ 'query_nomination_process_donations' => "select d.total_amount as amount, d.receive_date as receive_date, f.membership_type_id as member_type_id from drupal.users a inner join civicrm.civicrm_uf_match b on a.uid=b.uf_id inner join civicrm.civicrm_contact c on b.contact_id=c.id inner join civicrm.civicrm_contribution d on c.id=d.contact_id left join civicrm.civicrm_membership_payment e on d.id=e.contribution_id left join civicrm.civicrm_membership f on e.membership_id=f.id where a.name = :username and d.contribution_status_id = 1 and d.receive_date > subdate(:start_date, interval 1 year) and d.receive_date < :end_date;",
+ 'query_nomination_process_gift_receipt' => "select count(*) as gift_memberships_count from drupal.users a inner join civicrm.civicrm_uf_match b on a.uid=b.uf_id inner join civicrm.civicrm_contact c on b.contact_id=c.id inner join civicrm.civicrm_contribution d on c.id=d.contact_id where a.name = :username and d.contribution_page_id = :gift_redeem_page_id and d.receive_date > :start_date and d.receive_date < :end_date;",
+ 'query_nomination_process_adhoc' => "select count(*) as is_adhoc_member from drupal.users a inner join civicrm.civicrm_uf_match b on a.uid=b.uf_id inner join civicrm.civicrm_contact c on b.contact_id=c.id inner join civicrm.civicrm_group_contact d on c.id=d.contact_id where a.name=:username and a.status=1 and c.is_deleted=0 and d.group_id = :adhoc_access_group_id and d.status = 'Added' limit 1;",
+
+ 'fsf_org_id' => '739106',
+
+ 'nomination_process_contrib_start_date' => '2017-01-01',
+ 'nomination_process_contrib_end_date' => '2022-01-01',
+ 'gift_redeem_page_id' => '63',
+ 'membership_monthly_rate' => '10',
+ 'student_membership_monthly_rate' => '5',
+ 'adhoc_access_group_id' => '1786',
],
## License
/**
* The current system version.
*/
-define('VERSION', '7.83');
+define('VERSION', '7.95-dev');
/**
* Core API compatibility.
/**
* Minimum supported version of PHP.
*/
-define('DRUPAL_MINIMUM_PHP', '5.2.4');
+define('DRUPAL_MINIMUM_PHP', '5.3.3');
/**
* Minimum recommended value of PHP memory_limit.
if (!empty($_SERVER['HTTP_HOST'])) {
$cookie_domain = _drupal_get_cookie_domain($_SERVER['HTTP_HOST']);
}
+
+ // Drupal 7.83 included a security improvement whereby www. is no longer
+ // stripped from the cookie domain. However, this can cause problems with
+ // existing session cookies where some users are left unable to login. In
+ // order to avoid that, prepend a leading dot to the session_name that was
+ // derived from the base_url when a www. subdomain is in use.
+ // @see https://www.drupal.org/project/drupal/issues/2522002
+ if (strpos($session_name, 'www.') === 0) {
+ $session_name = '.' . $session_name;
+ }
}
// Per RFC 2109, cookie domains must contain at least one dot other than the
// first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain.
*/
function drupal_serve_page_from_cache(stdClass $cache) {
// Negotiate whether to use compression.
- $page_compression = !empty($cache->data['page_compressed']);
+ $page_compression = !empty($cache->data['page_compressed']) && !empty($cache->data['body']);
$return_compressed = $page_compression && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE;
// Get headers set in hook_boot(). Keys are lower-case.
* @ingroup sanitization
*/
function check_plain($text) {
- return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
+ return htmlspecialchars((string) $text, ENT_QUOTES, 'UTF-8');
}
/**
* TRUE if the text is valid UTF-8, FALSE if not.
*/
function drupal_validate_utf8($text) {
- if (strlen($text) == 0) {
+ if (strlen((string) $text) == 0) {
return TRUE;
}
// With the PCRE_UTF8 modifier 'u', preg_match() fails silently on strings
// the microtime() - is prepended rather than appended. This is to avoid
// directly leaking $random_state via the $output stream, which could
// allow for trivial prediction of further "random" numbers.
- if (strlen($bytes) < $count) {
+ if (strlen((string) $bytes) < $count) {
// Initialize on the first call. The contents of $_SERVER includes a mix of
// user-specific and system information that varies a little with each page.
if (!isset($random_state)) {
setcookie($name, $value, $options);
}
else {
+ $defaults = array(
+ 'expires' => 0,
+ 'path' => '',
+ 'domain' => '',
+ 'secure' => FALSE,
+ 'httponly' => FALSE,
+ );
+ $options += $defaults;
setcookie($name, $value, $options['expires'], $options['path'], $options['domain'], $options['secure'], $options['httponly']);
}
}
private $query_main;
private $query_membership;
private $query_staff;
+ private $query_nomination_process_donations;
+ private $query_nomination_process_gift_receipt;
+ private $query_nomination_process_adhoc;
+
+ /**
+ * SQL query parameters, or variables that help determine which attributes
+ * someone has
+ */
+ private $fsf_org_id;
+ private $nomination_process_contrib_start_date;
+ private $nomination_process_contrib_end_date;
+ private $gift_redeem_page_id;
+ private $membership_monthly_rate;
+ private $student_membership_monthly_rate;
+ private $adhoc_access_group_id;
/**
* Constructor for this authentication source.
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', 'gift_redeem_page_id',
+ 'fsf_org_id', 'membership_monthly_rate',
+ 'student_membership_monthly_rate',
+ 'nomination_process_contrib_start_date',
+ 'nomination_process_contrib_end_date', 'adhoc_access_group_id']
+ as $param) {
+
if (!array_key_exists($param, $config)) {
throw new Exception('Missing required attribute \''.$param.
'\' for authentication source '.$this->authId);
' 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'];
}
$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);
$errors_found_yet = true;
}
-
+
return (!$errors_found_yet && is_string($result) && rtrim($result) == "true");
} else {
* 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));
}
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());
// 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
}
}
+ //
+ // query for access to board nomination process
+ //
+
+ $start_date = $this->nomination_process_contrib_start_date;
+ $end_date = $this->nomination_process_contrib_end_date;
+
+ /**
+ * @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);
+ };
+
+ $compare_res = function ($result, $amount) {
+ foreach ($result[0] as $key => $value) {
+ if (intval($value) >= $amount) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ // 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.
+ $analyze_history = function ($selective_donations_history)
+ use ($start_date, $end_date) {
+
+ $eligible = false;
+
+ $start_date_obj = new \DateTime($start_date);
+ $end_date_obj = new \DateTime($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;
+ };
+
+ $donation_params = ['start_date' => $start_date, 'end_date' => $end_date];
+ $gift_member_params = ['start_date' => $start_date, 'end_date' => $end_date, 'gift_redeem_page_id' => intval($this->gift_redeem_page_id)];
+ $adhoc_params = ['adhoc_access_group_id' => intval($this->adhoc_access_group_id)];
+
+ if (($analyze_history($donation_query('query_nomination_process_donations', $donation_params))
+ || $compare_res($donation_query('query_nomination_process_gift_receipt', $gift_member_params), 1)
+ ) && ($attributes['is_member'] == ['true'])
+ || $compare_res($donation_query('query_nomination_process_adhoc', $adhoc_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'];
+ }
+
//
// query on staff
//
- $staff_data = $this->query_db('query_staff', $username);
+ $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
//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) {