AWS-S3

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

  - Fix DNS bucket name checking for non-valid DNS bucket nams (GH #4)
  - Fix URI escaping for filenames to avoid infinite loop on spaces (GH #5)

0.11 2015-08-31
  - Fix signed_url to URI escape the Signature param value (GH #3)

0.10 2015-06-23
  - dist changes related to kwalitee (no functional changes)

0.051 2015-05-23
  - default size to 0 when not set in content-length (GH #1)

0.050 2014-10-23
  New maintainer leejo
  - Add lib/AWS/S3/Request/GetPreSignedUrl.pm to MANIFEST
  - Various changes in dist related to issue tracking, testing, etc

0.040 2014-10-23
  Work done by leejo
  - Add signed_url method to AWS::S3::File and tests
  - Fix load / hash order bugs in AWS::S3::Signer

lib/AWS/S3/Bucket.pm  view on Meta::CPAN

    my $type = 'GetFileInfo';

    my $parser = $s->_get_property( $type, key => $key )
      or return;

    my $res = $parser->response;
    confess "Cannot get file: ", $res->as_string, " " unless $res->is_success;
    return AWS::S3::File->new(
        bucket       => $s,
        key          => $key || undef,
        size         => $res->header( 'content-length' ) || 0,
        contenttype  => $res->header( 'content-type' ) || 'application/octet-stream',
        etag         => $res->header( 'etag' ) || undef,
        lastmodified => $res->header( 'last-modified' ) || undef,
        is_encrypted => ( $res->header( 'x-amz-server-side-encryption' ) || '' ) eq 'AES256' ? 1 : 0,
    );
}    # end file()

sub add_file {
    my ( $s, %args ) = @_;

lib/AWS/S3/File.pm  view on Meta::CPAN

    required => 1,
    weak_ref => 0,
);

has 'size' => (
    is       => 'ro',
    isa      => 'Int',
    required => 0,
    default  => sub {
      my $self = shift;
      return length ${$self->contents};
    }
);

has 'etag' => (
    is       => 'ro',
    isa      => 'Str',
    required => 0,
);

has 'owner' => (

lib/AWS/S3/HTTPRequest.pm  view on Meta::CPAN

        s3      => $s->s3,
        method  => $method,
        uri     => $uri,
        content => $content ? \$content : undef,
        headers => [ $headers->flatten ],
    );

    $headers->header( 'Authorization'  => $signer->auth_header );
    $headers->header( 'Date'           => $signer->date );
    $headers->header( 'Host'           => URI->new( $uri )->host );
    $headers->header( 'content-length' => $signer->content_length ) if $content;
    $headers->header( 'content-type'   => $signer->content_type ) if $content;

    my $request = HTTP::Request->new( $method, $uri, $headers, $content );

    return $request;
}    # end http_request()

__PACKAGE__->meta->make_immutable;

lib/AWS/S3/Roles/Bucket.pm  view on Meta::CPAN

        $uri = "$protocol://$1.$endpoint$2";
    }    # end if()

    return $uri;
}

sub is_dns_bucket {
    my ( $s,$bucket ) = @_;

    # https://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html
    return 0 if ( length( $bucket ) < 3 or length( $bucket ) > 63 );
    return 0 if $bucket =~ /^(?:\d{1,3}\.){3}\d{1,3}$/;

    # DNS bucket names can contain lowercase letters, numbers, and hyphens
    # so anything outside this range we say isn't a valid DNS bucket
    return $bucket =~ /[^a-z0-9-\.]/ ? 0 : 1;
}

1;

lib/AWS/S3/Signer.pm  view on Meta::CPAN

        return '' unless $s->content;
        return encode_base64( md5( ${ $s->content } ), '' );
    }
);

has 'content' => (
    is       => 'ro',
    isa      => 'Maybe[ScalarRef]',
);

has 'content_length' => (
    is       => 'ro',
    isa      => 'Int',
    lazy     => 1,
    default  => sub { length( ${ shift->content } ) }
);

