-
Notifications
You must be signed in to change notification settings - Fork 103
feat(auth): implement SigV4 authentication for REST catalog #616
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
eaa433f
4326282
6e12fbd
eb74231
6a9cd32
abb2cf6
4197ceb
f26927e
655be23
39f4164
743a444
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -45,6 +45,7 @@ option(ICEBERG_BUILD_TESTS "Build tests" ON) | |
| option(ICEBERG_BUILD_BUNDLE "Build the battery included library" ON) | ||
| option(ICEBERG_BUILD_REST "Build rest catalog client" ON) | ||
| option(ICEBERG_BUILD_REST_INTEGRATION_TESTS "Build rest catalog integration tests" OFF) | ||
| option(ICEBERG_BUILD_SIGV4 "Build SigV4 authentication support (requires AWS SDK)" OFF) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please rebase on the latest main branch so we can see the option |
||
| option(ICEBERG_ENABLE_ASAN "Enable Address Sanitizer" OFF) | ||
| option(ICEBERG_ENABLE_UBSAN "Enable Undefined Behavior Sanitizer" OFF) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -531,3 +531,21 @@ endif() | |
| if(ICEBERG_BUILD_REST) | ||
| resolve_cpr_dependency() | ||
| endif() | ||
|
|
||
| # ---------------------------------------------------------------------- | ||
| # AWS SDK for C++ | ||
|
|
||
| function(resolve_aws_sdk_dependency) | ||
| find_package(AWSSDK REQUIRED COMPONENTS core) | ||
| list(APPEND ICEBERG_SYSTEM_DEPENDENCIES AWSSDK) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here it records only AWSSDK for installed-package dependency discovery, while src/iceberg/catalog/rest/CMakeLists.txt exports I'd suggest to special-case find_dependency(AWSSDK COMPONENTS core) in the iceberg-config.cmake.in or otherwise export the AWS SDK dependency component-aware. |
||
| set(ICEBERG_SYSTEM_DEPENDENCIES | ||
| ${ICEBERG_SYSTEM_DEPENDENCIES} | ||
| PARENT_SCOPE) | ||
| endfunction() | ||
|
|
||
| if(ICEBERG_BUILD_SIGV4) | ||
| if(NOT ICEBERG_BUILD_REST) | ||
| message(FATAL_ERROR "ICEBERG_BUILD_SIGV4 requires ICEBERG_BUILD_REST to be ON") | ||
| endif() | ||
| resolve_aws_sdk_dependency() | ||
| endif() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,6 +33,10 @@ set(ICEBERG_REST_SOURCES | |
| rest_util.cc | ||
| types.cc) | ||
|
|
||
| if(ICEBERG_BUILD_SIGV4) | ||
| list(APPEND ICEBERG_REST_SOURCES auth/sigv4_auth_manager.cc) | ||
| endif() | ||
|
|
||
| set(ICEBERG_REST_STATIC_BUILD_INTERFACE_LIBS) | ||
| set(ICEBERG_REST_SHARED_BUILD_INTERFACE_LIBS) | ||
| set(ICEBERG_REST_STATIC_INSTALL_INTERFACE_LIBS) | ||
|
|
@@ -51,6 +55,13 @@ list(APPEND | |
| "$<IF:$<TARGET_EXISTS:iceberg::iceberg_shared>,iceberg::iceberg_shared,iceberg::iceberg_static>" | ||
| "$<IF:$<BOOL:${CPR_VENDORED}>,iceberg::cpr,cpr::cpr>") | ||
|
|
||
| if(ICEBERG_BUILD_SIGV4) | ||
| list(APPEND ICEBERG_REST_STATIC_BUILD_INTERFACE_LIBS aws-cpp-sdk-core) | ||
| list(APPEND ICEBERG_REST_SHARED_BUILD_INTERFACE_LIBS aws-cpp-sdk-core) | ||
| list(APPEND ICEBERG_REST_STATIC_INSTALL_INTERFACE_LIBS aws-cpp-sdk-core) | ||
| list(APPEND ICEBERG_REST_SHARED_INSTALL_INTERFACE_LIBS aws-cpp-sdk-core) | ||
| endif() | ||
|
|
||
| add_iceberg_lib(iceberg_rest | ||
| SOURCES | ||
| ${ICEBERG_REST_SOURCES} | ||
|
|
@@ -63,4 +74,12 @@ add_iceberg_lib(iceberg_rest | |
| SHARED_INSTALL_INTERFACE_LIBS | ||
| ${ICEBERG_REST_SHARED_INSTALL_INTERFACE_LIBS}) | ||
|
|
||
| if(ICEBERG_BUILD_SIGV4) | ||
| foreach(LIB iceberg_rest_static iceberg_rest_shared) | ||
| if(TARGET ${LIB}) | ||
| target_compile_definitions(${LIB} PUBLIC ICEBERG_BUILD_SIGV4) | ||
| endif() | ||
| endforeach() | ||
| endif() | ||
|
|
||
| iceberg_install_all_headers(iceberg/catalog/rest) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,4 +47,11 @@ Result<std::unique_ptr<AuthManager>> MakeOAuth2Manager( | |
| std::string_view name, | ||
| const std::unordered_map<std::string, std::string>& properties); | ||
|
|
||
| #ifdef ICEBERG_BUILD_SIGV4 | ||
| /// \brief Create a SigV4 authentication manager with a delegate. | ||
| Result<std::unique_ptr<AuthManager>> MakeSigV4AuthManager( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where is the definition? BTW, we don't need to use macro |
||
| std::string_view name, | ||
| const std::unordered_map<std::string, std::string>& properties); | ||
| #endif | ||
|
|
||
| } // namespace iceberg::rest::auth | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -62,11 +62,15 @@ std::string InferAuthType( | |
| } | ||
|
|
||
| AuthManagerRegistry CreateDefaultRegistry() { | ||
| return { | ||
| AuthManagerRegistry registry = { | ||
| {AuthProperties::kAuthTypeNone, MakeNoopAuthManager}, | ||
| {AuthProperties::kAuthTypeBasic, MakeBasicAuthManager}, | ||
| {AuthProperties::kAuthTypeOAuth2, MakeOAuth2Manager}, | ||
| }; | ||
| #ifdef ICEBERG_BUILD_SIGV4 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto, we don't need to use this macro here. |
||
| registry[AuthProperties::kAuthTypeSigV4] = MakeSigV4AuthManager; | ||
| #endif | ||
| return registry; | ||
| } | ||
|
|
||
| // Get the global registry of auth manager factories. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,10 +54,14 @@ class ICEBERG_REST_EXPORT AuthProperties : public ConfigBase<AuthProperties> { | |
|
|
||
| // ---- SigV4 entries ---- | ||
|
|
||
| inline static const std::string kSigV4Region = "rest.auth.sigv4.region"; | ||
| inline static const std::string kSigV4Service = "rest.auth.sigv4.service"; | ||
| inline static const std::string kSigV4DelegateAuthType = | ||
| "rest.auth.sigv4.delegate-auth-type"; | ||
| inline static const std::string kSigV4SigningRegion = "rest.signing-region"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can remove the legacy key kSigV4Region/kSigV4Service
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| inline static const std::string kSigV4SigningName = "rest.signing-name"; | ||
| inline static const std::string kSigV4SigningNameDefault = "execute-api"; | ||
| inline static const std::string kSigV4AccessKeyId = "rest.access-key-id"; | ||
| inline static const std::string kSigV4SecretAccessKey = "rest.secret-access-key"; | ||
| inline static const std::string kSigV4SessionToken = "rest.session-token"; | ||
|
|
||
| // ---- OAuth2 entries ---- | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ | |
| #include <string> | ||
| #include <unordered_map> | ||
|
|
||
| #include "iceberg/catalog/rest/endpoint.h" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would suggest moving |
||
| #include "iceberg/catalog/rest/iceberg_rest_export.h" | ||
| #include "iceberg/catalog/rest/type_fwd.h" | ||
| #include "iceberg/result.h" | ||
|
|
@@ -32,25 +33,37 @@ | |
|
|
||
| namespace iceberg::rest::auth { | ||
|
|
||
| /// \brief An outgoing HTTP request passed through an AuthSession. Mirrors the | ||
| /// HTTPRequest type used by the Java reference implementation so signing | ||
| /// implementations like SigV4 can operate on method, url, headers, and body | ||
| /// as a single value. | ||
| struct ICEBERG_REST_EXPORT HTTPRequest { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's rename it to
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BTW, it looks weird that its namespace is |
||
| HttpMethod method = HttpMethod::kGet; | ||
| std::string url; | ||
| std::unordered_map<std::string, std::string> headers; | ||
| std::string body; | ||
| }; | ||
|
|
||
| /// \brief An authentication session that can authenticate outgoing HTTP requests. | ||
| class ICEBERG_REST_EXPORT AuthSession { | ||
| public: | ||
| virtual ~AuthSession() = default; | ||
|
|
||
| /// \brief Authenticate the given request headers. | ||
| /// \brief Authenticate an outgoing HTTP request. | ||
| /// | ||
| /// This method adds authentication information (e.g., Authorization header) | ||
| /// to the provided headers map. The implementation should be idempotent. | ||
| /// Returns a new request with authentication information (e.g., an | ||
| /// Authorization header) added. Implementations must be idempotent and must | ||
| /// not mutate the input request. | ||
| /// | ||
| /// \param[in,out] headers The headers map to add authentication information to. | ||
| /// \return Status indicating success or one of the following errors: | ||
| /// \param request The request to authenticate. | ||
| /// \return The authenticated request on success, or one of: | ||
| /// - AuthenticationFailed: General authentication failure (invalid credentials, | ||
| /// etc.) | ||
| /// - TokenExpired: Authentication token has expired and needs refresh | ||
| /// - NotAuthorized: Not authenticated (401) | ||
| /// - IOError: Network or connection errors when reaching auth server | ||
| /// - RestError: HTTP errors from authentication service | ||
| virtual Status Authenticate(std::unordered_map<std::string, std::string>& headers) = 0; | ||
| virtual Result<HTTPRequest> Authenticate(const HTTPRequest& request) = 0; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that this change makes sense. |
||
|
|
||
| /// \brief Close the session and release any resources. | ||
| /// | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need changes in the file? Is it because unrecognized headers from sigv4_auth_manager.cc? Does disabling
ICEBERG_BUILD_SIGV4help in this case? I am thinking if we can add a dedicated ci workflow for aws-related stuff like S3 and SigV4