diff --git a/benchmarks/util/dynamodb-decode.pl b/benchmarks/util/dynamodb-decode.pl index 7d0b409838..c24f2139ef 100755 --- a/benchmarks/util/dynamodb-decode.pl +++ b/benchmarks/util/dynamodb-decode.pl @@ -45,11 +45,15 @@ BEGIN package Test::CustomCredentials { use Moose; use Paws::Credential; + use Paws::Credential::Explicit; with 'Paws::Credential'; - sub access_key { 'CustomAK' }; - sub secret_key { 'CustomSK' }; - sub session_token {}; + sub refresh { + return Paws::Credential::Explicit->new( + access_key => 'CustomAK', + secret_key => 'CustomSK', + ); + } __PACKAGE__->meta->make_immutable; }; diff --git a/examples/athena.pl b/examples/athena.pl index f9c625cbb4..9971fe276a 100644 --- a/examples/athena.pl +++ b/examples/athena.pl @@ -75,7 +75,7 @@ $status->QueryExecution->ResultConfiguration->OutputLocation . "\n" if $opt->verbose; -my $a = Paws::Credential::ProviderChain->new->selected_provider; +my $a = Paws::Credential::ProviderChain->new->refresh; # Paws::S3 is marked as unstable; the following wouldn't work with IAM roles. my $s3 = Net::Amazon::S3->new( diff --git a/examples/sts-saml.pl b/examples/sts-saml.pl index fb2e1840a3..ab30e2e1a8 100755 --- a/examples/sts-saml.pl +++ b/examples/sts-saml.pl @@ -92,13 +92,15 @@ $config = Config::INI::Reader->read_file($aws_creds_file); } +my $a = $creds->refresh; + my $profile = lc $short_role; try { $config->{$profile} = { region => $region, - aws_access_key_id => $creds->access_key, - aws_secret_access_key => $creds->secret_key, - aws_session_token => $creds->session_token, + aws_access_key_id => $a->access_key, + aws_secret_access_key => $a->secret_key, + aws_session_token => $a->session_token, }; } catch(Paws::Exception $e) { die sprintf "FATAL: %s - %s\n", $e->code, $e->message; diff --git a/lib/Paws/API/Caller.pm b/lib/Paws/API/Caller.pm index acdbdd9089..9b0baaf5a9 100644 --- a/lib/Paws/API/Caller.pm +++ b/lib/Paws/API/Caller.pm @@ -10,7 +10,6 @@ package Paws::API::Caller; is => 'ro', does => 'Paws::Credential', required => 1, - handles => [ 'access_key', 'secret_key', 'session_token' ], ); # converts the params the user passed to the call into objects that represent the call diff --git a/lib/Paws/Credential.pm b/lib/Paws/Credential.pm index 25e5cb26a5..3a0502f251 100644 --- a/lib/Paws/Credential.pm +++ b/lib/Paws/Credential.pm @@ -1,13 +1,11 @@ package Paws::Credential; use Moose::Role; - requires 'access_key'; - requires 'secret_key'; - requires 'session_token'; + requires 'refresh'; sub are_set { my $self = shift; - return (defined $self->access_key && defined $self->secret_key); + return (defined $self->refresh); } no Moose; diff --git a/lib/Paws/Credential/AssumeRole.pm b/lib/Paws/Credential/AssumeRole.pm index 3b6356381c..eb20486a89 100644 --- a/lib/Paws/Credential/AssumeRole.pm +++ b/lib/Paws/Credential/AssumeRole.pm @@ -1,8 +1,11 @@ package Paws::Credential::AssumeRole; use Moose; use DateTime::Format::ISO8601; + use Paws::Credential::Explicit; with 'Paws::Credential'; + has credentials => (is => 'rw', isa => 'Paws::Credential::Explicit|Undef'); + has expiration => ( is => 'rw', isa => 'Int', @@ -10,26 +13,6 @@ package Paws::Credential::AssumeRole; default => sub { 0 } ); - has actual_creds => (is => 'rw'); - - sub access_key { - my $self = shift; - $self->_refresh; - $self->actual_creds->AccessKeyId; - } - - sub secret_key { - my $self = shift; - $self->_refresh; - $self->actual_creds->SecretAccessKey; - } - - sub session_token { - my $self = shift; - $self->_refresh; - $self->actual_creds->SessionToken; - } - has sts_region => (is => 'ro', isa => 'Str|Undef', default => sub { undef }); has sts => (is => 'ro', isa => 'Paws::STS', lazy => 1, default => sub { @@ -44,10 +27,12 @@ package Paws::Credential::AssumeRole; has RoleArn => (is => 'rw', isa => 'Str', required => 1); has RoleSessionName => (is => 'rw', isa => 'Str', required => 1); - sub _refresh { + sub refresh { my $self = shift; - return if $self->expiration >= time; + if ( $self->credentials && $self->expiration >= time ) { + return $self->credentials; + } my $result = $self->sts->AssumeRole( RoleSessionName => $self->RoleSessionName, @@ -57,8 +42,14 @@ package Paws::Credential::AssumeRole; (defined $self->Policy) ? (Policy => $self->Policy) : (), ); - my $creds = $self->actual_creds($result->Credentials); + $self->credentials(Paws::Credential::Explicit->new( + access_key => $result->Credentials->AccessKeyId, + secret_key => $result->Credentials->SecretAccessKey, + session_token => $result->Credentials->SessionToken, + )); $self->expiration(DateTime::Format::ISO8601->parse_datetime($result->Credentials->Expiration)->epoch); + + return $self->credentials; } no Moose; diff --git a/lib/Paws/Credential/AssumeRoleWithSAML.pm b/lib/Paws/Credential/AssumeRoleWithSAML.pm index 8a2d433241..ae6756da4c 100644 --- a/lib/Paws/Credential/AssumeRoleWithSAML.pm +++ b/lib/Paws/Credential/AssumeRoleWithSAML.pm @@ -2,9 +2,11 @@ package Paws::Credential::AssumeRoleWithSAML; use Moose; use DateTime::Format::ISO8601; use Paws::Credential::None; - + use Paws::Credential::Explicit; with 'Paws::Credential'; + has credentials => (is => 'rw', isa => 'Paws::Credential::Explicit|Undef'); + has expiration => ( is => 'rw', isa => 'Int', @@ -12,26 +14,6 @@ package Paws::Credential::AssumeRoleWithSAML; default => sub { 0 } ); - has actual_creds => (is => 'rw'); - - sub access_key { - my $self = shift; - $self->_refresh; - $self->actual_creds->AccessKeyId; - } - - sub secret_key { - my $self = shift; - $self->_refresh; - $self->actual_creds->SecretAccessKey; - } - - sub session_token { - my $self = shift; - $self->_refresh; - $self->actual_creds->SessionToken; - } - has sts_region => (is => 'ro', isa => 'Str|Undef', default => sub { undef }); has sts => (is => 'ro', isa => 'Paws::STS', lazy => 1, default => sub { @@ -46,10 +28,12 @@ package Paws::Credential::AssumeRoleWithSAML; has PrincipalArn => (is => 'rw', isa => 'Str', required => 1); has SAMLAssertion => (is => 'rw', isa => 'Str', required => 1); - sub _refresh { + sub refresh { my $self = shift; - return if $self->expiration >= time; + if ( $self->credentials && $self->expiration >= time ) { + return $self->credentials; + } my $result = $self->sts->AssumeRoleWithSAML( RoleArn => $self->RoleArn, @@ -59,11 +43,13 @@ package Paws::Credential::AssumeRoleWithSAML; (defined $self->Policy) ? (Policy => $self->Policy) : (), ); - my $creds = $self->actual_creds($result->Credentials); - + $self->credentials(Paws::Credential::Explicit->new( + access_key => $result->Credentials->AccessKeyId, + secret_key => $result->Credentials->SecretAccessKey, + session_token => $result->Credentials->SessionToken, + )); $self->expiration(DateTime::Format::ISO8601->parse_datetime($result->Credentials->Expiration)->epoch); } no Moose; - 1; diff --git a/lib/Paws/Credential/CredProcess.pm b/lib/Paws/Credential/CredProcess.pm index b929be5fef..f31223299c 100644 --- a/lib/Paws/Credential/CredProcess.pm +++ b/lib/Paws/Credential/CredProcess.pm @@ -3,9 +3,14 @@ package Paws::Credential::CredProcess; use JSON::MaybeXS qw/decode_json/; use Paws::Exception; use DateTime::Format::ISO8601; + use Paws::Credential::Explicit; + + with 'Paws::Credential'; has credential_process => (is => 'ro', isa => 'Str', required => 1); + has credentials => (is => 'rw', isa => 'Paws::Credential::Explicit|Undef'); + has expiration => ( is => 'rw', isa => 'Int|Undef', @@ -13,16 +18,19 @@ package Paws::Credential::CredProcess; default => sub { 0 } ); - has actual_creds => ( - is => 'ro', - isa => 'HashRef', - builder => '_build_actual_creds', - clearer => '_clear_actual_creds', - lazy => 1 - ); - - sub _build_actual_creds { + sub refresh { my $self = shift; + + if ( $self->credentials ) { + if (not defined $self->expiration) { + return $self->credentials; + } + + if ($self->expiration >= time ) { + return $self->credentials; + } + } + my $creds; my $rc; { @@ -44,103 +52,14 @@ package Paws::Credential::CredProcess; $self->expiration(undef); } - return $creds; - } - - sub access_key { - my $self = shift; - $self->_refresh; - $self->actual_creds->{ AccessKeyId }; - } - - sub secret_key { - my $self = shift; - $self->_refresh; - $self->actual_creds->{ SecretAccessKey } - } - - sub session_token { - my $self = shift; - $self->_refresh; - $self->actual_creds->{ SessionToken }; - } - - sub _refresh { - my $self = shift; + $self->credentials(Paws::Credential::Explicit->new( + access_key => $creds->{ AccessKeyId }, + secret_key => $creds->{ SecretAccessKey }, + session_token => $creds->{ SessionToken }, + )); - return if not defined $self->expiration; - return if $self->expiration >= time; - $self->_clear_actual_creds; + return $self->credentials; } - with 'Paws::Credential'; - no Moose; 1; -### main pod documentation begin ### - -=encoding UTF-8 - -=head1 NAME - -Paws::Credential::File - -=head1 SYNOPSIS - - use Paws::Credential::File; - - my $paws = Paws->new(config => { - credentials => Paws::Credential::File->new( - profile => 'profile1', - credentials_file => '/etc/aws_system_credentials', - ) - }); - # will open /etc/aws_system_credentials - - - my $paws = Paws->new(config => { - credentials => Paws::Credential::File->new( - profile => 'profile1', - file_name => 'my_creds', - ) - }); - # will open $HOME/.aws/my_creds - - my $paws = Paws->new(config => { - credentials => Paws::Credential::File->new( - profile => 'profile1', - dir => '/etc/', - ) - }); - # will open /etc/credentials - - -=head1 DESCRIPTION - -The File credential provider is to read credentials from AWS SDK config files - -=head2 profile: Str - -The section in the ini file where credentials will be looked up: - -Defaults to the environment variable C, and if that is not defined, to "default" - -=head2 credentials_file: Str - -The path of the ini file to open - -Defaults to the path + file_name (C<$HOME/.aws/credentials> by default) if the environment variable AWS_CONFIG_FILE doesn't exist - -=head2 path: Str - -Path to the ini file - -Defaults to C<$HOME/.aws> - -=head2 file_name: Str - -Name of the ini file - -Defaults to C - -=cut diff --git a/lib/Paws/Credential/ECSContainerProfile.pm b/lib/Paws/Credential/ECSContainerProfile.pm index c161dc5591..e7ce2a3977 100644 --- a/lib/Paws/Credential/ECSContainerProfile.pm +++ b/lib/Paws/Credential/ECSContainerProfile.pm @@ -3,6 +3,7 @@ package Paws::Credential::ECSContainerProfile; use Moose; use DateTime::Format::ISO8601; use URI; + use Paws::Credential::Explicit; with 'Paws::Credential'; has container_local_uri => ( @@ -41,44 +42,31 @@ package Paws::Credential::ECSContainerProfile; } ); + has credentials => (is => 'rw', isa => 'Paws::Credential::Explicit|Undef'); + has expiration => ( is => 'rw', isa => 'Int', default => sub { 0 } ); - has actual_creds => (is => 'rw', default => sub { {} }); - around are_set => sub { my ($orig, $self) = @_; return 0 if (not defined $self->container_local_uri); return $self->$orig; }; - sub access_key { - my $self = shift; - $self->_refresh; - $self->actual_creds->{AccessKeyId}; - } - - sub secret_key { - my $self = shift; - $self->_refresh; - $self->actual_creds->{SecretAccessKey}; - } - - sub session_token { - my $self = shift; - $self->_refresh; - $self->actual_creds->{Token}; - } - #TODO: Raise exceptions if HTTP get didn't return success - sub _refresh { + sub refresh { my $self = shift; - return if $self->expiration - 240 >= time; - return if ! $self->metadata_url; + if ( $self->credentials && $self->expiration - 240 >= time ) { + return $self->credentials; + } + + if ( ! $self->metadata_url ) { + return; + } my $ua = $self->ua; @@ -89,8 +77,14 @@ package Paws::Credential::ECSContainerProfile; my $json = eval { decode_json($r->{content}) }; if ($@) { die "Error in JSON from metadata URL" } - $self->actual_creds($json); + $self->credentials(Paws::Credential::Explicit->new( + access_key => $json->{AccessKeyId}, + secret_key => $json->{SecretAccessKey}, + session_token => $json->{Token}, + )); $self->expiration(DateTime::Format::ISO8601->parse_datetime($json->{Expiration})->epoch); + + return $self->credentials; } no Moose; diff --git a/lib/Paws/Credential/Environment.pm b/lib/Paws/Credential/Environment.pm index 23e01e5917..5927a64ffe 100644 --- a/lib/Paws/Credential/Environment.pm +++ b/lib/Paws/Credential/Environment.pm @@ -1,11 +1,25 @@ package Paws::Credential::Environment; use Moose; + use Paws::Credential::Explicit; + with 'Paws::Credential'; - has access_key => (is => 'ro', default => sub { $ENV{AWS_ACCESS_KEY} || $ENV{AWS_ACCESS_KEY_ID} }); - has secret_key => (is => 'ro', default => sub { $ENV{AWS_SECRET_KEY} || $ENV{AWS_SECRET_ACCESS_KEY} }); - has session_token => (is => 'ro', default => sub { $ENV{AWS_SESSION_TOKEN} }); + sub refresh { + my $self = shift; - with 'Paws::Credential'; + my $access_key = $ENV{AWS_ACCESS_KEY} || $ENV{AWS_ACCESS_KEY_ID}; + my $secret_key = $ENV{AWS_SECRET_KEY} || $ENV{AWS_SECRET_ACCESS_KEY}; + my $session_token = $ENV{AWS_SESSION_TOKEN}; + + if (!$access_key || !$secret_key) { + return; + } + + return Paws::Credential::Explicit->new( + access_key => $access_key, + secret_key => $secret_key, + session_token => $session_token, + ); + } no Moose; 1; diff --git a/lib/Paws/Credential/Explicit.pm b/lib/Paws/Credential/Explicit.pm index 5a6d1991c5..5ce45ea0f7 100644 --- a/lib/Paws/Credential/Explicit.pm +++ b/lib/Paws/Credential/Explicit.pm @@ -1,11 +1,15 @@ package Paws::Credential::Explicit; use Moose; + with 'Paws::Credential'; has access_key => (is => 'ro', isa => 'Str', required => 1); has secret_key => (is => 'ro', isa => 'Str', required => 1); - has session_token => (is => 'ro', isa => 'Str'); + has session_token => (is => 'ro', isa => 'Str|Undef'); - with 'Paws::Credential'; + sub refresh { + my $self = shift; + return $self; + } no Moose; 1; diff --git a/lib/Paws/Credential/File.pm b/lib/Paws/Credential/File.pm index 70cb720847..0f1d955e6f 100644 --- a/lib/Paws/Credential/File.pm +++ b/lib/Paws/Credential/File.pm @@ -5,6 +5,8 @@ package Paws::Credential::File; use JSON::MaybeXS qw/decode_json/; use Paws::Exception; use Paws::Credential::CredProcess; + use Paws::Credential::Explicit; + with 'Paws::Credential'; has profile => (is => 'ro', default => sub { $ENV{ AWS_DEFAULT_PROFILE } or 'default' }); @@ -32,41 +34,41 @@ package Paws::Credential::File; has _profile => (is => 'ro', isa => 'HashRef', lazy => 1, default => sub { my $self = shift; + my $profile = $self->profile; return $self->_ini_contents->{ $profile } || {}; }); - has credential_process => (is => 'ro', lazy => 1, default => sub { + has credential_process => (is => 'ro', isa => 'Paws::Credential::CredProcess|Undef', lazy => 1, default => sub { my $self = shift; - return undef if (not defined $self->_profile->{ credential_process }); + + my $process = $self->_profile->{ credential_process }; + return undef if (not defined $process); + return Paws::Credential::CredProcess->new( - credential_process => $self->_profile->{ credential_process }, + credential_process => $process, ); }); - sub access_key { - my $self = shift; - - return $self->credential_process->access_key if (defined $self->credential_process); - return $self->_profile->{ aws_access_key_id }; - } - - sub secret_key { + sub refresh { my $self = shift; - return $self->credential_process->secret_key if (defined $self->credential_process); - return $self->_profile->{ aws_secret_access_key }; - } + if (defined $self->credential_process) { + return $self->credential_process->refresh; + } - sub session_token { - my $self = shift; + my $profile = $self->_profile; + return if (not defined $profile); + return if (not defined $profile->{ aws_access_key_id }); + return if (not defined $profile->{ aws_secret_access_key }); - return $self->credential_process->session_token if (defined $self->credential_process); - return $self->_profile->{ aws_session_token }; + return Paws::Credential::Explicit->new( + access_key => $profile->{ aws_access_key_id }, + secret_key => $profile->{ aws_secret_access_key }, + session_token => $profile->{ aws_session_token }, + ); } - with 'Paws::Credential'; - no Moose; 1; ### main pod documentation begin ### diff --git a/lib/Paws/Credential/InstanceProfile.pm b/lib/Paws/Credential/InstanceProfile.pm index 1a4f6c8854..3d1a0775a0 100644 --- a/lib/Paws/Credential/InstanceProfile.pm +++ b/lib/Paws/Credential/InstanceProfile.pm @@ -2,6 +2,7 @@ package Paws::Credential::InstanceProfile; use JSON::MaybeXS; use Moose; use DateTime::Format::ISO8601; + use Paws::Credential::Explicit; with 'Paws::Credential'; has metadata_url => ( @@ -25,37 +26,21 @@ package Paws::Credential::InstanceProfile; } ); + has credentials => (is => 'rw', isa => 'Paws::Credential::Explicit|Undef'); + has expiration => ( is => 'rw', isa => 'Int', default => sub { 0 } ); - has actual_creds => (is => 'rw', default => sub { {} }); - - sub access_key { - my $self = shift; - $self->_refresh; - $self->actual_creds->{AccessKeyId}; - } - - sub secret_key { - my $self = shift; - $self->_refresh; - $self->actual_creds->{SecretAccessKey}; - } - - sub session_token { - my $self = shift; - $self->_refresh; - $self->actual_creds->{Token}; - } - #TODO: Raise exceptions if HTTP get didn't return success - sub _refresh { + sub refresh { my $self = shift; - return if $self->expiration - 240 >= time; + if ($self->credentials && $self->expiration - 240 >= time) { + return $self->credentials; + } my $ua = $self->ua; my $r = $ua->get($self->metadata_url); @@ -68,8 +53,14 @@ package Paws::Credential::InstanceProfile; my $json = eval { decode_json($r->{content}) }; if ($@) { die "Error in JSON from metadata URL" } - $self->actual_creds($json); + $self->credentials(Paws::Credential::Explicit->new( + access_key => $json->{AccessKeyId}, + secret_key => $json->{SecretAccessKey}, + session_token => $json->{Token}, + )); $self->expiration(DateTime::Format::ISO8601->parse_datetime($json->{Expiration})->epoch); + + return $self->credentials; } no Moose; diff --git a/lib/Paws/Credential/InstanceProfileV2.pm b/lib/Paws/Credential/InstanceProfileV2.pm index de9a455753..9c5d7cb343 100644 --- a/lib/Paws/Credential/InstanceProfileV2.pm +++ b/lib/Paws/Credential/InstanceProfileV2.pm @@ -2,6 +2,7 @@ package Paws::Credential::InstanceProfileV2; use JSON::MaybeXS; use Moose; use DateTime::Format::ISO8601; + use Paws::Credential::Explicit; with 'Paws::Credential'; has metadata_url => ( @@ -31,38 +32,22 @@ package Paws::Credential::InstanceProfileV2; } ); + has credentials => (is => 'rw', isa => 'Paws::Credential::Explicit|Undef'); + has expiration => ( is => 'rw', isa => 'Int', default => sub { 0 } ); - has actual_creds => (is => 'rw', default => sub { {} }); - - sub access_key { - my $self = shift; - $self->_refresh; - $self->actual_creds->{AccessKeyId}; - } - - sub secret_key { - my $self = shift; - $self->_refresh; - $self->actual_creds->{SecretAccessKey}; - } - - sub session_token { - my $self = shift; - $self->_refresh; - $self->actual_creds->{Token}; - } - #TODO: Raise exceptions if HTTP get didn't return success - sub _refresh { + sub refresh { my $self = shift; - return if $self->expiration - 240 >= time; - + if ($self->credentials && $self->expiration - 240 >= time) { + return $self->credentials; + } + my $ua = $self->ua; my $r = $ua->put( $self->token_url, { headers => { 'X-aws-ec2-metadata-token-ttl-seconds' => '21600' } } ); return unless $r->{success}; @@ -81,8 +66,14 @@ package Paws::Credential::InstanceProfileV2; my $json = eval { decode_json($r->{content}) }; if ($@) { die "Error in JSON from metadata URL" } - $self->actual_creds($json); + $self->credentials(Paws::Credential::Explicit->new( + access_key => $json->{AccessKeyId}, + secret_key => $json->{SecretAccessKey}, + session_token => $json->{Token}, + )); $self->expiration(DateTime::Format::ISO8601->parse_datetime($json->{Expiration})->epoch); + + return $self->credentials; } no Moose; diff --git a/lib/Paws/Credential/None.pm b/lib/Paws/Credential/None.pm index 62b42489a1..5b11289af7 100644 --- a/lib/Paws/Credential/None.pm +++ b/lib/Paws/Credential/None.pm @@ -2,12 +2,7 @@ package Paws::Credential::None; use Moose; with 'Paws::Credential'; - sub access_key { q{} } - - sub secret_key { q{} } - - sub session_token { q{} } + sub refresh { return undef; } no Moose; - 1; diff --git a/lib/Paws/Credential/ProviderChain.pm b/lib/Paws/Credential/ProviderChain.pm index c8d074c347..8e5cfbf0ee 100644 --- a/lib/Paws/Credential/ProviderChain.pm +++ b/lib/Paws/Credential/ProviderChain.pm @@ -17,7 +17,7 @@ package Paws::Credential::ProviderChain; has selected_provider => ( is => 'rw', does => 'Paws::Credential', - handles => [ 'access_key', 'secret_key', 'session_token' ], + handles => [ 'refresh' ], ); sub BUILD { diff --git a/lib/Paws/Credential/STS.pm b/lib/Paws/Credential/STS.pm index 608fa6a056..9a189bafa9 100644 --- a/lib/Paws/Credential/STS.pm +++ b/lib/Paws/Credential/STS.pm @@ -1,8 +1,11 @@ package Paws::Credential::STS; use Moose; use DateTime::Format::ISO8601; + use Paws::Credential::Explicit; with 'Paws::Credential'; + has credentials => (is => 'rw', isa => 'Paws::Credential::Explicit|Undef'); + has expiration => ( is => 'rw', isa => 'Int', @@ -10,26 +13,6 @@ package Paws::Credential::STS; default => sub { 0 } ); - has actual_creds => (is => 'rw'); - - sub access_key { - my $self = shift; - $self->_refresh; - $self->actual_creds->AccessKeyId; - } - - sub secret_key { - my $self = shift; - $self->_refresh; - $self->actual_creds->SecretAccessKey; - } - - sub session_token { - my $self = shift; - $self->_refresh; - $self->actual_creds->SessionToken; - } - has sts_region => (is => 'ro', isa => 'Str|Undef', default => sub { undef }); has sts => (is => 'ro', isa => 'Paws::STS', lazy => 1, default => sub { @@ -41,10 +24,12 @@ package Paws::Credential::STS; has DurationSeconds => (is => 'rw', isa => 'Maybe[Int]'); has Policy => (is => 'rw', isa => 'Maybe[Str]'); - sub _refresh { + sub refresh { my $self = shift; - return if $self->expiration >= time; + if ( $self->credentials && $self->expiration >= time ) { + return $self->credentials; + } my $result = $self->sts->GetFederationToken( Name => $self->Name, @@ -52,8 +37,14 @@ package Paws::Credential::STS; (defined $self->Policy) ? (Policy => $self->Policy) : (), ); - my $creds = $self->actual_creds($result->Credentials); + $self->credentials(Paws::Credential::Explicit->new( + access_key => $result->Credentials->AccessKeyId, + secret_key => $result->Credentials->SecretAccessKey, + session_token => $result->Credentials->SessionToken, + )); $self->expiration(DateTime::Format::ISO8601->parse_datetime($result->Credentials->Expiration)->epoch); + + return $self->credentials; } no Moose; diff --git a/lib/Paws/Net/JsonCaller.pm b/lib/Paws/Net/JsonCaller.pm index 5c173fe006..1774a77f69 100644 --- a/lib/Paws/Net/JsonCaller.pm +++ b/lib/Paws/Net/JsonCaller.pm @@ -72,10 +72,12 @@ package Paws::Net::JsonCaller; $request->uri('/'); $request->method('POST'); + my $creds = $self->credentials->refresh; + my @time = gmtime; $request->parameters({ Action => $call->_api_call, Version => $self->version, - AWSAccessKeyId => $self->access_key, + AWSAccessKeyId => $creds->access_key, Timestamp => strftime("%Y-%m-%dT%H:%M:%SZ",@time), }); $request->header('X-Amz-Target', sprintf('%s.%s', $self->target_prefix, $call->_api_call)); @@ -90,7 +92,7 @@ package Paws::Net::JsonCaller; my $data = $self->_to_jsoncaller_params($call); $request->content(encode_json($data)); - $self->sign($request); + $self->sign($request, $creds); return $request; } diff --git a/lib/Paws/Net/S3Signature.pm b/lib/Paws/Net/S3Signature.pm index 71fa1c467e..4a89893e7b 100755 --- a/lib/Paws/Net/S3Signature.pm +++ b/lib/Paws/Net/S3Signature.pm @@ -22,10 +22,12 @@ package Paws::Net::S3Signature; use Net::Amazon::Signature::V4; sub sign { - my ($self, $request) = @_; + my ($self, $request, $creds) = @_; - if ($self->session_token) { - $request->header( 'X-Amz-Security-Token' => $self->session_token ); + $creds ||= $self->credentials->refresh; + + if ($creds->session_token) { + $request->header( 'X-Amz-Security-Token' => $creds->session_token ); } my $hasher = Digest::SHA->new(256); @@ -40,7 +42,7 @@ package Paws::Net::S3Signature; ? $self->endpoint->host : $self->endpoint->host_port); - my $sig = Net::Amazon::Signature::V4->new( $self->access_key, $self->secret_key, $self->region, $self->service ); + my $sig = Net::Amazon::Signature::V4->new( $creds->access_key, $creds->secret_key, $self->region, $self->service ); my $signed_req = $sig->sign( $request ); return $signed_req; diff --git a/lib/Paws/Net/S3V4Signature.pm b/lib/Paws/Net/S3V4Signature.pm index dd618bf5f3..ff6eb61053 100644 --- a/lib/Paws/Net/S3V4Signature.pm +++ b/lib/Paws/Net/S3V4Signature.pm @@ -24,19 +24,21 @@ package Paws::Net::S3V4Signature; } sub sign { - my ($self, $request) = @_; + my ($self, $request, $creds) = @_; + + $creds ||= $self->credentials->refresh; $request->header( Date => $request->header('X-Amz-Date') // strftime( '%Y%m%dT%H%M%SZ', gmtime ) ); $request->header( 'Host' => $self->endpoint->default_port == $self->endpoint->port ? $self->endpoint->host : $self->endpoint->host_port); - if ($self->session_token) { - $request->header( 'X-Amz-Security-Token' => $self->session_token ); + if ($creds->session_token) { + $request->header( 'X-Amz-Security-Token' => $creds->session_token ); } my $name = $self->can('signing_name') ? $self->signing_name : $self->service; - my $sig = Net::Amazon::Signature::V4->new( $self->access_key, $self->secret_key, $self->_region_for_signature, $name ); + my $sig = Net::Amazon::Signature::V4->new( $creds->access_key, $creds->secret_key, $self->_region_for_signature, $name ); $sig->sign( $request ); } 1; diff --git a/lib/Paws/Net/V2Signature.pm b/lib/Paws/Net/V2Signature.pm index 863b23c64b..424ec2263a 100644 --- a/lib/Paws/Net/V2Signature.pm +++ b/lib/Paws/Net/V2Signature.pm @@ -69,15 +69,17 @@ sub _split_url { } sub sign { - my ($self, $request) = @_; + my ($self, $request, $creds) = @_; + + $creds ||= $self->credentials->refresh; $request->parameters->{ SignatureVersion } = "2"; $request->parameters->{ SignatureMethod } = "HmacSHA256"; $request->parameters->{ Timestamp } //= strftime("%Y-%m-%dT%H:%M:%SZ",gmtime); - $request->parameters->{ AWSAccessKeyId } = $self->access_key; + $request->parameters->{ AWSAccessKeyId } = $creds->access_key; - if ($self->session_token) { - $request->parameters->{ SecurityToken } = $self->session_token; + if ($creds->session_token) { + $request->parameters->{ SecurityToken } = $creds->session_token; } my %sign_hash = %{ $request->parameters }; @@ -88,7 +90,7 @@ sub sign { $sign_this .= $self->www_form_urlencode(\%sign_hash); - my $encoded = encode_base64(hmac_sha256($sign_this, $self->secret_key), ''); + my $encoded = encode_base64(hmac_sha256($sign_this, $creds->secret_key), ''); $request->parameters->{ Signature } = $encoded; diff --git a/lib/Paws/Net/V4Signature.pm b/lib/Paws/Net/V4Signature.pm index 4a65edd39f..2fac8a84f4 100644 --- a/lib/Paws/Net/V4Signature.pm +++ b/lib/Paws/Net/V4Signature.pm @@ -22,19 +22,21 @@ package Paws::Net::V4Signature; } sub sign { - my ($self, $request) = @_; + my ($self, $request, $creds) = @_; + + $creds ||= $self->credentials->refresh; $request->header( Date => $request->header('X-Amz-Date') // strftime( '%Y%m%dT%H%M%SZ', gmtime ) ); $request->header( 'Host' => $self->endpoint->default_port == $self->endpoint->port ? $self->endpoint->host : $self->endpoint->host_port); - if ($self->session_token) { - $request->header( 'X-Amz-Security-Token' => $self->session_token ); + if ($creds->session_token) { + $request->header( 'X-Amz-Security-Token' => $creds->session_token ); } my $name = $self->can('signing_name') ? $self->signing_name : $self->service; - my $sig = Net::Amazon::Signature::V4->new( $self->access_key, $self->secret_key, $self->_region_for_signature, $name ); + my $sig = Net::Amazon::Signature::V4->new( $creds->access_key, $creds->secret_key, $self->_region_for_signature, $name ); $sig->sign( $request ); } 1; diff --git a/t/04_credentials.t b/t/04_credentials.t index 8a99933ae4..6661995378 100644 --- a/t/04_credentials.t +++ b/t/04_credentials.t @@ -53,8 +53,10 @@ delete @ENV{qw( my $creds = Paws::Credential::Environment->new; ok($creds->are_set, 'Creds are set'); - cmp_ok($creds->access_key, 'eq', 'botoAK', 'Access Key boto style'); - cmp_ok($creds->secret_key, 'eq', 'botoSK', 'Secret Key boto style'); + my $a = $creds->refresh; + ok($a, 'Refresh returns a value'); + cmp_ok($a->access_key, 'eq', 'botoAK', 'Access Key boto style'); + cmp_ok($a->secret_key, 'eq', 'botoSK', 'Secret Key boto style'); } { @@ -63,42 +65,54 @@ delete @ENV{qw( my $creds = Paws::Credential::Environment->new; ok($creds->are_set, 'Creds are set'); - cmp_ok($creds->access_key, 'eq', 'AK', 'Access Key short style'); - cmp_ok($creds->secret_key, 'eq', 'SK', 'Secret Key short style'); + my $a = $creds->refresh; + ok($a, 'Refresh returns a value'); + cmp_ok($a->access_key, 'eq', 'AK', 'Access Key short style'); + cmp_ok($a->secret_key, 'eq', 'SK', 'Secret Key short style'); } { my $creds = Paws::Credential::InstanceProfile->new(ua => Test04::StubUAForMetadata->new); - cmp_ok($creds->access_key, 'eq', 'AK1', 'Access Key 1'); - cmp_ok($creds->secret_key, 'eq', 'SK1', 'Secret Key 1'); - cmp_ok($creds->session_token, 'eq', 'TK1', 'Token 1'); + + my $a = $creds->refresh; + ok($a, 'Refresh returns a value'); + cmp_ok($a->access_key, 'eq', 'AK1', 'Access Key 1'); + cmp_ok($a->secret_key, 'eq', 'SK1', 'Secret Key 1'); + cmp_ok($a->session_token, 'eq', 'TK1', 'Token 1'); sleep 2; - cmp_ok($creds->access_key, 'eq', 'AK2', 'Access Key 2'); - cmp_ok($creds->secret_key, 'eq', 'SK2', 'Secret Key 2'); - cmp_ok($creds->session_token, 'eq', 'TK2', 'Token 2'); + $a = $creds->refresh; + ok($a, 'Refresh returns a value'); + cmp_ok($a->access_key, 'eq', 'AK2', 'Access Key 2'); + cmp_ok($a->secret_key, 'eq', 'SK2', 'Secret Key 2'); + cmp_ok($a->session_token, 'eq', 'TK2', 'Token 2'); sleep 2; - dies_ok { $creds->access_key } 'Exception thrown when garbage arrives'; + dies_ok { $creds->refresh } 'Exception thrown when garbage arrives'; } { my $creds = Paws::Credential::InstanceProfileV2->new(ua => Test04::StubUAForMetadata->new(check_header => 1)); - cmp_ok($creds->access_key, 'eq', 'AK1', 'Access Key 1 (IMDSv2)'); - cmp_ok($creds->secret_key, 'eq', 'SK1', 'Secret Key 1 (IMDSv2)'); - cmp_ok($creds->session_token, 'eq', 'TK1', 'Token 1 (IMDSv2)'); + + my $a = $creds->refresh; + ok($a, 'Refresh returns a value'); + cmp_ok($a->access_key, 'eq', 'AK1', 'Access Key 1 (IMDSv2)'); + cmp_ok($a->secret_key, 'eq', 'SK1', 'Secret Key 1 (IMDSv2)'); + cmp_ok($a->session_token, 'eq', 'TK1', 'Token 1 (IMDSv2)'); sleep 2; - cmp_ok($creds->access_key, 'eq', 'AK2', 'Access Key 2 (IMDSv2)'); - cmp_ok($creds->secret_key, 'eq', 'SK2', 'Secret Key 2 (IMDSv2)'); - cmp_ok($creds->session_token, 'eq', 'TK2', 'Token 2 (IMDSv2)'); + $a = $creds->refresh; + ok($a, 'Refresh returns a value'); + cmp_ok($a->access_key, 'eq', 'AK2', 'Access Key 2 (IMDSv2)'); + cmp_ok($a->secret_key, 'eq', 'SK2', 'Secret Key 2 (IMDSv2)'); + cmp_ok($a->session_token, 'eq', 'TK2', 'Token 2 (IMDSv2)'); sleep 2; - dies_ok { $creds->access_key } 'Exception thrown when garbage arrives (IMDSv2)'; + dies_ok { $creds->refresh } 'Exception thrown when garbage arrives (IMDSv2)'; } { @@ -122,26 +136,32 @@ delete @ENV{qw( my $creds = Paws::Credential::ECSContainerProfile->new(container_local_uri => '/metadata', ua => Test04::StubUAForECSMetadata->new); cmp_ok($creds->metadata_url, 'eq', "http://169.254.170.2/metadata"); - cmp_ok($creds->access_key, 'eq', 'AK1', 'ECS Access Key 1'); - cmp_ok($creds->secret_key, 'eq', 'SK1', 'ECS Secret Key 1'); - cmp_ok($creds->session_token, 'eq', 'TK1', 'ECS Token 1'); + my $a = $creds->refresh; + ok($a, 'Refresh returns a value'); + cmp_ok($a->access_key, 'eq', 'AK1', 'ECS Access Key 1'); + cmp_ok($a->secret_key, 'eq', 'SK1', 'ECS Secret Key 1'); + cmp_ok($a->session_token, 'eq', 'TK1', 'ECS Token 1'); sleep 2; - cmp_ok($creds->access_key, 'eq', 'AK2', 'ECS Access Key 2'); - cmp_ok($creds->secret_key, 'eq', 'SK2', 'ECS Secret Key 2'); - cmp_ok($creds->session_token, 'eq', 'TK2', 'ECS Token 2'); + $a = $creds->refresh; + ok($a, 'Refresh returns a value'); + cmp_ok($a->access_key, 'eq', 'AK2', 'ECS Access Key 2'); + cmp_ok($a->secret_key, 'eq', 'SK2', 'ECS Secret Key 2'); + cmp_ok($a->session_token, 'eq', 'TK2', 'ECS Token 2'); sleep 2; - dies_ok { $creds->access_key } 'Exception thrown when garbage arrives to ECS Provider'; + dies_ok { $creds->refresh } 'Exception thrown when garbage arrives to ECS Provider'; } { my $creds = Paws::Credential::ProviderChain->new(providers => [ 'Test::CustomCredentials', 'Paws::Credentail::Environment' ]); ok($creds->are_set, 'Creds are set'); - cmp_ok($creds->access_key, 'eq', 'CustomAK', 'Access Key short style'); - cmp_ok($creds->secret_key, 'eq', 'CustomSK', 'Secret Key short style'); + my $a = $creds->refresh; + ok($a, 'Refresh returns a value'); + cmp_ok($a->access_key, 'eq', 'CustomAK', 'Access Key short style'); + cmp_ok($a->secret_key, 'eq', 'CustomSK', 'Secret Key short style'); } ## File provider testing @@ -168,8 +188,10 @@ delete @ENV{qw( path => 't/04_credentials/' ); ok($creds->are_set, 'File: Attribute path works correctly'); - cmp_ok($creds->access_key, 'eq', 'defaultAK', 'File: default Access Key loaded correctly'); - cmp_ok($creds->secret_key, 'eq', 'defaultSK', 'File: default Secret Key loaded correctly'); + my $a = $creds->refresh; + ok($a, 'Refresh returns a value'); + cmp_ok($a->access_key, 'eq', 'defaultAK', 'File: default Access Key loaded correctly'); + cmp_ok($a->secret_key, 'eq', 'defaultSK', 'File: default Secret Key loaded correctly'); } { @@ -178,8 +200,10 @@ delete @ENV{qw( profile => 'testprofile' ); ok($creds->are_set, 'File: Attributes path and profile work correctly'); - cmp_ok($creds->access_key, 'eq', 'testAK', 'File: named profile Access Key loaded correctly'); - cmp_ok($creds->secret_key, 'eq', 'testSK', 'File: named profile Secret Key loaded correctly'); + my $a = $creds->refresh; + ok($a, 'Refresh returns a value'); + cmp_ok($a->access_key, 'eq', 'testAK', 'File: named profile Access Key loaded correctly'); + cmp_ok($a->secret_key, 'eq', 'testSK', 'File: named profile Secret Key loaded correctly'); } { @@ -187,8 +211,10 @@ delete @ENV{qw( credentials_file => 't/04_credentials/credentials.alternate', ); ok($creds->are_set, 'File: credentials_file attribute works correctly'); - cmp_ok($creds->access_key, 'eq', 'alternateAK', 'File: alternate Access Key loaded correctly'); - cmp_ok($creds->secret_key, 'eq', 'alternateSK', 'File: alternate Secret Key loaded correctly'); + my $a = $creds->refresh; + ok($a, 'Refresh returns a value'); + cmp_ok($a->access_key, 'eq', 'alternateAK', 'File: alternate Access Key loaded correctly'); + cmp_ok($a->secret_key, 'eq', 'alternateSK', 'File: alternate Secret Key loaded correctly'); } { @@ -197,8 +223,10 @@ delete @ENV{qw( file_name => 'credentials.alternate', ); ok($creds->are_set, 'File: file_name attribute works correctly'); - cmp_ok($creds->access_key, 'eq', 'alternateAK', 'File: alternate Access Key loaded correctly'); - cmp_ok($creds->secret_key, 'eq', 'alternateSK', 'File: alternate Secret Key loaded correctly'); + my $a = $creds->refresh; + ok($a, 'Refresh returns a value'); + cmp_ok($a->access_key, 'eq', 'alternateAK', 'File: alternate Access Key loaded correctly'); + cmp_ok($a->secret_key, 'eq', 'alternateSK', 'File: alternate Secret Key loaded correctly'); } { @@ -207,10 +235,12 @@ delete @ENV{qw( my $creds = Paws::Credential::File->new; ok($creds->are_set, 'File: Attributes from environment variables'); - cmp_ok($creds->access_key, 'eq', 'alternateprofileAK', - 'File: alternate using ENV variables Access Key loaded correctly'); - cmp_ok($creds->secret_key, 'eq', 'alternateprofileSK', + my $a = $creds->refresh; + ok($a, 'Refresh returns a value'); + cmp_ok($a->access_key, 'eq', 'alternateprofileAK', + 'File: alternate using ENV variables Access Key loaded correctly'); + cmp_ok($a->secret_key, 'eq', 'alternateprofileSK', 'File: alternate using ENV variables Secret Key loaded correctly'); } @@ -220,9 +250,11 @@ delete @ENV{qw( ); ok($creds->are_set, 'CredProcess: creds are set'); - cmp_ok($creds->access_key, 'eq', 'PCAccessKey', 'process: Access Key loaded correctly'); - cmp_ok($creds->secret_key, 'eq', 'PCSecretKey', 'process: Secret Key loaded correctly'); - cmp_ok($creds->session_token, 'eq', 'PCSessionToken', 'process: Session Token loaded correctly'); + my $a = $creds->refresh; + ok($a, 'Refresh returns a value'); + cmp_ok($a->access_key, 'eq', 'PCAccessKey', 'process: Access Key loaded correctly'); + cmp_ok($a->secret_key, 'eq', 'PCSecretKey', 'process: Secret Key loaded correctly'); + cmp_ok($a->session_token, 'eq', 'PCSessionToken', 'process: Session Token loaded correctly'); ok(not(defined $creds->expiration), 'Creds don\'t expire'); } @@ -245,9 +277,9 @@ delete @ENV{qw( credential_process => 't/04_credentials/test_cred_process.expiry', ); - my $first_execution = $creds->access_key; # the test suite returns the timestamp of execution in the AK + my $first_execution = $creds->refresh->access_key; # the test suite returns the timestamp of execution in the AK sleep 1; - my $second_execution = $creds->access_key; # the test suite returns the timestamp of execution in the AK + my $second_execution = $creds->refresh->access_key; # the test suite returns the timestamp of execution in the AK cmp_ok($first_execution, 'ne', $second_execution, 'Expiring credentials have been refreshed'); } @@ -260,9 +292,11 @@ delete @ENV{qw( ); ok($creds->are_set, 'File with credentials_process'); - cmp_ok($creds->access_key, 'eq', 'PCAccessKey', 'process: Access Key loaded correctly'); - cmp_ok($creds->secret_key, 'eq', 'PCSecretKey', 'process: Secret Key loaded correctly'); - cmp_ok($creds->session_token, 'eq', 'PCSessionToken', 'process: Session Token loaded correctly'); + my $a = $creds->refresh; + ok($a, 'Refresh returns a value'); + cmp_ok($a->access_key, 'eq', 'PCAccessKey', 'process: Access Key loaded correctly'); + cmp_ok($a->secret_key, 'eq', 'PCSecretKey', 'process: Secret Key loaded correctly'); + cmp_ok($a->session_token, 'eq', 'PCSessionToken', 'process: Session Token loaded correctly'); } { @@ -290,9 +324,9 @@ delete @ENV{qw( profile => 'expiry', ); - my $first_execution = $creds->access_key; # the test suite returns the timestamp of execution in the AK + my $first_execution = $creds->refresh->access_key; # the test suite returns the timestamp of execution in the AK sleep 1; - my $second_execution = $creds->access_key; # the test suite returns the timestamp of execution in the AK + my $second_execution = $creds->refresh->access_key; # the test suite returns the timestamp of execution in the AK cmp_ok($first_execution, 'ne', $second_execution, 'Expiring credentials have been refreshed'); } diff --git a/t/lib/Test/CustomCredentials.pm b/t/lib/Test/CustomCredentials.pm index be0bb1f119..b2cb8cef89 100644 --- a/t/lib/Test/CustomCredentials.pm +++ b/t/lib/Test/CustomCredentials.pm @@ -1,9 +1,13 @@ package Test::CustomCredentials; use Moose; use Paws::Credential; + use Paws::Credential::Explicit; with 'Paws::Credential'; - sub access_key { 'CustomAK' }; - sub secret_key { 'CustomSK' }; - sub session_token {}; + sub refresh { + return Paws::Credential::Explicit->new( + access_key => 'CustomAK', + secret_key => 'CustomSK', + ); + } 1;