Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions .github/workflows/cpp-linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,39 @@ jobs:
- name: Install dependencies
shell: bash
run: sudo apt-get update && sudo apt-get install -y libcurl4-openssl-dev
- name: Set up Python for Conan
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install Conan
shell: bash
run: |
pip install 'conan>=2.1.0'
conan profile detect --force
- name: Resolve Conan dependencies
# Pulls aws-crt-cpp so the SigV4 source compiles under clang-tidy.
# Other REST deps (cpr, arrow, …) still come via FetchContent below.
shell: bash
env:
CC: gcc-14
CXX: g++-14
run: |
conan install . -s build_type=Release --build=missing
- name: Run build
env:
CC: gcc-14
CXX: g++-14
run: |
mkdir build && cd build
cmake .. -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake --build .
# The conanfile.py disables tests for the package build; override
# here so the test targets land in compile_commands.json and
# clang-tidy can resolve their includes (gmock/gtest). We also
# skip the arrow-heavy bundle so the lint job stays fast: the
# REST targets are what we need for this PR's lint coverage.
cmake --preset conan-release \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DICEBERG_BUILD_TESTS=ON \
-DICEBERG_BUILD_BUNDLE=OFF
cmake --build build/Release --target rest_catalog_test
- uses: cpp-linter/cpp-linter-action@0f6d1b8d7e38b584cbee606eb23d850c217d54f8
id: linter
continue-on-error: true
Expand All @@ -59,10 +84,10 @@ jobs:
lines-changed-only: true
thread-comments: true
ignore: 'build|cmake_modules|ci'
database: build
database: build/Release
verbosity: 'debug'
# need '-fno-builtin-std-forward_like', see https://github.com/llvm/llvm-project/issues/101614
extra-args: '-std=c++23 -I$PWD/src -I$PWD/build/src -fno-builtin-std-forward_like'
extra-args: '-std=c++23 -I$PWD/src -I$PWD/build/Release/src -fno-builtin-std-forward_like'
- name: Fail fast?!
if: steps.linter.outputs.checks-failed != 0
run: |
Expand Down
26 changes: 26 additions & 0 deletions cmake_modules/IcebergThirdpartyToolchain.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,32 @@ if(ICEBERG_BUILD_BUNDLE)
resolve_zstd_dependency()
endif()

# ----------------------------------------------------------------------
# aws-crt-cpp (optional, used by REST catalog SigV4 AuthManager).
# If not found, the REST client still builds and SigV4 returns NotImplemented
# at runtime. Install via Conan or vcpkg to enable AWS Glue integration.

function(resolve_aws_crt_cpp_dependency)
find_package(aws-crt-cpp CONFIG QUIET)
if(aws-crt-cpp_FOUND)
list(APPEND ICEBERG_SYSTEM_DEPENDENCIES aws-crt-cpp)
set(ICEBERG_REST_HAVE_SIGV4
TRUE
PARENT_SCOPE)
message(STATUS "Found aws-crt-cpp (${aws-crt-cpp_VERSION_STRING}); enabling REST "
"SigV4 auth")
set(ICEBERG_SYSTEM_DEPENDENCIES
${ICEBERG_SYSTEM_DEPENDENCIES}
PARENT_SCOPE)
else()
set(ICEBERG_REST_HAVE_SIGV4
FALSE
PARENT_SCOPE)
Comment on lines +591 to +593
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we just fail? This seems unlikely?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is for completeness as Meson build doesn't require the AWS CRT

message(STATUS "aws-crt-cpp not found; REST SigV4 auth will return NotImplemented")
endif()
endfunction()

