feat(auth): implement SigV4 authentication for REST catalog#616
feat(auth): implement SigV4 authentication for REST catalog#616plusplusjiajia wants to merge 4 commits intoapache:mainfrom
Conversation
bfc98f7 to
9ddbdbd
Compare
915c87b to
d1c0732
Compare
| ICEBERG_PRECHECK(delegate_type != AuthProperties::kAuthTypeSigV4, | ||
| "Cannot delegate a SigV4 auth manager to another SigV4 auth manager"); |
There was a problem hiding this comment.
add delegate_type in error message?
There was a problem hiding this comment.
add delegate_type in error message?
Good idea, done.
| - name: Install dependencies | ||
| shell: bash | ||
| run: sudo apt-get update && sudo apt-get install -y libcurl4-openssl-dev | ||
| run: sudo apt-get update && sudo apt-get install -y libcurl4-openssl-dev ninja-build |
There was a problem hiding this comment.
n00b question why is ninja-build required here?
There was a problem hiding this comment.
Good question — I added a build step in this PR so the linter can see the SigV4 code (needs compile_commands.json from a real build). I used cmake -G Ninja for speed and to be consistent with the other CI workflows, and Ninja is not preinstalled on ubuntu-24.04, hence the extra ninja-build package. Happy to switch to Make if you'd prefer.
| if (session_token_it != properties.end() && !session_token_it->second.empty()) { | ||
| Aws::Auth::AWSCredentials credentials(access_key_it->second.c_str(), | ||
| secret_key_it->second.c_str(), | ||
| session_token_it->second.c_str()); | ||
| return std::make_shared<Aws::Auth::SimpleAWSCredentialsProvider>(credentials); | ||
| } | ||
| Aws::Auth::AWSCredentials credentials(access_key_it->second.c_str(), | ||
| secret_key_it->second.c_str()); | ||
| return std::make_shared<Aws::Auth::SimpleAWSCredentialsProvider>(credentials); |
There was a problem hiding this comment.
Nit: could do only one return if Credentials are created in the conditional statement.
There was a problem hiding this comment.
Nit: could do only one return if Credentials are created in the conditional statement.
Nice catch, done.
| auto it = properties.find(AuthProperties::kSigV4SigningName); | ||
| if (it != properties.end() && !it->second.empty()) { | ||
| return it->second; | ||
| } |
There was a problem hiding this comment.
if(properties.count(AuthProperties::kSigV4SigningName) > 0) {
// do work
}
might be a little less verbose than
auto it = properties.find(AuthProperties::kSigV4SigningName);
if (it != properties.end() && !it->second.empty()) {
return it->second;
}
There was a problem hiding this comment.
Thanks for the suggestion! I chose to keep the !it->second.empty() check on purpose — the intent is for an explicitly-empty value (e.g., a env var set to "") to also fall through to the legacy key / default.
| const TableIdentifier& table, | ||
| const std::unordered_map<std::string, std::string>& properties, | ||
| std::shared_ptr<AuthSession> parent) { | ||
| auto* sigv4_parent = dynamic_cast<SigV4AuthSession*>(parent.get()); |
There was a problem hiding this comment.
use checked_pointer_cast instead
There was a problem hiding this comment.
use
checked_pointer_castinstead
Done here as well.
| Result<std::shared_ptr<AuthSession>> SigV4AuthManager::ContextualSession( | ||
| const std::unordered_map<std::string, std::string>& context, | ||
| std::shared_ptr<AuthSession> parent) { | ||
| auto* sigv4_parent = dynamic_cast<SigV4AuthSession*>(parent.get()); |
There was a problem hiding this comment.
use
checked_pointer_cast
Thanks, that's much better! Done.
| std::string signing_region_; | ||
| std::string signing_name_; | ||
| std::shared_ptr<Aws::Auth::AWSCredentialsProvider> credentials_provider_; | ||
| /// Shared signer instance, matching Java's single Aws4Signer per manager. |
There was a problem hiding this comment.
I find this comment a bit confusing especially given that signer_ is a unique pointer that will be destroyed when SigV4AuthSession is destructed
There was a problem hiding this comment.
You're right, sorry about the confusion — the "shared signer" wording was misleading since signer_ is owned per-session via unique_ptr. I've removed the comment.
|
|
||
| std::unordered_map<std::string, std::string> headers; | ||
| ASSERT_THAT(session_result.value()->Authenticate(headers), IsOk()); | ||
| ASSERT_THAT(session_result.value()->Authenticate(headers, {}), IsOk()); |
There was a problem hiding this comment.
it feels like Authenticate coudl accept a default value for the second parameter
There was a problem hiding this comment.
Good point, thanks! Done
Implement AWS SigV4 authentication for the REST catalog client, following Java's
RESTSigV4AuthManagerandRESTSigV4AuthSession.AuthSession::Authenticate()withHTTPRequestContext(method, url, body) for SigV4 request signingSigV4AuthSession: delegate-first auth → relocate conflicting Authorization header → sign with AWS SDKSigV4AuthManager: wraps delegate AuthManager (default OAuth2), resolves credentials from properties or default chainSignerChecksumParamsoutput: empty body → hexEMPTY_BODY_SHA256; non-empty body →Base64(SHA256(body))