AWS-S3
view release on metacpan or search on metacpan
lib/AWS/S3/Signer/V4.pm view on Meta::CPAN
containing the proper endpoint and parameters needed for an AWS REST
API Call. This method will return an appropriately signed request as a
URI object, which can be shared with non-AWS users for the purpose of,
e.g., accessing an object in a private S3 bucket.
Pass an optional $expires argument to indicate that the URL will only
be valid for a finite period of time. The value of the argument is in
seconds.
Pass an optional verb which is useful for HEAD requests, this defaults to GET.
=cut
sub signed_url {
my $self = shift;
my ( $arg1, $expires, $verb ) = @_;
my ( $request, $uri );
$verb ||= 'GET';
$verb = uc($verb);
my $incorrect_verbs = {
POST => 1,
PUT => 1
};
if ( exists( $incorrect_verbs->{$verb} ) ) {
die "Use AWS::S3::Signer::V4->sign sub for $verb method";
}
if ( ref $arg1 && UNIVERSAL::isa( $arg1, 'HTTP::Request' ) ) {
$request = $arg1;
$uri = $request->uri;
my $content = $request->content;
$uri->query($content) if $content;
if ( my $date =
$request->header('X-Amz-Date') || $request->header('Date') )
{
$uri->query_param( 'Date' => $date );
}
}
$uri ||= URI->new($arg1);
my $date = $uri->query_param_delete('Date')
|| $uri->query_param_delete('X-Amz-Date');
$request = HTTP::Request->new( $verb => $uri );
$request->header( 'Date' => $date );
$uri = $request->uri; # because HTTP::Request->new() copies the uri!
return $uri if $uri->query_param('X-Amz-Signature');
my $scope = $self->_scope($request);
$uri->query_param( 'X-Amz-Algorithm' => $self->_algorithm );
$uri->query_param( 'X-Amz-Credential' => $self->access_key . '/' . $scope );
$uri->query_param( 'X-Amz-Date' => $self->_datetime($request) );
$uri->query_param( 'X-Amz-Expires' => $expires ) if $expires;
$uri->query_param( 'X-Amz-SignedHeaders' => 'host' );
# If there was a security token passed, we need to supply it as part of the authorization
# because AWS requires it to validate IAM Role temporary credentials.
if ( defined( $self->{security_token} ) ) {
$uri->query_param( 'X-Amz-Security-Token' => $self->{security_token} );
}
# Since we're providing auth via query parameters, we need to include UNSIGNED-PAYLOAD
# http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
# it seems to only be needed for S3.
if ( $scope =~ /\/s3\/aws4_request$/ ) {
$self->_sign( $request, undef, 'UNSIGNED-PAYLOAD' );
}
else {
$self->_sign($request);
}
my ( $algorithm, $credential, $signedheaders, $signature ) =
$request->header('Authorization') =~
/^(\S+) Credential=(\S+), SignedHeaders=(\S+), Signature=(\S+)/;
$uri->query_param_append( 'X-Amz-Signature' => $signature );
return $uri;
}
sub _add_date_header {
my $self = shift;
my $request = shift;
my $datetime;
unless ( $datetime = $request->header('x-amz-date') ) {
$datetime = $self->_zulu_time($request);
$request->header( 'x-amz-date' => $datetime );
}
}
sub _scope {
my $self = shift;
my ( $request, $region ) = @_;
my $host = $request->uri->host;
my $datetime = $self->_datetime($request);
my ($date) = $datetime =~ /^(\d+)T/;
my $service;
( $service, $region ) = $self->parse_host( $host, $region );
$service ||= $self->{service} || 's3';
$region ||= $self->{region} || 'us-east-1'; # default
return "$date/$region/$service/aws4_request";
}
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,
( run in 1.180 second using v1.01-cache-2.11-cpan-39bf76dae61 )