has 'signature' => (
    is       => 'ro',
    isa      => 'Str',
    lazy     => 1,
    default  => sub {
        my $s    = shift;
        my $hmac = Digest::HMAC_SHA1->new( $s->s3->secret_access_key );
        $hmac->add( $s->string_to_sign() );

lib/AWS/S3/Signer/V4.pm  view on Meta::CPAN

sub parse_host {
    my $self = shift;
    my $host = shift;
    my $region = shift;

    # this entire thing should probably refactored into its own
    # distribution, a la https://github.com/zirkelc/amazon-s3-url

    # https://docs.aws.amazon.com/prescriptive-guidance/latest/defining-bucket-names-data-lakes/faq.html
    # Only lowercase letters, numbers, dashes, and dots are allowed in S3 bucket names.
    # Bucket names must be three to 63 characters in length,
    # must begin and end with a number or letter,
    # and cannot be in an IP address format.
    my $bucket_re = '[a-z0-9][a-z0-9\-\.]{1,61}[a-z0-9]';
    my $domain_re = 'amazonaws\.com';
    my $region_re = '(?:af|ap|ca|eu|il|me|mx|sa|us)-[a-z]+-\d';

    my ( $service, $url_style );

    # listed in order of appearance found in the docs:
    # https://community.aws/content/2biM1C0TkMkvJ2BLICiff8MKXS9/format-and-parse-amazon-s3-url?lang=en

t/010_basic.t  view on Meta::CPAN

        contents  => \$contents,
      ), "Added file $_";
    }# end for()
    
    # Make sure they all worked:
    my $counted = 0;
    foreach my $key ( sort keys %info )
    {
      my $contents = $info{$key};
      ok my $file = $bucket->file($key), "bucket.file($key) returned a file";
      is $file->size, length($contents), 'file.size is correct';
      is ${$file->contents}, $contents, 'file.contents is correct';
      my $expiration_date = time() + 3600;
      my $url = $file->signed_url( $expiration_date );
      is( $file->signed_url( $expiration_date ),$url,'signed_url same' ) for 1 .. 10;
warn "--->$url";
      my $res = $s3->ua->get( $url );
      ok( $res->is_success,'get signed_url' );
      isnt( $res->code,403,'not forbidden' );
      last if $counted++ > 4;
    }# end for()

t/aws/s3/bucket.t  view on Meta::CPAN

use warnings;

package Mocked::HTTP::Response;

use Moose;
extends 'HTTP::Response';

sub content        { shift->{_msg}; }
sub code           { 200 }
sub is_success     { 1 }
sub header         { $_[1] =~ /content-length/i ? 1 : 'header' }

1;

package main;
use Test::More;
use Test::Exception;
use FindBin qw/ $Script /;

use Carp 'confess';
$SIG{__DIE__} = \&confess;

t/aws/s3/file.t  view on Meta::CPAN

use warnings;

package Mocked::HTTP::Response;

use Moose;
extends 'HTTP::Response';

sub content        { shift->{_msg}; }
sub code           { 200 }
sub is_success     { 1 }
sub header         { $_[1] =~ /content-length/i ? 1 : 'header' }

1;

package main;

use Test::More;
use Test::Deep;
use URI::Escape qw/ uri_escape /;

use Carp 'confess';

t/aws/s3/file_iterator.t  view on Meta::CPAN

use warnings;

package Mocked::HTTP::Response;

use Moose;
extends 'HTTP::Response';

sub content        { shift->{_msg}; }
sub code           { 200 }
sub is_success     { 1 }
sub header         { $_[1] =~ /content-length/i ? 1 : 'header' }

1;

package main;
use Test::More;
use Test::Deep;
use Test::Exception;
use FindBin qw/ $Script /;
use Data::Section::Simple 'get_data_section';

t/aws/s3/http_request.t  view on Meta::CPAN

        method  => 'POST',
        path    => '/bar/baz',
        content => 'Hello World!'
    ),
    'AWS::S3::HTTPRequest'
);

isa_ok( my $http_request_with_content = $request_with_content->http_request, 'HTTP::Request' );
my $header = $http_request_with_content->headers;
is( $header->header( 'content-type' ), 'text/plain', '... and content-type got set' );
is( $header->header( 'content-length' ), 12, '... and content-length got set' );
is( $header->header( 'host' ), 's3.baz.com', '... and host got set' );

done_testing();

t/aws/s3/signer.t  view on Meta::CPAN

        bucket_name
        uri
        headers
        date
        string_to_sign
        canonicalized_amz_headers
        canonicalized_resource
        content_type
        content_md5
        content
        content_length
        signature
    /,
);


note( "attributes" );
isa_ok( $signer->s3,'AWS::S3' );
is( $signer->method,'HEAD','method' );
is( $signer->bucket_name,'maibucket','bucket_name' );
isa_ok( $signer->uri,'URI' );

t/aws/s3/signer.t  view on Meta::CPAN

is(
    $signer->string_to_sign,
    "HEAD\nXrY7u+Ae7tCTyyK7j1rNww==\ntext/plain\n".$signer->date."\n/maibucket/boz",
    'string_to_sign'
);
is( $signer->canonicalized_amz_headers,'','canonicalized_amz_headers' );
is( $signer->canonicalized_resource,'/maibucket/boz','canonicalized_resource' );
is( $signer->content_type,'text/plain','content_type' );
is( $signer->content_md5,'XrY7u+Ae7tCTyyK7j1rNww==','content_md5' );
is( ${ $signer->content },'hello world','content' );
is( $signer->content_length,11,'content_length' );
like( $signer->signature,qr/^.{28}$/,'signature' );

note( "methods" );
like( $signer->auth_header,qr/AWS foo:.{28}/,'auth_header' );

done_testing();

t/aws/s3/signer/v4.t  view on Meta::CPAN

is( $request->method,         'POST',              'request method correct' );
is( $request->header('Host'), 'iam.amazonaws.com', 'host correct' );
is( $request->header('X-Amz-Date'), '20140101T060000Z', 'timestamp correct' );
is(
    $request->content,
    'Action=ListUsers&Version=2010-05-08',
    'payload correct'
);
is(
    $request->header('Authorization'),
'AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20140101/us-east-1/iam/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date, Signature=0233049369ae675cea7616efa5d2e5216c37a4b1496a36595f32181f078e3549',
    'signature correct'
);

$request = GET( 'https://iam.amazonaws.com?Action=ListUsers&Version=2010-05-08',
    Date => '1 January 2014 01:00:00 -0500' );

my $expected =
'https://iam.amazonaws.com?Action=ListUsers&Version=2010-05-08&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIDEXAMPLE%2F20140101%2Fus-east-1%2Fiam%2Faws4_request&X-Amz-Date=20140101T060000Z&X-Amz-SignedHeaders=host&X-Amz-Signature=9d0b832ec5c5...

is( $signer->signed_url($request),

t/aws/s3/signer/v4.t  view on Meta::CPAN

  Date => '1 January 2014 01:00:00 -0500'
);

is( $signer->signed_url($request), $expected, 'domain bucket url' );

$request = POST('https://cognito-identity.us-east-1.amazonaws.com',
		   Date    => '1 January 2014 01:00:00 -0500');

$signer->sign($request);

is($request->header('Authorization'),'AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20140101/us-east-1/cognito-identity/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date, Signature=047c9335c6a34448efc59c2a1813711602e208dcb42ae95cd3b88...

exit 0;



( run in 0.341 second using v1.01-cache-2.11-cpan-65fba6d93b7 )