Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancements to getAuthenticatedURL (allow response override headers, fixes for clock drift on short-expiry links) #25

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 44 additions & 6 deletions S3.php
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,20 @@ public static function deleteObject($bucket, $uri)
}


/**
* Get the current system time on AWS servers, needed to check for sync issues
* between local clock and AWS system clock.
*
* @return int
*/
public static function getAWSSystemTime()
{
$rest = new S3Request('HEAD');
$rest = $rest->getResponse();
return $rest->headers['server-time'];
}


/**
* Get a query string authenticated URL
*
Expand All @@ -960,16 +974,38 @@ public static function deleteObject($bucket, $uri)
* @param integer $lifetime Lifetime in seconds
* @param boolean $hostBucket Use the bucket name as the hostname
* @param boolean $https Use HTTPS ($hostBucket should be false for SSL verification)
* @param array $headers An associative array of headers/values to override in the response see:
* http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectGET.html
* @return string
*/
public static function getAuthenticatedURL($bucket, $uri, $lifetime, $hostBucket = false, $https = false)
public static function getAuthenticatedURL($bucket, $uri, $lifetime, $hostBucket = false, $https = false, $headers = false)
{
$expires = time() + $lifetime;
if ($lifetime < 180) {
/**
* Many shared servers are still running cron+rdate for clock sync, and drift can add up to
* a significant difference between system clock and AWS time. So for requests under 3 minutes
* total we get the clock time from AWS to prepare an expiration time, rather than system time.
*/
$expires = self::getAWSSystemTime() + $lifetime;
} else {
$expires = time() + $lifetime;
}
$uri = str_replace(array('%2F', '%2B'), array('/', '+'), rawurlencode($uri)); // URI should be encoded (thanks Sean O'Dea)
return sprintf(($https ? 'https' : 'http').'://%s/%s?AWSAccessKeyId=%s&Expires=%u&Signature=%s',
// $hostBucket ? $bucket : $bucket.'.s3.amazonaws.com', $uri, self::$__accessKey, $expires,
$hostBucket ? $bucket : 's3.amazonaws.com/'.$bucket, $uri, self::$__accessKey, $expires,
urlencode(self::__getHash("GET\n\n\n{$expires}\n/{$bucket}/{$uri}")));

$finalUrl = sprintf(($https ? 'https' : 'http').'://%s/%s?',
$hostBucket ? $bucket : $bucket.'.s3.amazonaws.com', $uri);
$requestToSign = "GET\n\n\n{$expires}\n/{$bucket}/{$uri}";
if (is_array($headers)) {
ksort($headers); // AMZ servers reject signatures if headers are not in alphabetical order
$appendString = '?';
foreach ($headers as $header => $value) {
$finalUrl .= $header . '=' . urlencode($value) . '&';
$requestToSign .= $appendString . $header . '=' . $value;
$appendString = '&';
}
}
$finalUrl .= 'AWSAccessKeyId=' . self::$__accessKey . '&Expires=' . $expires . '&Signature=' . urlencode(self::__getHash($requestToSign));
return $finalUrl;
}


Expand Down Expand Up @@ -1945,6 +1981,8 @@ private function __responseHeaderCallback(&$curl, &$data)
$this->response->headers['size'] = (int)$value;
elseif ($header == 'Content-Type')
$this->response->headers['type'] = $value;
elseif ($header == 'Date')
$this->response->headers['server-time'] = strtotime($value);
elseif ($header == 'ETag')
$this->response->headers['hash'] = $value{0} == '"' ? substr($value, 1, -1) : $value;
elseif (preg_match('/^x-amz-meta-.*$/', $header))
Expand Down