/
githubmirror
/
postgres
Обзор
Документация
Войти
/
githubmirror
/
postgres
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/test/modules/oauth_validator/t/002_client.pl
222 строки
6 KB
Jacob Champion
libpq: Introduce PQAUTHDATA_OAUTH_BEARER_TOKEN_V2
06 мар 2026, 23:05
06 мар 2026, 23:05
e982331
Код
Авторство
О чём код?
# # Exercises the API for custom OAuth client flows, using the oauth_hook_client # test driver. # # Copyright (c) 2021-2026, PostgreSQL Global Development Group # use strict; use warnings FATAL => 'all'; use JSON::PP qw(encode_json); use MIME::Base64 qw(encode_base64); use PostgreSQL::Test::Cluster; use PostgreSQL::Test::Utils; use Test::More; if (!$ENV{PG_TEST_EXTRA} || $ENV{PG_TEST_EXTRA} !~ /\boauth\b/) { plan skip_all => 'Potentially unsafe test oauth not enabled in PG_TEST_EXTRA'; } # # Cluster Setup # my $node = PostgreSQL::Test::Cluster->new('primary'); $node->init; $node->append_conf('postgresql.conf', "log_connections = all\n"); $node->append_conf('postgresql.conf', "oauth_validator_libraries = 'validator'\n"); # Needed to inspect postmaster log after connection failure: $node->append_conf('postgresql.conf', "log_min_messages = debug2"); $node->start; $node->safe_psql('postgres', 'CREATE USER test;'); # These tests don't use the builtin flow, and we don't have an authorization # server running, so the address used here shouldn't matter. Use an invalid IP # address, so if there's some cascade of errors that causes the client to # attempt a connection, we'll fail noisily. my $issuer = "https://256.256.256.256"; my $scope = "openid postgres"; unlink($node->data_dir . '/pg_hba.conf'); $node->append_conf( 'pg_hba.conf', qq{ local all test oauth issuer="$issuer" scope="$scope" }); $node->reload; $node->wait_for_log(qr/reloading configuration files/); $ENV{PGOAUTHDEBUG} = "UNSAFE"; # # Tests # my $user = "test"; my $base_connstr = $node->connstr() . " user=$user"; my $common_connstr = "$base_connstr oauth_issuer=$issuer oauth_client_id=myID"; sub test { my ($test_name, %params) = @_; my $flags = []; if (defined($params{flags})) { $flags = $params{flags}; } my @cmd = ("oauth_hook_client", @{$flags}, $common_connstr); note "running '" . join("' '", @cmd) . "'"; my $log_start = -s $node->logfile; my ($stdout, $stderr) = run_command(\@cmd); if ($params{expect_success}) { like($stdout, qr/connection succeeded/, "$test_name: stdout matches"); } if (defined($params{expected_stderr})) { like($stderr, $params{expected_stderr}, "$test_name: stderr matches"); } else { is($stderr, "", "$test_name: no stderr"); } if (defined($params{log_like})) { # See Cluster::connect_fails(). To avoid races, we have to wait for the # postmaster to flush the log for the finished connection. $node->wait_for_log( qr/DEBUG: (?:00000: )?forked new client backend, pid=(\d+) socket.*DEBUG: (?:00000: )?client backend \(PID \1\) exited with exit code \d/s, $log_start); $node->log_check("$test_name: log matches", $log_start, log_like => $params{log_like}); } } test( "basic synchronous hook can provide a token", flags => [ "--token", "my-token", "--expected-uri", "$issuer/.well-known/openid-configuration", "--expected-issuer", "$issuer", "--expected-scope", $scope, ], expect_success => 1, log_like => [qr/oauth_validator: token="my-token", role="$user"/]); # The issuer ID provided to the hook is based on, but not equal to, # oauth_issuer. Make sure the correct string is passed. $common_connstr = "$base_connstr oauth_issuer=$issuer/.well-known/openid-configuration oauth_client_id=myID oauth_scope='$scope'"; test( "derived issuer ID is correctly provided", flags => [ "--token", "my-token", "--expected-uri", "$issuer/.well-known/openid-configuration", "--expected-issuer", "$issuer", "--expected-scope", $scope, ], expect_success => 1, log_like => [qr/oauth_validator: token="my-token", role="$user"/]); $common_connstr = "$base_connstr oauth_issuer=$issuer oauth_client_id=myID"; # Make sure the v1 hook continues to work. test( "v1 synchronous hook can provide a token", flags => [ "-v1", "--token" => "my-token-v1", "--expected-uri" => "$issuer/.well-known/openid-configuration", "--expected-scope" => $scope, ], expect_success => 1, log_like => [qr/oauth_validator: token="my-token-v1", role="$user"/]); if ($ENV{with_libcurl} ne 'yes') { # libpq should help users out if no OAuth support is built in. test( "fails without custom hook installed", flags => ["--no-hook"], expected_stderr => qr/no OAuth flows are available \(try installing the libpq-oauth package\)/ ); } # v2 synchronous flows should be able to set custom error messages. test( "basic synchronous hook can set error messages", flags => [ "--error" => "a custom error message", ], expected_stderr => qr/user-defined OAuth flow failed: a custom error message/); # connect_timeout should work if the flow doesn't respond. $common_connstr = "$common_connstr connect_timeout=1"; test( "connect_timeout interrupts hung client flow", flags => ["--hang-forever"], expected_stderr => qr/failed: timeout expired/); # Remove the timeout for later tests. $common_connstr = "$base_connstr oauth_issuer=$issuer oauth_client_id=myID"; # Test various misbehaviors of the client hook. my @cases = ( { flag => "--misbehave=no-hook", expected_error => qr/user-defined OAuth flow provided neither a token nor an async callback/, }, { flag => "--misbehave=fail-async", expected_error => qr/user-defined OAuth flow failed/, }, { flag => "--misbehave=no-token", expected_error => qr/user-defined OAuth flow did not provide a token/, }, { flag => "--misbehave=no-socket", expected_error => qr/user-defined OAuth flow did not provide a socket for polling/, }); foreach my $c (@cases) { test( "hook misbehavior: $c->{'flag'}", flags => [ $c->{'flag'} ], expected_stderr => $c->{'expected_error'}); test( "hook misbehavior: $c->{'flag'} (v1)", flags => [ '-v1', $c->{'flag'} ], expected_stderr => $c->{'expected_error'}); } # v2 async flows should be able to set error messages, too. test( "asynchronous hook can set error messages", flags => [ "--misbehave" => "fail-async", "--error" => "async error message", ], expected_stderr => qr/user-defined OAuth flow failed: async error message/); done_testing();