amazon-s3-php-class
amazon-s3-php-class copied to clipboard
Support for IAM credentials
It would be great if this library supported the use of IAM credentials (as opposed to an explicit access key / secret key pair). I'm not entirely sure how feasible it would be to implement this, although from my experience it would generally involve the following:
- Curl
http://169.254.169.254/latest/meta-data/iam/security-credentials/
to get the relevant IAM role. - Curl
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/<ROLE>/
to get the S3 credentials (access key, secret key and token). - Use the credentials from step 2.
I needed to add support for this for a project so here is the patch in case anyone also needs IAM role support.
Patch is under public domain.
diff --git a/components/S3Adapter/S3.php b/components/S3Adapter/S3.php
index 0b1564b..af4d65c 100644
--- a/components/S3Adapter/S3.php
+++ b/components/S3Adapter/S3.php
@@ -127,6 +127,15 @@ class S3
*/
private static $__timeOffset = 0;
+ /**
+ * Security Token used when accessing from an IAM role
+ *
+ * @var string
+ * @access public
+ * @static
+ */
+ public static $securityToken = null;
+
/**
* SSL client key
*
@@ -180,14 +189,16 @@ class S3
* @param string $secretKey Secret key
* @param boolean $useSSL Enable SSL
* @param string $endpoint Amazon URI
+ * @param string $securityToken For use with IAM roles
* @return void
*/
- public function __construct($accessKey = null, $secretKey = null, $useSSL = false, $endpoint = 's3.amazonaws.com')
+ public function __construct($accessKey = null, $secretKey = null, $useSSL = false, $endpoint = 's3.amazonaws.com', $securityToken = null)
{
if ($accessKey !== null && $secretKey !== null)
self::setAuth($accessKey, $secretKey);
self::$useSSL = $useSSL;
self::$endpoint = $endpoint;
+ self::$securityToken = $securityToken;
}
@@ -364,7 +375,7 @@ class S3
*/
public static function listBuckets($detailed = false)
{
- $rest = new S3Request('GET', '', '', self::$endpoint);
+ $rest = new S3Request('GET', '', '', self::$endpoint, self::$securityToken);
$rest = $rest->getResponse();
if ($rest->error === false && $rest->code !== 200)
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
@@ -410,7 +421,7 @@ class S3
*/
public static function getBucket($bucket, $prefix = null, $marker = null, $maxKeys = null, $delimiter = null, $returnCommonPrefixes = false)
{
- $rest = new S3Request('GET', $bucket, '', self::$endpoint);
+ $rest = new S3Request('GET', $bucket, '', self::$endpoint, self::$securityToken);
if ($maxKeys == 0) $maxKeys = null;
if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix);
if ($marker !== null && $marker !== '') $rest->setParameter('marker', $marker);
@@ -455,7 +466,7 @@ class S3
if ($maxKeys == null && $nextMarker !== null && (string)$response->body->IsTruncated == 'true')
do
{
- $rest = new S3Request('GET', $bucket, '', self::$endpoint);
+ $rest = new S3Request('GET', $bucket, '', self::$endpoint, self::$securityToken);
if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix);
$rest->setParameter('marker', $nextMarker);
if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter);
@@ -497,7 +508,7 @@ class S3
*/
public static function putBucket($bucket, $acl = self::ACL_PRIVATE, $location = false)
{
- $rest = new S3Request('PUT', $bucket, '', self::$endpoint);
+ $rest = new S3Request('PUT', $bucket, '', self::$endpoint, self::$securityToken);
$rest->setAmzHeader('x-amz-acl', $acl);
if ($location !== false)
@@ -533,7 +544,7 @@ class S3
*/
public static function deleteBucket($bucket)
{
- $rest = new S3Request('DELETE', $bucket, '', self::$endpoint);
+ $rest = new S3Request('DELETE', $bucket, '', self::$endpoint, self::$securityToken);
$rest = $rest->getResponse();
if ($rest->error === false && $rest->code !== 204)
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
@@ -615,7 +626,7 @@ class S3
public static function putObject($input, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array(), $storageClass = self::STORAGE_CLASS_STANDARD, $serverSideEncryption = self::SSE_NONE)
{
if ($input === false) return false;
- $rest = new S3Request('PUT', $bucket, $uri, self::$endpoint);
+ $rest = new S3Request('PUT', $bucket, $uri, self::$endpoint, self::$securityToken);
if (!is_array($input)) $input = array(
'data' => $input, 'size' => strlen($input),
@@ -731,7 +742,7 @@ class S3
*/
public static function getObject($bucket, $uri, $saveTo = false)
{
- $rest = new S3Request('GET', $bucket, $uri, self::$endpoint);
+ $rest = new S3Request('GET', $bucket, $uri, self::$endpoint, self::$securityToken);
if ($saveTo !== false)
{
if (is_resource($saveTo))
@@ -766,7 +777,7 @@ class S3
*/
public static function getObjectInfo($bucket, $uri, $returnInfo = true)
{
- $rest = new S3Request('HEAD', $bucket, $uri, self::$endpoint);
+ $rest = new S3Request('HEAD', $bucket, $uri, self::$endpoint, self::$securityToken);
$rest = $rest->getResponse();
if ($rest->error === false && ($rest->code !== 200 && $rest->code !== 404))
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
@@ -795,7 +806,7 @@ class S3
*/
public static function copyObject($srcBucket, $srcUri, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array(), $storageClass = self::STORAGE_CLASS_STANDARD)
{
- $rest = new S3Request('PUT', $bucket, $uri, self::$endpoint);
+ $rest = new S3Request('PUT', $bucket, $uri, self::$endpoint, self::$securityToken);
$rest->setHeader('Content-Length', 0);
foreach ($requestHeaders as $h => $v) $rest->setHeader($h, $v);
foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v);
@@ -831,7 +842,7 @@ class S3
*/
public static function setBucketRedirect($bucket = NULL, $location = NULL)
{
- $rest = new S3Request('PUT', $bucket, '', self::$endpoint);
+ $rest = new S3Request('PUT', $bucket, '', self::$endpoint, self::$securityToken);
if( empty($bucket) || empty($location) ) {
self::__triggerError("S3::setBucketRedirect({$bucket}, {$location}): Empty parameter.", __FILE__, __LINE__);
@@ -908,7 +919,7 @@ class S3
}
$dom->appendChild($bucketLoggingStatus);
- $rest = new S3Request('PUT', $bucket, '', self::$endpoint);
+ $rest = new S3Request('PUT', $bucket, '', self::$endpoint, self::$securityToken);
$rest->setParameter('logging', null);
$rest->data = $dom->saveXML();
$rest->size = strlen($rest->data);
@@ -937,7 +948,7 @@ class S3
*/
public static function getBucketLogging($bucket)
{
- $rest = new S3Request('GET', $bucket, '', self::$endpoint);
+ $rest = new S3Request('GET', $bucket, '', self::$endpoint, self::$securityToken);
$rest->setParameter('logging', null);
$rest = $rest->getResponse();
if ($rest->error === false && $rest->code !== 200)
@@ -976,7 +987,7 @@ class S3
*/
public static function getBucketLocation($bucket)
{
- $rest = new S3Request('GET', $bucket, '', self::$endpoint);
+ $rest = new S3Request('GET', $bucket, '', self::$endpoint, self::$securityToken);
$rest->setParameter('location', null);
$rest = $rest->getResponse();
if ($rest->error === false && $rest->code !== 200)
@@ -1040,7 +1051,7 @@ class S3
$accessControlPolicy->appendChild($accessControlList);
$dom->appendChild($accessControlPolicy);
- $rest = new S3Request('PUT', $bucket, $uri, self::$endpoint);
+ $rest = new S3Request('PUT', $bucket, $uri, self::$endpoint, self::$securityToken);
$rest->setParameter('acl', null);
$rest->data = $dom->saveXML();
$rest->size = strlen($rest->data);
@@ -1067,7 +1078,7 @@ class S3
*/
public static function getAccessControlPolicy($bucket, $uri = '')
{
- $rest = new S3Request('GET', $bucket, $uri, self::$endpoint);
+ $rest = new S3Request('GET', $bucket, $uri, self::$endpoint, self::$securityToken);
$rest->setParameter('acl', null);
$rest = $rest->getResponse();
if ($rest->error === false && $rest->code !== 200)
@@ -1128,7 +1139,7 @@ class S3
*/
public static function deleteObject($bucket, $uri)
{
- $rest = new S3Request('DELETE', $bucket, $uri, self::$endpoint);
+ $rest = new S3Request('DELETE', $bucket, $uri, self::$endpoint, self::$securityToken);
$rest = $rest->getResponse();
if ($rest->error === false && $rest->code !== 204)
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
@@ -1291,7 +1302,7 @@ class S3
$useSSL = self::$useSSL;
self::$useSSL = true; // CloudFront requires SSL
- $rest = new S3Request('POST', '', '2010-11-01/distribution', 'cloudfront.amazonaws.com');
+ $rest = new S3Request('POST', '', '2010-11-01/distribution', 'cloudfront.amazonaws.com', self::$securityToken);
$rest->data = self::__getCloudFrontDistributionConfigXML(
$bucket.'.s3.amazonaws.com',
$enabled,
@@ -1339,7 +1350,7 @@ class S3
$useSSL = self::$useSSL;
self::$useSSL = true; // CloudFront requires SSL
- $rest = new S3Request('GET', '', '2010-11-01/distribution/'.$distributionId, 'cloudfront.amazonaws.com');
+ $rest = new S3Request('GET', '', '2010-11-01/distribution/'.$distributionId, 'cloudfront.amazonaws.com', self::$securityToken);
$rest = self::__getCloudFrontResponse($rest);
self::$useSSL = $useSSL;
@@ -1381,7 +1392,7 @@ class S3
$useSSL = self::$useSSL;
self::$useSSL = true; // CloudFront requires SSL
- $rest = new S3Request('PUT', '', '2010-11-01/distribution/'.$dist['id'].'/config', 'cloudfront.amazonaws.com');
+ $rest = new S3Request('PUT', '', '2010-11-01/distribution/'.$dist['id'].'/config', 'cloudfront.amazonaws.com', self::$securityToken);
$rest->data = self::__getCloudFrontDistributionConfigXML(
$dist['origin'],
$dist['enabled'],
@@ -1433,7 +1444,7 @@ class S3
$useSSL = self::$useSSL;
self::$useSSL = true; // CloudFront requires SSL
- $rest = new S3Request('DELETE', '', '2008-06-30/distribution/'.$dist['id'], 'cloudfront.amazonaws.com');
+ $rest = new S3Request('DELETE', '', '2008-06-30/distribution/'.$dist['id'], 'cloudfront.amazonaws.com', self::$securityToken);
$rest->setHeader('If-Match', $dist['hash']);
$rest = self::__getCloudFrontResponse($rest);
@@ -1467,7 +1478,7 @@ class S3
$useSSL = self::$useSSL;
self::$useSSL = true; // CloudFront requires SSL
- $rest = new S3Request('GET', '', '2010-11-01/distribution', 'cloudfront.amazonaws.com');
+ $rest = new S3Request('GET', '', '2010-11-01/distribution', 'cloudfront.amazonaws.com', self::$securityToken);
$rest = self::__getCloudFrontResponse($rest);
self::$useSSL = $useSSL;
@@ -1511,7 +1522,7 @@ class S3
}
self::$useSSL = true; // CloudFront requires SSL
- $rest = new S3Request('GET', '', '2010-11-01/origin-access-identity/cloudfront', 'cloudfront.amazonaws.com');
+ $rest = new S3Request('GET', '', '2010-11-01/origin-access-identity/cloudfront', 'cloudfront.amazonaws.com', self::$securityToken);
$rest = self::__getCloudFrontResponse($rest);
$useSSL = self::$useSSL;
@@ -1556,7 +1567,7 @@ class S3
$useSSL = self::$useSSL;
self::$useSSL = true; // CloudFront requires SSL
- $rest = new S3Request('POST', '', '2010-08-01/distribution/'.$distributionId.'/invalidation', 'cloudfront.amazonaws.com');
+ $rest = new S3Request('POST', '', '2010-08-01/distribution/'.$distributionId.'/invalidation', 'cloudfront.amazonaws.com', self::$securityToken);
$rest->data = self::__getCloudFrontInvalidationBatchXML($paths, (string)microtime(true));
$rest->size = strlen($rest->data);
$rest = self::__getCloudFrontResponse($rest);
@@ -1623,7 +1634,7 @@ class S3
$useSSL = self::$useSSL;
self::$useSSL = true; // CloudFront requires SSL
- $rest = new S3Request('GET', '', '2010-11-01/distribution/'.$distributionId.'/invalidation', 'cloudfront.amazonaws.com');
+ $rest = new S3Request('GET', '', '2010-11-01/distribution/'.$distributionId.'/invalidation', 'cloudfront.amazonaws.com', self::$securityToken);
$rest = self::__getCloudFrontResponse($rest);
self::$useSSL = $useSSL;
@@ -1998,9 +2009,10 @@ final class S3Request
* @param string $bucket Bucket name
* @param string $uri Object URI
* @param string $endpoint AWS endpoint URI
+ * @param string $securityToken When using IAM roles
* @return mixed
*/
- function __construct($verb, $bucket = '', $uri = '', $endpoint = 's3.amazonaws.com')
+ function __construct($verb, $bucket = '', $uri = '', $endpoint = 's3.amazonaws.com', $securityToken=null)
{
$this->endpoint = $endpoint;
@@ -2035,6 +2047,10 @@ final class S3Request
$this->resource = $this->uri;
}
+ if ($securityToken !== null)
+ {
+ $this->amzHeaders['x-amz-security-token'] = $securityToken;
+ }
$this->headers['Date'] = gmdate('D, d M Y H:i:s T');
$this->response = new STDClass;
@andrewfenn Thanks! That simple patch was very helpful for something I'm working on.