Skip to content

Commit

Permalink
Merge branch 'cloudfront'
Browse files Browse the repository at this point in the history
  • Loading branch information
zerocrates committed Mar 7, 2024
2 parents 089f44f + 11d0f26 commit 6e1e289
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 1 deletion.
2 changes: 1 addition & 1 deletion application/libraries/Omeka/Storage/Adapter/ZendS3.php
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ private function _getObjectName($path)
*
* @return int
*/
private function _getExpiration()
protected function _getExpiration()
{
$expiration = (int) @$this->_options[self::EXPIRATION_OPTION];
return $expiration > 0 ? $expiration : 0;
Expand Down
128 changes: 128 additions & 0 deletions application/libraries/Omeka/Storage/Adapter/ZendS3Cloudfront.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php
/**
* Cloud storage adapter for Amazon S3, serving through CloudFront.
*
* @package Omeka\Storage\Adapter
*/
class Omeka_Storage_Adapter_ZendS3Cloudfront extends Omeka_Storage_Adapter_ZendS3
{
const CLOUDFRONT_DOMAIN = 'cloudfrontDomain';
const CLOUDFRONT_KEY_ID = 'cloudfrontKeyId';
const CLOUDFRONT_KEY_PATH = 'cloudfrontKeyPath';
const CLOUDFRONT_KEY_PASSPHRASE = 'cloudfrontKeyPassphrase';

/**
* @var string
*/
private $_cloudfrontDomain;

/**
* @var string
*/
private $_cloudfrontKeyId;

/**
* @var string
*/
private $_cloudfrontKeyPath;

/**
* @var string
*/
private $_cloudfrontKeyPassphrase = '';

/**
* Set options for the storage adapter.
*
* @param array $options
*/
public function __construct(array $options = array())
{
parent::__construct($options);

if (isset($options[self::CLOUDFRONT_DOMAIN])) {
$this->_cloudfrontDomain = $options[self::CLOUDFRONT_DOMAIN];
} else {
throw new Omeka_Storage_Exception('The cloudfrontDomain storage option is required');
}

if (isset($options[self::CLOUDFRONT_KEY_ID])) {
$this->_cloudfrontKeyId = $options[self::CLOUDFRONT_KEY_ID];
}

if (isset($options[self::CLOUDFRONT_KEY_PATH])) {
$this->_cloudfrontKeyPath = $options[self::CLOUDFRONT_KEY_PATH];
}

if (isset($options[self::CLOUDFRONT_KEY_PASSPHRASE])) {
$this->_cloudfrontKeyPassphrase = $options[self::CLOUDFRONT_KEY_PASSPHRASE];
}

if ($this->_getExpiration() && !($this->_cloudfrontKeyId && $this->_cloudfrontKeyPath)) {
throw new Omeka_Storage_Exception('The cloudfrontKeyId and cloudfrontKeyPath storage options are required when enabling expiration');
}
}

/**
* Get a URI to a stored file from CloudFront
*/
public function getUri($path)
{
$object = str_replace('%2F', '/', rawurlencode($path));
$uri = "https://{$this->_cloudfrontDomain}/{$object}";

if ($expiration = $this->_getExpiration()) {
$timestamp = time();
$expirationSeconds = $expiration * 60;
$expires = $timestamp + $expirationSeconds;
// "Chunk" expirations to allow browser caching
$expires = $expires + $expirationSeconds - ($expires % $expirationSeconds);

$statement = json_encode(array(
'Statement' => array(
array(
'Resource' => $uri,
'Condition' => array(
'DateLessThan' => array(
'AWS:EpochTime' => $expires,
),
),
),
),
), JSON_UNESCAPED_SLASHES);

$key = openssl_pkey_get_private('file://' . $this->_cloudfrontKeyPath, $this->_cloudfrontKeyPassphrase);
if (!$key) {
throw new Omeka_Storage_Exception("Unable to load key for Cloudfront\n\n" . $this->_getOpensslErrors());
}

$result = openssl_sign($statement, $signature, $key);
if (!$result) {
throw new Omeka_Storage_Exception("Failed to create Cloudfront URI signature\n\n" . $this->_getOpensslErrors());
}

unset($key);

$signatureParam = strtr(base64_encode($signature), '+=/', '-_~');

$query['Expires'] = $expires;
$query['Signature'] = $signatureParam;
$query['Key-Pair-Id'] = $this->_cloudfrontKeyId;

$queryString = http_build_query($query);

$uri .= "?$queryString";
}

return $uri;
}

private function _getOpensslErrors()
{
$errors = array();
while (($error = openssl_error_string()) !== false) {
$errors[] = $error;
}
return implode("\n", $errors);
}
}

0 comments on commit 6e1e289

Please sign in to comment.