Skip to content

Commit 37787fb

Browse files
author
shuxu.li
committed
feat: add restcatalog authentication api
1 parent 28e195a commit 37787fb

File tree

9 files changed

+114
-108
lines changed

9 files changed

+114
-108
lines changed

src/iceberg/catalog/rest/CMakeLists.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
add_subdirectory(auth)
1919

2020
set(ICEBERG_REST_SOURCES
21+
auth/auth_manager.cc
22+
auth/auth_managers.cc
23+
auth/auth_session.cc
2124
catalog_properties.cc
2225
endpoint.cc
2326
error_handlers.cc
@@ -26,10 +29,7 @@ set(ICEBERG_REST_SOURCES
2629
resource_paths.cc
2730
rest_catalog.cc
2831
rest_util.cc
29-
types.cc
30-
auth/auth_manager.cc
31-
auth/auth_managers.cc
32-
auth/auth_session.cc)
32+
types.cc)
3333

3434
set(ICEBERG_REST_STATIC_BUILD_INTERFACE_LIBS)
3535
set(ICEBERG_REST_SHARED_BUILD_INTERFACE_LIBS)

src/iceberg/catalog/rest/auth/auth_manager.cc

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,26 @@
2323

2424
namespace iceberg::rest::auth {
2525

26-
Result<std::unique_ptr<AuthSession>> AuthManager::InitSession(
26+
Result<std::shared_ptr<AuthSession>> AuthManager::InitSession(
2727
HttpClient& init_client,
2828
const std::unordered_map<std::string, std::string>& properties) {
2929
// By default, use the catalog session for initialization
3030
return CatalogSession(init_client, properties);
3131
}
3232

33-
Result<std::unique_ptr<AuthSession>> AuthManager::TableSession(
33+
Result<std::shared_ptr<AuthSession>> AuthManager::ContextualSession(
34+
[[maybe_unused]] const std::unordered_map<std::string, std::string>& context,
35+
const std::shared_ptr<AuthSession>& parent) {
36+
// By default, return the parent session as-is
37+
return parent;
38+
}
39+
40+
Result<std::shared_ptr<AuthSession>> AuthManager::TableSession(
3441
[[maybe_unused]] const TableIdentifier& table,
3542
[[maybe_unused]] const std::unordered_map<std::string, std::string>& properties,
36-
[[maybe_unused]] const AuthSession& parent) {
37-
// By default, return nullptr to indicate the parent session should be reused.
38-
return nullptr;
43+
const std::shared_ptr<AuthSession>& parent) {
44+
// By default, return the parent session as-is
45+
return parent;
3946
}
4047

4148
} // namespace iceberg::rest::auth

src/iceberg/catalog/rest/auth/auth_manager.h

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,14 @@
2626
#include "iceberg/catalog/rest/iceberg_rest_export.h"
2727
#include "iceberg/catalog/rest/type_fwd.h"
2828
#include "iceberg/result.h"
29-
#include "iceberg/table_identifier.h"
29+
#include "iceberg/type_fwd.h"
3030

3131
/// \file iceberg/catalog/rest/auth/auth_manager.h
3232
/// \brief Authentication manager interface for REST catalog.
3333

3434
namespace iceberg::rest::auth {
3535

3636
/// \brief Produces authentication sessions for catalog and table requests.
37-
///
38-
/// AuthManager is responsible for creating authentication sessions at different scopes:
39-
/// - InitSession: Short-lived session for catalog initialization (optional)
40-
/// - CatalogSession: Long-lived session for catalog-level operations (required)
41-
/// - TableSession: Optional table-specific session or reuse of catalog session
42-
///
43-
/// Implementations are registered via AuthManagers::Register() and loaded by auth type.
4437
class ICEBERG_REST_EXPORT AuthManager {
4538
public:
4639
virtual ~AuthManager() = default;
@@ -54,37 +47,52 @@ class ICEBERG_REST_EXPORT AuthManager {
5447
/// \param init_client HTTP client used for initialization requests.
5548
/// \param properties Client configuration supplied by the catalog.
5649
/// \return Session for initialization or an error if credentials cannot be acquired.
57-
virtual Result<std::unique_ptr<AuthSession>> InitSession(
50+
virtual Result<std::shared_ptr<AuthSession>> InitSession(
5851
HttpClient& init_client,
5952
const std::unordered_map<std::string, std::string>& properties);
6053

6154
/// \brief Create the long-lived catalog session that acts as the parent session.
6255
///
6356
/// This session is used for all catalog-level operations (list namespaces, list tables,
64-
/// etc.) and serves as the parent session for table-specific operations. It is owned
65-
/// by the catalog and reused throughout the catalog's lifetime.
57+
/// etc.) and serves as the parent session for contextual and table-specific sessions.
58+
/// It is owned by the catalog and reused throughout the catalog's lifetime.
6659
///
6760
/// \param shared_client HTTP client owned by the catalog and reused for auth calls.
6861
/// \param properties Catalog properties (client config + server defaults).
6962
/// \return Session for catalog operations or an error if authentication cannot be set
7063
/// up.
71-
virtual Result<std::unique_ptr<AuthSession>> CatalogSession(
64+
virtual Result<std::shared_ptr<AuthSession>> CatalogSession(
7265
HttpClient& shared_client,
7366
const std::unordered_map<std::string, std::string>& properties) = 0;
7467

68+
/// \brief Create or reuse a session for a specific context.
69+
///
70+
/// This method is used by SessionCatalog to create sessions for different contexts
71+
/// (e.g., different users or tenants). Each REST endpoint call should use the
72+
/// appropriate contextual session before sending the HTTP request.
73+
///
74+
/// \param context Context properties (e.g., user credentials, tenant info).
75+
/// \param parent Catalog session to inherit from or return as-is.
76+
/// \return A context-specific session, or the parent session if no context-specific
77+
/// session is needed, or an error if session creation fails.
78+
virtual Result<std::shared_ptr<AuthSession>> ContextualSession(
79+
const std::unordered_map<std::string, std::string>& context,
80+
const std::shared_ptr<AuthSession>& parent);
81+
7582
/// \brief Create or reuse a session scoped to a single table/view.
7683
///
77-
/// This method can return a new table-specific session or indicate that the parent
78-
/// catalog session should be reused by returning nullptr.
84+
/// This method is called when loading a table that may have table-specific auth
85+
/// properties returned by the server.
7986
///
8087
/// \param table Target table identifier.
8188
/// \param properties Table-specific auth properties returned by the server.
82-
/// \param parent Catalog session to inherit from or extract information from.
83-
/// \return A new session for the table, nullptr to reuse parent session, or an error.
84-
virtual Result<std::unique_ptr<AuthSession>> TableSession(
89+
/// \param parent Catalog or contextual session to inherit from or return as-is.
90+
/// \return A table-specific session, or the parent session if no table-specific
91+
/// session is needed, or an error if session creation fails.
92+
virtual Result<std::shared_ptr<AuthSession>> TableSession(
8593
const TableIdentifier& table,
8694
const std::unordered_map<std::string, std::string>& properties,
87-
const AuthSession& parent);
95+
const std::shared_ptr<AuthSession>& parent);
8896

8997
/// \brief Release resources held by the manager.
9098
///

src/iceberg/catalog/rest/auth/auth_managers.cc

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ namespace iceberg::rest::auth {
2929

3030
namespace {
3131

32+
/// \brief Registry type for AuthManager factories with heterogeneous lookup support.
33+
using AuthManagerRegistry =
34+
std::unordered_map<std::string, AuthManagerFactory, StringHash, StringEqual>;
35+
3236
/// \brief Infer the authentication type from properties.
3337
///
3438
/// If no explicit auth type is set, this function tries to infer it from
@@ -38,22 +42,20 @@ namespace {
3842
/// This behavior is consistent with Java Iceberg's AuthManagers.
3943
std::string InferAuthType(
4044
const std::unordered_map<std::string, std::string>& properties) {
41-
// Check for explicit auth type first
42-
auto it = properties.find(std::string(AuthProperties::kAuthType));
45+
auto it = properties.find(AuthProperties::kAuthType);
4346
if (it != properties.end() && !it->second.empty()) {
4447
return StringUtils::ToLower(it->second);
4548
}
4649

4750
// Infer from OAuth2 properties (credential or token)
48-
bool has_credential =
49-
properties.contains(std::string(AuthProperties::kOAuth2Credential));
50-
bool has_token = properties.contains(std::string(AuthProperties::kOAuth2Token));
51+
bool has_credential = properties.contains(AuthProperties::kOAuth2Credential);
52+
bool has_token = properties.contains(AuthProperties::kOAuth2Token);
5153
if (has_credential || has_token) {
52-
return std::string(AuthProperties::kAuthTypeOAuth2);
54+
return AuthProperties::kAuthTypeOAuth2;
5355
}
5456

5557
// Default to no authentication
56-
return std::string(AuthProperties::kAuthTypeNone);
58+
return AuthProperties::kAuthTypeNone;
5759
}
5860

5961
/// \brief Get the global registry of auth manager factories.
@@ -65,7 +67,7 @@ AuthManagerRegistry& GetRegistry() {
6567
} // namespace
6668

6769
void AuthManagers::Register(std::string_view auth_type, AuthManagerFactory factory) {
68-
GetRegistry()[StringUtils::ToLower(std::string(auth_type))] = std::move(factory);
70+
GetRegistry()[StringUtils::ToLower(auth_type)] = std::move(factory);
6971
}
7072

7173
Result<std::unique_ptr<AuthManager>> AuthManagers::Load(
@@ -76,6 +78,7 @@ Result<std::unique_ptr<AuthManager>> AuthManagers::Load(
7678
auto& registry = GetRegistry();
7779
auto it = registry.find(auth_type);
7880
if (it == registry.end()) {
81+
// TODO: Fallback to default auth manager implementations
7982
return NotImplemented("Authentication type '{}' is not supported", auth_type);
8083
}
8184

src/iceberg/catalog/rest/auth/auth_managers.h

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,32 +28,27 @@
2828
#include "iceberg/catalog/rest/auth/auth_manager.h"
2929
#include "iceberg/catalog/rest/iceberg_rest_export.h"
3030
#include "iceberg/result.h"
31-
#include "iceberg/util/string_util.h"
3231

3332
/// \file iceberg/catalog/rest/auth/auth_managers.h
3433
/// \brief Factory for creating authentication managers.
3534

3635
namespace iceberg::rest::auth {
3736

38-
/// \brief Function that builds an AuthManager for a given catalog.
37+
/// \brief Function that creates an AuthManager from its name.
3938
///
40-
/// \param name Catalog name passed to the manager.
41-
/// \param properties Consolidated catalog configuration.
42-
/// \return Newly created manager instance.
43-
using AuthManagerFactory = std::function<std::unique_ptr<AuthManager>(
39+
/// \param name Name of the auth manager.
40+
/// \param properties Properties required by the auth manager.
41+
/// \return Newly created manager instance or an error if creation fails.
42+
using AuthManagerFactory = std::function<Result<std::unique_ptr<AuthManager>>(
4443
std::string_view name,
4544
const std::unordered_map<std::string, std::string>& properties)>;
4645

47-
/// \brief Registry type for AuthManager factories with heterogeneous lookup support.
48-
using AuthManagerRegistry =
49-
std::unordered_map<std::string, AuthManagerFactory, StringHash, StringEqual>;
50-
5146
/// \brief Registry-backed factory for AuthManager implementations.
5247
class ICEBERG_REST_EXPORT AuthManagers {
5348
public:
5449
/// \brief Load a manager by consulting the "rest.auth.type" configuration.
5550
///
56-
/// \param name Catalog name passed to the manager.
51+
/// \param name Name of the auth manager.
5752
/// \param properties Catalog properties used to determine auth type.
5853
/// \return Manager instance or an error if no factory matches.
5954
static Result<std::unique_ptr<AuthManager>> Load(

src/iceberg/catalog/rest/auth/auth_properties.h

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#pragma once
2121

22+
#include <string>
2223
#include <string_view>
2324

2425
/// \file iceberg/catalog/rest/auth/auth_properties.h
@@ -32,37 +33,40 @@ namespace iceberg::rest::auth {
3233
/// for the REST catalog. It follows the same naming conventions as Java Iceberg.
3334
struct AuthProperties {
3435
/// \brief Property key for specifying the authentication type.
35-
static constexpr std::string_view kAuthType = "rest.auth.type";
36+
inline static const std::string kAuthType = "rest.auth.type";
3637
/// \brief Authentication type: no authentication.
37-
static constexpr std::string_view kAuthTypeNone = "none";
38+
inline static const std::string kAuthTypeNone = "none";
3839
/// \brief Authentication type: HTTP Basic authentication.
39-
static constexpr std::string_view kAuthTypeBasic = "basic";
40+
inline static const std::string kAuthTypeBasic = "basic";
4041
/// \brief Authentication type: OAuth2 authentication.
41-
static constexpr std::string_view kAuthTypeOAuth2 = "oauth2";
42+
inline static const std::string kAuthTypeOAuth2 = "oauth2";
4243
/// \brief Authentication type: AWS SigV4 authentication.
43-
static constexpr std::string_view kAuthTypeSigV4 = "sigv4";
44+
inline static const std::string kAuthTypeSigV4 = "sigv4";
45+
4446
/// \brief Property key for Basic auth username.
45-
static constexpr std::string_view kBasicUsername = "rest.auth.basic.username";
47+
inline static const std::string kBasicUsername = "rest.auth.basic.username";
4648
/// \brief Property key for Basic auth password.
47-
static constexpr std::string_view kBasicPassword = "rest.auth.basic.password";
49+
inline static const std::string kBasicPassword = "rest.auth.basic.password";
50+
4851
/// \brief Property key for OAuth2 token (bearer token).
49-
static constexpr std::string_view kOAuth2Token = "token";
52+
inline static const std::string kOAuth2Token = "token";
5053
/// \brief Property key for OAuth2 credential (client_id:client_secret).
51-
static constexpr std::string_view kOAuth2Credential = "credential";
54+
inline static const std::string kOAuth2Credential = "credential";
5255
/// \brief Property key for OAuth2 scope.
53-
static constexpr std::string_view kOAuth2Scope = "scope";
56+
inline static const std::string kOAuth2Scope = "scope";
5457
/// \brief Property key for OAuth2 server URI.
55-
static constexpr std::string_view kOAuth2ServerUri = "oauth2-server-uri";
58+
inline static const std::string kOAuth2ServerUri = "oauth2-server-uri";
5659
/// \brief Property key for enabling token refresh.
57-
static constexpr std::string_view kOAuth2TokenRefreshEnabled = "token-refresh-enabled";
60+
inline static const std::string kOAuth2TokenRefreshEnabled = "token-refresh-enabled";
5861
/// \brief Default OAuth2 scope for catalog operations.
59-
static constexpr std::string_view kOAuth2DefaultScope = "catalog";
62+
inline static const std::string kOAuth2DefaultScope = "catalog";
63+
6064
/// \brief Property key for SigV4 region.
61-
static constexpr std::string_view kSigV4Region = "rest.auth.sigv4.region";
65+
inline static const std::string kSigV4Region = "rest.auth.sigv4.region";
6266
/// \brief Property key for SigV4 service name.
63-
static constexpr std::string_view kSigV4Service = "rest.auth.sigv4.service";
67+
inline static const std::string kSigV4Service = "rest.auth.sigv4.service";
6468
/// \brief Property key for SigV4 delegate auth type.
65-
static constexpr std::string_view kSigV4DelegateAuthType =
69+
inline static const std::string kSigV4DelegateAuthType =
6670
"rest.auth.sigv4.delegate-auth-type";
6771
};
6872

src/iceberg/catalog/rest/auth/auth_session.cc

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,30 @@
2323

2424
namespace iceberg::rest::auth {
2525

26-
DefaultAuthSession::DefaultAuthSession(
27-
std::unordered_map<std::string, std::string> headers)
28-
: headers_(std::move(headers)) {}
29-
30-
Status DefaultAuthSession::Authenticate(
31-
std::unordered_map<std::string, std::string>& headers) {
32-
for (const auto& [key, value] : headers_) {
33-
headers.try_emplace(key, value);
26+
namespace {
27+
28+
/// \brief Default implementation that adds static headers to requests.
29+
class DefaultAuthSession : public AuthSession {
30+
public:
31+
explicit DefaultAuthSession(std::unordered_map<std::string, std::string> headers)
32+
: headers_(std::move(headers)) {}
33+
34+
Status Authenticate(std::unordered_map<std::string, std::string>& headers) override {
35+
for (const auto& [key, value] : headers_) {
36+
headers.try_emplace(key, value);
37+
}
38+
return {};
3439
}
35-
return {};
36-
}
3740

38-
std::unique_ptr<DefaultAuthSession> DefaultAuthSession::Make(
41+
private:
42+
std::unordered_map<std::string, std::string> headers_;
43+
};
44+
45+
} // namespace
46+
47+
std::shared_ptr<AuthSession> AuthSession::Make(
3948
std::unordered_map<std::string, std::string> headers) {
40-
return std::make_unique<DefaultAuthSession>(std::move(headers));
49+
return std::make_shared<DefaultAuthSession>(std::move(headers));
4150
}
4251

4352
} // namespace iceberg::rest::auth

0 commit comments

Comments
 (0)