Created
October 16, 2017 01:26
-
-
Save mmollick/1d760a7b9beae48cde4f3df8b95e0ad8 to your computer and use it in GitHub Desktop.
Class to generate V4 style signing keys for direct browser upload to S3 bucket
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php namespace NeverBounce\Repositories; | |
| use Carbon\Carbon; | |
| class DirectS3Upload { | |
| /** | |
| * Supported regions | |
| * @var array | |
| */ | |
| protected $supported = [ | |
| 'default' => [ | |
| 'human' => 'US Standard', // Human friendly name | |
| 'region' => 'us-west-2', // AWS region code | |
| 'bucket' => 'my-default-bucket', // S3 bucket name | |
| ], | |
| 'eu-frankfurt' => [ | |
| 'human' => 'EU Frankfurt', | |
| 'region' => 'eu-central-1', | |
| 'bucket' => 'my-eu-bucket' | |
| ], | |
| ]; | |
| /** | |
| * Stores region short string | |
| * @var bool|void | |
| */ | |
| protected $region; | |
| /** | |
| * S3Repository constructor. | |
| * @param \User $user | |
| */ | |
| public function __construct(\User $user) | |
| { | |
| $this->region = $user->getMeta('data_region', 'default'); | |
| } | |
| /** | |
| * Returns human friendly name | |
| * @return mixed | |
| */ | |
| public function getHuman() | |
| { | |
| return $this->supported[$this->region]['human']; | |
| } | |
| /** | |
| * Returns AWS region code | |
| * @return mixed | |
| */ | |
| public function getRegionCode() | |
| { | |
| return $this->supported[$this->region]['region']; | |
| } | |
| /** | |
| * Returns S3 bucket name | |
| * @return mixed | |
| */ | |
| public function getBucketName() | |
| { | |
| return $this->supported[$this->region]['bucket']; | |
| } | |
| public function getDataRegion() | |
| { | |
| return $this->region; | |
| } | |
| /** | |
| * Returns the url for accessing and uploading to the bucket | |
| * @return string | |
| */ | |
| public function getUrl() | |
| { | |
| return sprintf("https://%s.s3.amazonaws.com/", $this->getBucketName()); | |
| } | |
| /** | |
| * Get all the necessary details to directly upload a private file to S3 | |
| * asynchronously with JavaScript using the Signature V4. | |
| * | |
| * @return array ['url', 'inputs'] the forms url to s3 and any inputs the form will need. | |
| */ | |
| public function getS3Details() { | |
| // Options and Settings | |
| $awsKey = env('aws.key'); | |
| $awsSecret = env('aws.secret'); | |
| $acl = 'private'; | |
| $algorithm = "AWS4-HMAC-SHA256"; | |
| $service = "s3"; | |
| $date = gmdate('Ymd\THis\Z'); | |
| $shortDate = gmdate("Ymd"); | |
| $requestType = "aws4_request"; | |
| $expires = "86400"; // 24 Hours | |
| $successStatus = "201"; | |
| // Step 1: Generate the Scope | |
| $scope = [ | |
| $awsKey, | |
| $shortDate, | |
| $this->getRegionCode(), | |
| $service, | |
| $requestType | |
| ]; | |
| $credentials = implode('/', $scope); | |
| // Step 2: Making a Base64 Policy | |
| $policy = [ | |
| 'expiration' => gmdate('Y-m-d\TG:i:s\Z', strtotime('+6 hours')), | |
| 'conditions' => [ | |
| ['bucket' => $this->getBucketName()], | |
| ['acl' => $acl], | |
| ['starts-with', '$key', 'uploads/'], | |
| ['starts-with', '$Content-Type', ''], | |
| ['success_action_status' => $successStatus], | |
| ['x-amz-credential' => $credentials], | |
| ['x-amz-algorithm' => $algorithm], | |
| ['x-amz-date' => $date], | |
| ['x-amz-expires' => $expires], | |
| ] | |
| ]; | |
| $base64Policy = base64_encode(json_encode($policy)); | |
| // Step 3: Signing your Request (Making a Signature) | |
| $dateKey = hash_hmac('sha256', $shortDate, 'AWS4' . $awsSecret, true); | |
| $dateRegionKey = hash_hmac('sha256', $this->getRegionCode(), $dateKey, true); | |
| $dateRegionServiceKey = hash_hmac('sha256', $service, $dateRegionKey, true); | |
| $signingKey = hash_hmac('sha256', $requestType, $dateRegionServiceKey, true); | |
| $signature = hash_hmac('sha256', $base64Policy, $signingKey); | |
| // Step 4: Build form inputs | |
| // This is the data that will get sent with the form to S3 | |
| return [ | |
| 'Content-Type' => '', | |
| 'key' => 'uploads/${filename}', | |
| 'acl' => $acl, | |
| 'success_action_status' => $successStatus, | |
| 'policy' => $base64Policy, | |
| 'X-amz-credential' => $credentials, | |
| 'X-amz-algorithm' => $algorithm, | |
| 'X-amz-date' => $date, | |
| 'X-amz-expires' => $expires, | |
| 'X-amz-signature' => $signature | |
| ]; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment