Amazon-S3-SignedURLGenerator

 view release on metacpan or  search on metacpan

lib/Amazon/S3/SignedURLGenerator.pm  view on Meta::CPAN

package Amazon::S3::SignedURLGenerator;

use strict;
use warnings;
our $VERSION = '0.02';

use Carp;
use URI::Escape;
use Digest::HMAC_SHA1;
use MIME::Base64 qw(encode_base64);

sub new {
    my $proto = shift;
    my $class = ref($proto) || $proto;

    my %args = scalar(@_) % 2 ? %{$_[0]} : @_;
    $args{aws_access_key_id} or croak 'aws_access_key_id is required';
    $args{aws_secret_access_key} or croak 'aws_secret_access_key is required';

    $args{prefix}  ||= 'https://s3.amazonaws.com';
    $args{expires} ||= 3600;

    $args{prefix} =~ s/\/$//; # remove last /

    return bless \%args, $class;
}

sub generate_url {
    my ($self, $method, $path, $headers) = @_;

    $path =~ s/^\///;
    $headers ||= {};
    my $expires = $headers->{expires} || (time() + $self->{expires});

    my $x_path = $path;
    if ($self->{prefix} =~ '//(.*)\.s3') {
        $x_path = $1 . '/' . $path;
    }

    my $canonical_string = __canonical_string($method, $x_path, $headers, $expires);
    my $encoded_canonical = __encode($self->{aws_secret_access_key}, $canonical_string, 1);
    if (index($path, '?') == -1) {
        return "$self->{prefix}/$path?Signature=$encoded_canonical&Expires=$expires&AWSAccessKeyId=$self->{aws_access_key_id}";
    } else {
        return "$self->{prefix}/$path&Signature=$encoded_canonical&Expires=$expires&AWSAccessKeyId=$self->{aws_access_key_id}";
    }
}

our $AMAZON_HEADER_PREFIX = 'x-amz-';
our $METADATA_PREFIX = 'x-amz-meta-';

sub __trim {
    my ($value) = @_;

    $value =~ s/^\s+//;
    $value =~ s/\s+$//;
    return $value;
}

# generate a canonical string for the given parameters.  expires is optional and is
# only used by query string authentication.
sub __canonical_string {
    my ($method, $path, $headers, $expires) = @_;
    my %interesting_headers = ();
    while (my ($key, $value) = each %$headers) {
        my $lk = lc $key;
        if (
            $lk eq 'content-md5' or
            $lk eq 'content-type' or
            $lk eq 'date' or
            $lk =~ /^$AMAZON_HEADER_PREFIX/
        ) {
            $interesting_headers{$lk} = __trim($value);
        }
    }

    # these keys get empty strings if they don't exist
    $interesting_headers{'content-type'} ||= '';
    $interesting_headers{'content-md5'} ||= '';

    # just in case someone used this.  it's not necessary in this lib.
    $interesting_headers{'date'} = '' if $interesting_headers{'x-amz-date'};

    # if you're using expires for query string auth, then it trumps date
    # (and x-amz-date)



( run in 0.900 second using v1.01-cache-2.11-cpan-39bf76dae61 )