if(ICEBERG_BUILD_REST)
resolve_cpr_dependency()
resolve_aws_crt_cpp_dependency()
endif()
5 changes: 5 additions & 0 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ def requirements(self):
self.requires("zlib/[>=1.2.11 <2]")
self.requires("snappy/[>=1.1 <2]")
self.requires("zstd/[>=1.5 <2]")
# AWS CRT (C++) — used by the REST catalog's SigV4 AuthManager to sign
# requests for AWS Glue's Iceberg REST endpoint. Pulls aws-c-auth,
# aws-c-common, aws-c-cal, aws-c-http, aws-c-io transitively.
self.requires("aws-crt-cpp/[>=0.26 <1]")

def generate(self):
tc = CMakeToolchain(self)
Expand Down Expand Up @@ -152,6 +156,7 @@ def package_info(self):
self.cpp_info.components["iceberg_rest"].requires = [
"iceberg",
"vendored_cpr",
"aws-crt-cpp::aws-crt-cpp" # Conan component; CMake target is AWS::aws-crt-cpp,
]

# Bundle (Arrow/Parquet integration)
Expand Down
18 changes: 18 additions & 0 deletions src/iceberg/catalog/rest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ set(ICEBERG_REST_SOURCES
rest_util.cc
types.cc)

if(ICEBERG_REST_HAVE_SIGV4)
list(APPEND ICEBERG_REST_SOURCES auth/sigv4_signer.cc)
endif()

set(ICEBERG_REST_STATIC_BUILD_INTERFACE_LIBS)
set(ICEBERG_REST_SHARED_BUILD_INTERFACE_LIBS)
set(ICEBERG_REST_STATIC_INSTALL_INTERFACE_LIBS)
Expand All @@ -51,9 +55,23 @@ list(APPEND
"$<IF:$<TARGET_EXISTS:iceberg::iceberg_shared>,iceberg::iceberg_shared,iceberg::iceberg_static>"
"$<IF:$<BOOL:${CPR_VENDORED}>,iceberg::cpr,cpr::cpr>")

if(ICEBERG_REST_HAVE_SIGV4)
list(APPEND ICEBERG_REST_STATIC_BUILD_INTERFACE_LIBS AWS::aws-crt-cpp)
list(APPEND ICEBERG_REST_SHARED_BUILD_INTERFACE_LIBS AWS::aws-crt-cpp)
list(APPEND ICEBERG_REST_STATIC_INSTALL_INTERFACE_LIBS AWS::aws-crt-cpp)
list(APPEND ICEBERG_REST_SHARED_INSTALL_INTERFACE_LIBS AWS::aws-crt-cpp)
endif()

set(ICEBERG_REST_DEFINITIONS)
if(ICEBERG_REST_HAVE_SIGV4)
list(APPEND ICEBERG_REST_DEFINITIONS ICEBERG_REST_WITH_SIGV4)
endif()

add_iceberg_lib(iceberg_rest
SOURCES
${ICEBERG_REST_SOURCES}
DEFINITIONS
${ICEBERG_REST_DEFINITIONS}
SHARED_LINK_LIBS
${ICEBERG_REST_SHARED_BUILD_INTERFACE_LIBS}
STATIC_LINK_LIBS
Expand Down
6 changes: 6 additions & 0 deletions src/iceberg/catalog/rest/auth/auth_manager_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,10 @@ Result<std::unique_ptr<AuthManager>> MakeOAuth2Manager(
std::string_view name,
const std::unordered_map<std::string, std::string>& properties);

/// \brief Create a SigV4 authentication manager for AWS services such as
/// the AWS Glue Iceberg REST endpoint.
Result<std::unique_ptr<AuthManager>> MakeSigV4Manager(
std::string_view name,
const std::unordered_map<std::string, std::string>& properties);

} // namespace iceberg::rest::auth
6 changes: 5 additions & 1 deletion src/iceberg/catalog/rest/auth/auth_managers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,15 @@ std::string InferAuthType(
}

