I googled for a solution to share a private S3 object using AWS SDK for PHP 2.
I can only find the solution for .Net, Java, and Visual Studio.
http://docs.aws.amazon.com/AmazonS3/latest/dev/ShareObjectPreSignedURL.html
I also want to generate this presigned url for just 15 minutes of expiry time.
Currently, there is a way to use the latest AWS SDK for PHP 2 to accomplish the above.
Read this.
This link will show you 2 ways to do this.
Most common way is:
$signedUrl = $client->getObjectUrl($bucket, 'data.txt', '+15 minutes');
The second way is to use the command object method which is reproduced below.
// Get a command object from the client and pass in any options
// available in the GetObject command (e.g. ResponseContentDisposition)
$command = $client->getCommand('GetObject', array(
'Bucket' => $bucket,
'Key' => 'data.txt',
'ResponseContentDisposition' => 'attachment; filename="data.txt"'
));
// Create a signed URL from the command object that will last for
// 15 minutes from the current time
$signedUrl = $command->createPresignedUrl('+15 minutes');
$signedUrl will give you a string that looks something like this:
https://bucketname.s3.amazonaws.com/keytothefile.ext?AWSAccessKeyId=AASDASDFDFGTSSYCQ&Expires=1380861181&Signature=eD6qtV81278nmashdkp0huURXc%3D
Related
As I understood, I have 2 ways of sending push notification to Apple APN : certificate-based, and token-based. I chose token-based
Apple guide says we need to create a token and refresh it every hour at least. So, i created a cron job that refreshes this token every hour and put it in a file on my server. Another cron job reads this token to send new pending push notification every second.
The problem comes in the refresh_token job, that I launch every hour. I use this librairy to create the JWT : https://web-token.spomky-labs.com/v/v2.x/components/signed-tokens-jws/jws-creation
Here is my code ( i just followed the guide I've just given as link) :
$algorithmManager = AlgorithmManager::create([
new ES256()
]);
// Our key.
$jwk = new JWK([
'kty' => 'EC', // *** PROBLEM HERE ***
'k' => $keyFile
]);
// The JSON Converter.
$jsonConverter = new StandardConverter();
// We instantiate our JWS Builder.
$jwsBuilder = new JWSBuilder(
$jsonConverter,
$algorithmManager
);
// The payload we want to sign. The payload MUST be a string hence we use our JSON Converter.
$payload = $jsonConverter->encode([
'iat' => time(),
'nbf' => time(),
'exp' => time() + 3600,
'iss' => APPLE_TEAM_ID
]);
$jws = $jwsBuilder
->create()
->withPayload($payload)
->addSignature($jwk, /* with header: */['kid' => APPLE_KEY_NAME, 'alg' => 'ES256'])
->build();
This code throws an exception at ->build(); function, at the end. It says that x, y and crv parameters are not specified in the key. These parameters seem to be related to the algorithm (ES256), because when I choose the alg provided in the JWT guide, they dont ask me for these parameters.
Though, Apple didn't provide any of these informations about the key they gave me on their website. Here is their guide : https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/establishing_a_token-based_connection_to_apns
The way you load the key is not correct. The key in the guide corresponds to an Octet key, not an EC one. A JWK EC key should look like the example showed in the RFC7517 section 3 (with crv, x and y parameters).
You have to convert the key file you received from Apple services into a JWK EC key.
As you already have PHP on your platform, I recommend the use of the CLI tool:
curl -OL https://github.com/web-token/jwt-app/raw/gh-pages/jose.phar
curl -OL https://github.com/web-token/jwt-app/raw/gh-pages/jose.phar.pubkey
chmod +x jose.phar
# Replace `/path/to/you/private/key/file.p8` with the actual path to your private key
./jose.phar key:load:key /path/to/you/private/key/file.p8
rm ./jose.phar
rm ./jose.phar.pubkey
You should get something like {"kty":"EC","crv":"P-256","d":"…","x":"…","y":"…"}.
The JWK can be loaded using the following line of code:
$jwk = JWK::createFromJson('{"kty":"EC","crv":"P-256","d":"…","x":"…","y":"…"}');
I am trying to make a PutObject presigned request using the AWS S3 PHP SDK.
I have gotten the request to work but now I only want to allow my users to be able to only upload video files.I have tried a lot of combinations and searched a lot but I could not get it to work.
Here is the sample code I use:
$cmd = $this->s3client->getCommand('PutObject', [
'Bucket' => 'myBucket',
'Key' => 'inputs/' . $movie->getId(),
'ACL' => 'private',
'Conditions' => ['Starts-With', '$Content-Type', 'video/'], // I have tried other combinations but it seems to not work
]);
$request = $this->s3client->createPresignedRequest($cmd, '+30 minutes');
$movie->setSignedUrl((string)$request->getUri());
The signed url generated does never include the Content-Type in the X-Amz-SignedHeaders query parameter, only the host is included.
The putObject() request has no documented Conditions key.
You appear to be confusing S3's PUT upload interface with the pre-signed POST capability, which supports policy document conditions like ['Starts-With', '$Content-Type', 'video/'],
PUT does not support "starts with". It requires the exact Content-Type and the key for this (which should result in the header appearing in the X-Amz-SignedHeaders query string parameter) is simply ContentType. It goes in the outer parameters array, just like Bucket and Key.
But if you want to support multiple content types without knowing the specific type in advance, you need to use POST uploads.
I am trying to generate a URL that other system can use to upload a file to my S3 bucket. I looked over the documentation and similar issues, however, I cannot find the right solution.
I have tried creating the PreSigned URL multiple ways ($this->s3 is a reference so S3Client):
1:
$signedUrl = $this->s3->getCommand(
'PutObject',
array(
'Bucket' => $this->bucket,
'Key' => 'recording_test.mp3',
'Body' => ''
)
)->createPresignedUrl('+2 hours');
2:
$command = $this->s3->getCommand('PutObject', array('Bucket' => $this->bucket, 'Key' => 'recording_test3.mp3', 'Body' => ''));
$request = $command->prepare();
$signedUrl = $this->s3->createPresignedUrl($request, '+2 hours');
When trying to simply access the URL I get following error:
The request signature we calculated does not match the signature you provided. Check your key and signing method.
I have also tried it this way:
$key = 'recording_test2.mp3';
$url = $this->bucket.'/'.$key;
$request = $this->s3->put($url);
$signedUrl = $this->s3->createPresignedUrl($request, '+2 hours');
However, this generates a URL in format of s3.amazonaws.com/bucket/key..., which yields an error about invalid endpoint, and that endpoint in format bucket.s3.amazonaws.com/key... should be used. Manually changing the URL gives the same invalid signature error as per above.
I cannot see what am I doing wrong or any other way to generate the PreSigned URL?
Thanks for help
When creating your signed URL, you should not use the Body key. The signed URL generated includes a signature of the body to be uploaded. Then, when you try upload a file using the URL, it can't match the empty string you are using to the content of the file.
Also, if you are using Curl to put the file, I recommend using \CURLFile, and remember to use curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); for your PUT request.
Ok, so I have tried several methods of getting a file to upload to my S3 account. Finally found myself getting somewhere and BOOM - confusing documentation and strange error messages which appear to contradict themselves.
Ok, to start with I am not using composer or anything like that, I am doing this the old fashioned way:
require '/path/to/aws-autoload.php';
Now this loads correctly, and I have trimmed down the autoload to only use Common and S3 classes - don't need everything!
Next up I load the S3 Client and Credentials:
use Aws\S3\S3Client;
use Aws\Common\Credentials\Credentials;
Now the code to start the magic happening:
$file = '/path/to/' . $filename;
$credentials = new Credentials('KEY', 'SECRET KEY');
$s3 = S3Client::factory(array('credentials' => $credentials));
try{
$s3->putObject(array(
'Bucket' => 'sub.domain.com.s3.amazonaws.com',
'Body' => fopen($file, 'r'),
'ACL' => 'public-read',
));
} catch (S3Exception $e) {
$result = array(
'ok' => false,
'descr' => 'There was an error uploading the file to S3 : ' . $filename
);
}
The issue I seem to be having is with the 'Bucket' itself.
When I format the Bucket as sub.domain.com I get the following message back from the AWS API:
AWS Error Message: The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint: "sub.domain.com.s3.amazonaws.com".
Now when I change the 'Bucket' to match the above like so : sub.domain.com.s3.amazonaws.com
I get the following error message :
AWS Error Message: The specified bucket does not exist
Am I doing something wrong? Is something missing? Unfortunately the AWS Documentation is not quite up to scratch. The API seems to be contradicting itself at the moment. I know all the keys are correct, and all permissions are correct. It went from 301 - Redirect to 404 - Not Found from its own advice.
Any help/advise would be greatly appreciated. I feel like I am going in circles a little here!
Here are some things to double check.
If the bucket was created in a certain region (e.g., us-west-2), instantiate the S3Client with that region [..., 'region' => 'us-west-2', ...].
The 'Bucket' parameter should be set to exactly what your bucket is named (e.g., if your bucket is named "sub.domain.com", then the 'Bucket' parameter should be set to 'sub.domain.com'). Do not include the region or "s3.amazonaws.com" in the 'Bucket' parameter (i.e., bucket != endpoint). The SDK automatically determines the endpoint (based on the client's region and provided bucket name) and adjusts the URL for path-style if needed.
The PutObject operation requires the 'Key' parameter, which is not present in the code sample above.
I'm trying to use the AWS SDK for PHP to programatically upload a file to a bucket that's set to be a static website in the S3 Console.
The bucket is named foo.ourdomain.com and is hosted in eu-west. I'm using the following code to try and test if I can upload a file:
$client = \Aws\S3\S3Client::factory(array('key' => bla, 'secret' => bla));
$client->upload('foo.ourdomain.com', 'test.txt', 'hello world', 'public-read');
This is pretty much like it is in the examples, however, I received the following exception:
PHP Fatal error: Uncaught Aws\S3\Exception\PermanentRedirectException: AWS Error Code: PermanentRedirect, Status Code: 301, AWS Request ID: -, AWS Error Type: client, AWS Error Message: The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint: "foo.ourdomain.com.s3.amazonaws.com"., User-Agent: aws-sdk-php2/2.4.8 Guzzle/3.7.4 curl/7.22.0 PHP/5.3.10-1ubuntu3.8
At this point I was surprised as there's no mention of this in the manual for the S3 SDK. But okay, I found a method setEndpoint and adjusted the code to:
$client = \Aws\S3\S3Client::factory(array('key' => bla, 'secret' => bla));
$client->setEndpoint('foo.ourdomain.com.s3.amazonaws.com');
$client->upload('foo.ourdomain.com', 'test.txt', 'hello world', 'public-read');
I assumed that'd work, but I'm getting the exact same error. I've doublechecked and the endpoint mentioned in the exception byte-for-byte matches the one I'm setting in the second line.
I've also tried using foo.ourdomain.com.s3-website-eu-west-1.amazonaws.com as the endpoint (this is the host our CNAME points to as per the S3 consoles instructions). Didn't work either.
I must be missing something, but I can't find it anywhere. Perhaps buckets set to 'static website' behave differently in a way which is not currently supported by the SDK? If so, I can't find mention of it in the docs nor in the management console.
Got it. The solution was to change the initialisation of the client to:
$client = \Aws\S3\S3Client::factory(array(
'key' => bla,
'secret' => bla,
'region' => 'eu-west-1'
));
I.e. rather than specify an endpoint I needed to explicitly set the region in the options array. I guess the example code happens to use whatever the default region is.
If you don't want to initialize the client to a specific region and/or you'll need to work with different regions, I have been successful in using the getBucketLocation/setRegion set of calls as follows:
// Bucket location is fetched
$m_bucketLocation = $I_s3->getBucketLocation(array(
'Bucket' => $s_backupBucket,
));
// Bucket location is specified before operation is made
$I_s3->setRegion($m_bucketLocation['Location']);
I have one extra call, but solved my issue without the need to intervene on the factory.