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 )