Directo
A library to generate AWS Signature V4 and supplement form inputs, for direct upload to Amazon S3. Includes a bridge to Laravel as-well., (*1)
Install
$ composer require kfirba/directo
Prerequisites - CORS & Bucket Policy
In order for the upload to succeed and not get denied by Amazon S3 we will need to configure the CORS option and the bucket policy., (*2)
To update the CORS and the bucket policy, log in to your AWS account and go to S3. Now select your bucket and click Properties on the top-right corner. Click on Permissions., (*3)
To update the CORS configuration, click the Edit CORS Configuration button. I recommend the following CORS configuration:, (*4)
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
To update the bucket policy, click the Edit bucket policy button. I recommend the following bucket policy:, (*5)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": "arn:aws:s3:::BUCKET_NAME/*"
}
]
}
Don't forget to change BUCKET_NAME
to your bucket's name., (*6)
Note: If you don't plan to ever upload any file with any other ACL than private
, you may want to omit the "s3:PutObjectAcl"
., (*7)
Laravel Users
If you are a Laravel user, the package contains a bridge for you., (*8)
Update your app.php providers
array and add:, (*9)
Kfirba\Directo\Support\DirectoServiceProvider::class,
Also, update your aliases
array and add:, (*10)
'Directo' => Kfirba\Directo\Support\Facades\Directo::class,
If you need to change some of the default settings, you can either publish the directo.php
config to make the changes global every time you try to generate a signature, or use the convenient setter Directo provides., (*11)
$ php artisan vendor:publish --tag=directo
You should now be able to find a directo.php
file in your config/
directory with all of the default options., (*12)
// You can also change the options on-the-fly
Directo::setOptions(['acl' => 'private'])->inputsAsArray();
The on-the-fly change is useful especially when you may have more than 1 form in your document and one of them may require different options than the other one. For example, a form to upload some private metrics data to S3, you will want to set the acl
to private
and a form to upload a profile picture which you will want to set the acl
to atleast public-read
., (*13)
The package reads your S3 configurations specified in config/filesystems.php
under the disks.s3
array. To change the configs, you can change the following keys in your .env
file:, (*14)
S3_KEY=myS3Key
S3_SECRET=myS3Secret
S3_REGION=eu-central-1
S3_BUCKET=bucket
Laravel then reads these environment variables and set the config in filesystems.disks.s3
which Directo uses., (*15)
Usage
use Kfirba\Directo\Directo;
require_once __DIR__ . "/vendor/autoload.php";
$directo = new Directo('bucket', 'region', 'key', 'secret', $options = []);
Then, using the object we've just made, we can generate the form's url and all the needed hidden inputs., (*16)
<form action="<?php echo $directo->formUrl()?>" method="post" enctype="multipart/form-data">
<?php echo $directo->inputsAsHtml() ?>
<input type="file" name="file">
</form>
Laravel Users Usage
If you are using laravel, there is a Directo
facade you can use. Also, you can type-hint the Directo
class in any auto-resolving class, such as any Controller
and it will be automatically resolved., (*17)
// Facade:
Directo::formUrl();
Directo::inputsAsHtml();
// Type-hinted
// SomeController.php:
use Kfirba\Directo\Directo;
// ...
public function index(Directo $directo)
{
dd($directo->signature());
}
Signing a policy
Sometimes, when using file uploader plugin, such as FineUploader, the plugin will send a policy to the server to be signed. Once the policy has been signed, the plugin will submit the policy and the signature to AWS as authentication key., (*18)
Directo also offers the ability to sign a given policy. The policy should be either a valid json-encoded policy or a base64-encoded policy (which decodes to a valid json-encoded policy)., (*19)
If you already have a Directo
object, you may simply call the sign()
method:, (*20)
$directo->sign($policy);
// =>
// ['policy' => 'base64-encoded policy...', 'signature => 'the signature...']
If you don't have a Directo
object, you may create a new one or use the underlying Signature
object:, (*21)
$signature = new Signature($secret, $region, $policy);
$signature->sign($policy);
// =>
// ['policy' => 'base64-encoded policy...', 'signature => 'the signature...']
The arguments that the Signature
object needs are required for generating the SigningKey
. The Signature
object will parse the given policy and will extract any additional parameters it needs for the SigningKey
., (*22)
If you are a Laravel user, you can use the Directo
facade anywhere:, (*23)
Directo::sign($policy);
// =>
// ['policy' => 'base64-encoded policy...', 'signature => 'the signature...']
The sign()
method returns an array with the following keys:, (*24)
-
policy
- base64 encoded string of the policy.
-
signature
- the signature for the aforementioned policy.
Options
The options availalbe are:, (*25)
Option |
Default |
Description |
success_action_redirect |
The URL to which the client is redirected upon successful upload. Useful if you are not using any kind of AJAX uploading mechanism. |
success_action_status |
201 |
The status code returned to the client upon successful upload if success_action_redirect is not specified. |
acl |
public-read |
The ACL for the uploaded file. Supported: private, public-read, public-read-write, aws-exec-read, authenticated-read, bucket-owner-read, bucket-owner-full-control, log-delivery-write |
default_filename |
${filename} |
The file's name on s3, can be set with JS by changing the input[name="key"]. Leaving this as ${filename} will retain the original file's name. |
max_file_size |
500 |
The maximum file size of an upload in MB. Will refuse with a EntityTooLarge and 400 Bad Request if you exceed this limit. |
expires |
+6 hours |
Request expiration time, specified in relative time format or in seconds. min: 1 (+1 second), max: 604800 (+7 days) |
valid_prefix |
Server will check that the filename starts with this prefix and fail with a AccessDenied 403 if not. |
content_type |
Strictly only allow a single content type, blank will allow all. Will fail with a AccessDenied 403 is this condition is not met. |
additional_inputs |
Any additional inputs to add to the form. This is an array of name => value pairs e.g. ['Content-Disposition' => 'attachment'] |
For example:, (*26)
$directo = new Directo("bucket", "region", "key", "secret", [
'acl' => 'private',
'max_file_size' => 10,
'additional_inputs' => [
'Content-Disposition' => 'attachment'
]
]);
Available Methods
Method |
Description |
signature() |
Get the AWS Signature (V4) for the auto-generated policy. |
policy() |
Get the policy used by the signature. |
sign() |
Sign a given json/base64-encoded policies. Useful if you are using a plugin such as FineUploader which needs an endpoint to sign a policy. |
inputsAsArray() |
Returns an array of all the inputs you'll need to submit in your form. |
inputsAsHtml() |
Returns an HTML string of all the inputs you'll need to submit in your form. |
setOptions(array $options ) |
Allows you to change options on-the-fly. |
Thumbs Up :thumbsup:
Thanks Edd Turtle for your great article. The purpose of this package is to make the process of signature generating a bit more modular, hence offer additional features, and create a bridge for Laravel users., (*27)
License
Directo is open-sourced software licensed under the MIT license., (*28)