AuthManagerRegistry CreateDefaultRegistry() {
return {
AuthManagerRegistry registry = {
{AuthProperties::kAuthTypeNone, MakeNoopAuthManager},
{AuthProperties::kAuthTypeBasic, MakeBasicAuthManager},
{AuthProperties::kAuthTypeOAuth2, MakeOAuth2Manager},
};
#ifdef ICEBERG_REST_WITH_SIGV4
registry.emplace(AuthProperties::kAuthTypeSigV4, MakeSigV4Manager);
#endif
return registry;
}

// Get the global registry of auth manager factories.
Expand Down
19 changes: 19 additions & 0 deletions src/iceberg/catalog/rest/auth/auth_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,25 @@ class ICEBERG_REST_EXPORT AuthProperties : public ConfigBase<AuthProperties> {
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 kSigV4AccessKeyId = "rest.auth.sigv4.access-key-id";
inline static const std::string kSigV4SecretAccessKey =
"rest.auth.sigv4.secret-access-key";
inline static const std::string kSigV4SessionToken = "rest.auth.sigv4.session-token";
/// Selects which credential source drives SigV4 signing. Values:
/// "static" — use the access-key-id/secret-access-key/session-token
/// properties. Best for tests and short-lived scripts.
/// "default" — aws-crt-cpp's cached default chain:
/// Environment → Profile → STS Web Identity (IRSA) → IMDSv2/ECS.
/// Best for EC2/EKS/ECS deployments where creds rotate.
/// If unset, we infer: "static" when an access-key-id is configured,
/// "default" otherwise.
inline static const std::string kSigV4CredentialsProvider =
"rest.auth.sigv4.credentials-provider";
inline static constexpr std::string_view kSigV4ProviderStatic = "static";
inline static constexpr std::string_view kSigV4ProviderDefault = "default";
/// Default service name when rest.auth.sigv4.service is unset — matches the
/// Java Iceberg client, which defaults to AWS API Gateway's signing name.
inline static constexpr std::string_view kSigV4DefaultService = "execute-api";

// ---- OAuth2 entries ----

Expand Down
31 changes: 31 additions & 0 deletions src/iceberg/catalog/rest/auth/auth_session.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <cstdint>
#include <memory>
#include <string>
#include <string_view>
#include <unordered_map>

#include "iceberg/catalog/rest/iceberg_rest_export.h"
Expand All @@ -33,6 +34,24 @@

namespace iceberg::rest::auth {

/// \brief A description of an outgoing HTTP request that an AuthSession may
/// need to inspect in order to authenticate (e.g., SigV4 needs the
/// method, URL, query params and body hash to build its canonical
/// request). Header-only auth schemes ignore it.
struct SignableRequest {
/// HTTP method, uppercase (e.g., "GET", "POST").
std::string_view method;
/// Full request URL including scheme, host, path, but *without* query
/// string — query parameters are passed separately so callers don't need
/// to re-encode them.
std::string_view url;
/// Query parameters (pre-encoding). May be null for requests that have
/// none. Not owned.
const std::unordered_map<std::string, std::string>* query_params = nullptr;
/// Raw request body. Empty for GET/HEAD/DELETE without a body.
std::string_view body;
};

/// \brief An authentication session that can authenticate outgoing HTTP requests.
class ICEBERG_REST_EXPORT AuthSession {
public:
Expand All @@ -53,6 +72,18 @@ class ICEBERG_REST_EXPORT AuthSession {
/// - RestError: HTTP errors from authentication service
virtual Status Authenticate(std::unordered_map<std::string, std::string>& headers) = 0;

/// \brief Authenticate using full request context.
///
/// Overloaded form used by auth schemes that need to see the request method,
/// URL, query parameters and body in order to compute their header value
/// (e.g., SigV4). The default implementation ignores the request context and
/// forwards to the headers-only Authenticate(), which is what every non-
/// signing auth scheme wants.
virtual Status Authenticate(const SignableRequest& /*request*/,
std::unordered_map<std::string, std::string>& headers) {
return Authenticate(headers);
}

/// \brief Close the session and release any resources.
///
/// This method is called when the session is no longer needed. For stateful
Expand Down
Loading
Loading