view release on metacpan or search on metacpan
if (ec_sig) ECDSA_SIG_free(ec_sig);
if (der_buf) Safefree(der_buf);
if (verify_res > 0) {
XSRETURN_YES;
} else {
XSRETURN_NO;
}
SV *
rsa_sign_sha256(SV *private_key_pem_sv, SV *message_sv)
PREINIT:
char *pem_str = NULL;
char *msg_str = NULL;
STRLEN pem_len, msg_len;
BIO *bio = NULL;
EVP_PKEY *pkey = NULL;
EVP_MD_CTX *mdctx = NULL;
unsigned char sig[4096];
unsigned int sig_len = 0;
SV *retval = NULL;
CODE:
pem_str = SvPV(private_key_pem_sv, pem_len);
msg_str = SvPV(message_sv, msg_len);
bio = BIO_new_mem_buf(pem_str, pem_len);
if (!bio) XSRETURN_UNDEF;
pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
BIO_free(bio);
if (!pkey) XSRETURN_UNDEF;
mdctx = EVP_MD_CTX_new();
lib/Google/Auth/DefaultCredentials.pm view on Meta::CPAN
scope => $scopes,
%options
);
}
# If no explicit json info was provided, fallback to checking environment variables
my $env = $self->environment;
if ($ENV{GOOGLE_PRIVATE_KEY} && $ENV{GOOGLE_CLIENT_EMAIL}) {
require Google::Auth::ServiceAccountCredentials;
return Google::Auth::ServiceAccountCredentials->new(
private_key => $ENV{GOOGLE_PRIVATE_KEY},
client_email => $ENV{GOOGLE_CLIENT_EMAIL},
scope => $scopes,
%options
);
}
return;
}
sub from_env {
lib/Google/Auth/ServiceAccountCredentials.pm view on Meta::CPAN
use Google::Auth;
use Google::Auth::Exceptions;
use Google::Auth::RetryHelper;
use Log::Any qw($log);
has json_key => (
is => 'ro',
required => 0,
);
has private_key_id => (
is => 'ro',
required => 0,
);
has private_key => (
is => 'ro',
required => 0,
);
has client_email => (
is => 'ro',
required => 0,
);
has client_id => (
lib/Google/Auth/ServiceAccountCredentials.pm view on Meta::CPAN
return $ua;
},
);
around BUILDARGS => sub {
my ($orig, $class, @args) = @_;
my $args = $class->$orig(@args);
if (my $json = $args->{json_key}) {
$args->{project_id} //= $json->{project_id};
$args->{private_key_id} //= $json->{private_key_id};
$args->{private_key} //= $json->{private_key};
$args->{client_email} //= $json->{client_email};
$args->{client_id} //= $json->{client_id};
$args->{auth_uri} //= $json->{auth_uri};
$args->{token_uri} //= $json->{token_uri};
$args->{auth_provider_x509_cert_url} //=
$json->{auth_provider_x509_cert_url};
$args->{client_x509_cert_url} //= $json->{client_x509_cert_url};
}
return $args;
lib/Google/Auth/ServiceAccountCredentials.pm view on Meta::CPAN
sub _encode_base64url {
my ($data) = @_;
my $s = MIME::Base64::encode_base64url($data);
$s =~ s/=+$//;
return $s;
}
sub fetch_access_token {
my ($self, %options) = @_;
my $private_key = $self->private_key;
my $client_email = $self->client_email;
my $token_uri = $self->token_uri;
$self->_validate_url($token_uri, 'token_uri');
if (!defined $private_key || !defined $client_email) {
$log->errorf(
'Missing private_key or client_email for ServiceAccountCredentials token exchange'
);
Google::Auth::Error->throw(
'Missing private_key or client_email to sign and fetch token');
}
my $now = time();
my $header = {
alg => 'RS256',
typ => 'JWT',
};
$header->{kid} = $self->private_key_id if defined $self->private_key_id;
my $scope = $self->scope;
if (ref($scope) eq 'ARRAY') {
$scope = join(' ', @$scope);
}
my $payload = {
iss => $client_email,
sub => $client_email,
aud => $token_uri,
exp => $now + 3600,
iat => $now,
};
$payload->{scope} = $scope if defined $scope;
my $header_b64 = _encode_base64url(encode_json($header));
my $payload_b64 = _encode_base64url(encode_json($payload));
my $message = $header_b64 . '.' . $payload_b64;
$log->tracef('Signing JWT assertion for service account %s (key ID: %s)...',
$client_email, $self->private_key_id // 'N/A');
my $signature_raw = Google::Auth::rsa_sign_sha256($private_key, $message);
if (!defined $signature_raw) {
$log->errorf('Failed to sign JWT assertion for service account %s',
$client_email);
Google::Auth::Error->throw(
'Failed to sign JWT assertion using private key');
}
my $signature_b64 = _encode_base64url($signature_raw);
my $assertion = $message . '.' . $signature_b64;
my $ua = $self->ua;
t/05-default-credentials.t view on Meta::CPAN
&& ($tmpdir eq '\\' || $tmpdir eq '/' || $tmpdir =~ /^[a-zA-Z]:\\?$/))
{
$tmpdir = $ENV{RUNNER_TEMP} || $ENV{TEMP} || $ENV{TMP} || '.';
}
($tmpdir) = $tmpdir =~ /^(.*)$/;
subtest 'from_env loading valid service account credentials' => sub {
my $sa_data = {
type => 'service_account',
project_id => 'test-project-123',
private_key_id => 'abcd1234efgh5678',
private_key =>
'-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----',
client_email => 'test-sa@test-project-123.iam.gserviceaccount.com',
client_id => '1234567890',
auth_uri => 'https://accounts.google.com/o/oauth2/auth',
token_uri => 'https://oauth2.googleapis.com/token',
auth_provider_x509_cert_url => 'https://www.googleapis.com/oauth2/v1/certs',
client_x509_cert_url =>
'https://www.googleapis.com/robot/v1/metadata/x509/test-sa%40test-project-123.iam.gserviceaccount.com',
};
my $sa_json = encode_json($sa_data);
t/05-default-credentials.t view on Meta::CPAN
ok(defined $creds, 'credentials returned');
isa_ok($creds, 'Google::Auth::ServiceAccountCredentials');
if ($creds) {
is($creds->project_id, 'test-project-123', 'project_id matches');
is(
$creds->client_email,
'test-sa@test-project-123.iam.gserviceaccount.com',
'client_email matches'
);
is($creds->private_key_id, 'abcd1234efgh5678', 'private_key_id matches');
}
delete $ENV{GOOGLE_APPLICATION_CREDENTIALS};
};
subtest 'from_env loading invalid JSON' => sub {
my ($fh, $filename) = tempfile(UNLINK => 1, DIR => $tmpdir);
print $fh 'not a valid json string';
close($fh);
t/13-impersonated-credentials.t view on Meta::CPAN
subtest 'Load from JSON configuration' => sub {
my $json_config = {
type => 'impersonated_service_account',
service_account_impersonation_url =>
'https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/target-sa@google.com:generateAccessToken',
scopes => ['https://www.googleapis.com/auth/compute'],
source_credentials => {
type => 'service_account',
project_id => 'test-project',
client_email => 'source-sa@google.com',
private_key => 'some-private-key',
}};
require Google::Auth::DefaultCredentials;
my $creds =
Google::Auth::DefaultCredentials->make_creds(json_key => $json_config,);
ok(defined $creds, 'loaded credentials successfully');
isa_ok($creds, 'Google::Auth::ImpersonatedServiceAccountCredentials');
is(
$creds->impersonation_url,
t/17-logging-verification.t view on Meta::CPAN
use Google::Auth::RetryHelper;
subtest 'ServiceAccountCredentials Logging' => sub {
my $mock_ua = Test::LWP::UserAgent->new();
my $keypair = Google::Auth::generate_self_signed_cert();
my $valid_pkey = $keypair->{key};
my $creds = Google::Auth::ServiceAccountCredentials->new(
project_id => 'my-project',
private_key => $valid_pkey,
client_email => 'test-sa@google.com',
private_key_id => 'key-12345',
token_uri => 'https://oauth2.googleapis.com/token',
ua => $mock_ua,
);
$mock_ua->map_response(
qr/oauth2.googleapis.com\/token/,
HTTP::Response->new(
200, 'OK',
['Content-Type' => 'application/json'],
encode_json({
t/17-logging-verification.t view on Meta::CPAN
};
subtest 'RetryHelper and Error Logging' => sub {
my $mock_ua = Test::LWP::UserAgent->new();
my $keypair = Google::Auth::generate_self_signed_cert();
my $valid_pkey = $keypair->{key};
my $creds = Google::Auth::ServiceAccountCredentials->new(
project_id => 'my-project',
private_key => $valid_pkey,
client_email => 'test-sa@google.com',
token_uri => 'https://oauth2.googleapis.com/token',
ua => $mock_ua,
);
$mock_ua->map_response(
qr/oauth2.googleapis.com\/token/,
HTTP::Response->new(
503,
'Service Unavailable',
t/18-sa-token-uri-ssrf.t view on Meta::CPAN
# 1. Test invalid domain (SSRF Sink)
subtest 'ServiceAccountCredentials token_uri SSRF Validation' => sub {
plan tests => 2;
my $mock_ua = Test::LWP::UserAgent->new();
my $sa = Google::Auth::DefaultCredentials->make_creds(
json_key => {
type => 'service_account',
project_id => 'victim-project',
private_key_id => 'victim-key-id',
private_key => $valid_pkey,
client_email => 'victim-sa@victim-project.iam.gserviceaccount.com',
token_uri => 'http://evil.com/token', # Attacker URL
},
scope => 'https://www.googleapis.com/auth/cloud-platform',
ua => $mock_ua,
);
# It should throw before even using UA if validation works
my $token = eval { $sa->fetch_access_token() };
my $err = $@;
t/18-sa-token-uri-ssrf.t view on Meta::CPAN
['Content-Type' => 'application/json'],
encode_json({
access_token => 'valid-token',
expires_in => 3600
})));
my $sa = Google::Auth::DefaultCredentials->make_creds(
json_key => {
type => 'service_account',
project_id => 'victim-project',
private_key_id => 'victim-key-id',
private_key => $valid_pkey,
client_email => 'victim-sa@victim-project.iam.gserviceaccount.com',
token_uri => 'https://oauth2.googleapis.com/token', # Valid URL
},
scope => 'https://www.googleapis.com/auth/cloud-platform',
ua => $mock_ua,
);
my $token = eval { $sa->fetch_access_token() };
is($@, '', 'no exception thrown for valid URL');
is($token, 'valid-token', 'token fetched successfully');