Skip to content

Commit

Permalink
EDS API: Additional Headers to enable EBSCO to assist with BOT protec…
Browse files Browse the repository at this point in the history
…tion (#4105)
  • Loading branch information
cwolfebsco authored Dec 2, 2024
1 parent 4051277 commit 7eec896
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 19 deletions.
15 changes: 15 additions & 0 deletions config/vufind/EDS.ini
Original file line number Diff line number Diff line change
Expand Up @@ -443,3 +443,18 @@ DetailPageFormat = 'Long'
; than you define in ShortAuthorLimit, then VuFind will display the more_authors_abbrev text.
; If undefined, the default value is 3.
;ShortAuthorLimit = 3

[AdditionalHeaders]
; Due to the nature of EDS API integrations EBSCO's web application firewall (WAF) does not have sufficient
; data to distinguish bots from users. By sending some end-user data to EDS API, EBSCO's WAF can make more
; informed decisions regarding bots.
; This is important to guarantee accurate COUNTER 5 usage reports. The Counter Code of Practice 5.1 stipulates:
; "Activity generated by internet robots and crawlers MUST be excluded from all COUNTER usage reports."
; See https://cop5.countermetrics.org/en/5.1/07-processing/08-internet-robots-and-crawlers.html

; If you are already protecting your site from bots, e.g. through your own WAF, you might not wish to enable this

; Please note that this will send the end-user's IP Address & User Agent to EBSCO as x-eis-enduser-ip-address
; and x-eis-enduser-user-agent headers, so you might thus wish to consult your privacy officer.

; send_user_ip = true
9 changes: 9 additions & 0 deletions module/VuFind/src/VuFind/Search/Factory/EdsBackendFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ protected function createConnectorOptions()
'api_url' => $this->edsConfig->General->api_url
?? $this->defaultApiUrl,
'is_guest' => !$auth->isGranted('access.EDSExtendedResults'),
'send_user_ip' => $this->edsConfig->AdditionalHeaders->send_user_ip ?? false,
];
if (isset($this->edsConfig->General->auth_url)) {
$options['auth_url'] = $this->edsConfig->General->auth_url;
Expand All @@ -198,6 +199,14 @@ protected function createConnectorOptions()
if (!empty($this->edsConfig->EBSCO_Account->api_key_guest)) {
$options['api_key_guest'] = $this->edsConfig->EBSCO_Account->api_key_guest;
}
if ($options['send_user_ip']) {
$options['ip_to_report'] = $this->getService(\VuFind\Net\UserIpReader::class)->getUserIp();
$options['report_vendor_version'] = \VuFind\Config\Version::getBuildVersion();
$server = $this->getService(\VuFind\Http\PhpEnvironment\Request::class)->getServer();
if (!empty($server['HTTP_USER_AGENT'])) {
$options['user_agent'] = $server['HTTP_USER_AGENT'];
}
}
return $options;
}

Expand Down
99 changes: 80 additions & 19 deletions module/VuFindSearch/src/VuFindSearch/Backend/EDS/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,34 @@ abstract class Base implements LoggerAwareInterface
*/
protected $isGuest = true;

/**
* Indicator if additional headers should be sent
*
* @var bool
*/
protected $sendUserIp = false;

/**
* Vendor (e.g. 10.1)
*
* @var ?string
*/
protected $reportVendorVersion = null;

/**
* IpToReport (e.g. 123.123.123.13)
*
* @var ?string
*/
protected $ipToReport = null;

/**
* UserAgent (e.g. 10.1)
*
* @var ?string
*/
protected $userAgent = null;

/**
* Constructor
*
Expand Down Expand Up @@ -158,6 +186,18 @@ public function __construct($settings = [])
case 'is_guest':
$this->isGuest = $value;
break;
case 'send_user_ip':
$this->sendUserIp = $value;
break;
case 'report_vendor_version':
$this->reportVendorVersion = $value;
break;
case 'ip_to_report':
$this->ipToReport = $value;
break;
case 'user_agent':
$this->userAgent = $value;
break;
}
}
}
Expand Down Expand Up @@ -366,7 +406,7 @@ public function autocomplete($query, $type, $data, $raw = false)
$url = $data['url'] . '?' . http_build_query($params);

$this->debug('Autocomplete URL: ' . $url);
$response = $this->call($url, null, null, 'GET', null);
$response = $this->call($url, [], null, 'GET', null);
return $raw ? $response : $this->parseAutocomplete($response);
}

Expand Down Expand Up @@ -405,7 +445,7 @@ public function authenticate(
$authInfo['Options'] = $params;
}
$messageBody = json_encode($authInfo);
return $this->call($url, null, null, 'POST', $messageBody, '', false);
return $this->call($url, [], null, 'POST', $messageBody, '', false);
}

/**
Expand Down Expand Up @@ -461,7 +501,7 @@ protected function createQSFromArray($params)
*/
protected function call(
$baseUrl,
$headerParams,
$headerParams = [],
$params = [],
$method = 'GET',
$message = null,
Expand All @@ -473,27 +513,13 @@ protected function call(
$queryString = implode('&', $queryParameters);
$this->debug("Querystring to use: $queryString ");
// Build headers
$headers = [
'Accept' => $this->accept,
'Content-Type' => $this->contentType,
'Accept-Encoding' => 'gzip,deflate',
];
$headers = $this->getRequestHeaders($headerParams);
// Debug some info about Guest Access & API Keys used
$this->debug(
'isGuest: ' . ($this->isGuest ? 'true' : 'false')
. ' | APIKey: ' . ($this->apiKey ? substr($this->apiKey, 0, 10) : '-')
. ' | APIKey Guest: ' . ($this->apiKeyGuest ? substr($this->apiKeyGuest, 0, 10) : '-')
);
if (null != $headerParams) {
foreach ($headerParams as $key => $value) {
$headers[$key] = $value;
}
}
if (!empty($this->apiKey)) {
$headers['x-api-key'] = $this->apiKey;
}
if ($this->isGuest && !empty($this->apiKeyGuest)) {
$headers['x-api-key'] = $this->apiKeyGuest;
}
$response = $this->httpRequest(
$baseUrl,
$method,
Expand All @@ -506,6 +532,41 @@ protected function call(
return $this->process($response);
}

/**
* Creat Header Array for Call Function
*
* @param array $headerParams An array (could be empty) of headers to build
*
* @return array Array of Headers to be used in call function
*/
protected function getRequestHeaders(array $headerParams = []): array
{
$headers = [
'Accept' => $this->accept,
'Content-Type' => $this->contentType,
'Accept-Encoding' => 'gzip,deflate',
];
foreach ($headerParams as $key => $value) {
$headers[$key] = $value;
}
if (!empty($this->apiKey)) {
$headers['x-api-key'] = $this->apiKey;
}
if ($this->isGuest && !empty($this->apiKeyGuest)) {
$headers['x-api-key'] = $this->apiKeyGuest;
}
if ($this->sendUserIp) {
$headers['x-eis-enduser-ip-address'] = $this->ipToReport ?? '-';
$headers['x-eis-enduser-user-agent'] = $this->userAgent ?? 'No user agent';
$headers['x-eis-vendor'] = 'VuFind';
if (!empty($this->reportVendorVersion)) {
$headers['x-eis-vendor-version'] = $this->reportVendorVersion;
}
}

return $headers;
}

/**
* Process EDS API response message
*
Expand Down

0 comments on commit 7eec896

Please sign in to comment.