Skip to content

Commit 721e529

Browse files
authored
feat: Implement NoopAuthManager and integrate it with RestCatalog (#544)
- Add NoopAuthManager for "none" authentication type - Register "none" auth type in static registry with case-insensitive matching - Add KnownAuthTypes() to distinguish known-but-unimplemented types (NotImplemented) from unknown types (InvalidArgument) - Integrate AuthManager into RestCatalog
1 parent 2e00ce0 commit 721e529

File tree

9 files changed

+291
-77
lines changed

9 files changed

+291
-77
lines changed

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

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919

2020
#include "iceberg/catalog/rest/auth/auth_managers.h"
2121

22+
#include <unordered_set>
23+
2224
#include "iceberg/catalog/rest/auth/auth_properties.h"
25+
#include "iceberg/catalog/rest/auth/auth_session.h"
2326
#include "iceberg/util/string_util.h"
2427

2528
namespace iceberg::rest::auth {
@@ -30,6 +33,16 @@ namespace {
3033
using AuthManagerRegistry =
3134
std::unordered_map<std::string, AuthManagerFactory, StringHash, StringEqual>;
3235

36+
const std::unordered_set<std::string, StringHash, StringEqual>& KnownAuthTypes() {
37+
static const std::unordered_set<std::string, StringHash, StringEqual> kAuthTypes = {
38+
AuthProperties::kAuthTypeNone,
39+
AuthProperties::kAuthTypeBasic,
40+
AuthProperties::kAuthTypeOAuth2,
41+
AuthProperties::kAuthTypeSigV4,
42+
};
43+
return kAuthTypes;
44+
}
45+
3346
// Infer the authentication type from properties.
3447
std::string InferAuthType(
3548
const std::unordered_map<std::string, std::string>& properties) {
@@ -48,9 +61,39 @@ std::string InferAuthType(
4861
return AuthProperties::kAuthTypeNone;
4962
}
5063

64+
/// \brief Authentication manager that performs no authentication.
65+
class NoopAuthManager : public AuthManager {
66+
public:
67+
static Result<std::unique_ptr<AuthManager>> Make(
68+
[[maybe_unused]] std::string_view name,
69+
[[maybe_unused]] const std::unordered_map<std::string, std::string>& properties) {
70+
return std::make_unique<NoopAuthManager>();
71+
}
72+
73+
Result<std::shared_ptr<AuthSession>> CatalogSession(
74+
[[maybe_unused]] HttpClient& client,
75+
[[maybe_unused]] const std::unordered_map<std::string, std::string>& properties)
76+
override {
77+
return AuthSession::MakeDefault({});
78+
}
79+
};
80+
81+
template <typename T>
82+
AuthManagerFactory MakeAuthFactory() {
83+
return
84+
[](std::string_view name, const std::unordered_map<std::string, std::string>& props)
85+
-> Result<std::unique_ptr<AuthManager>> { return T::Make(name, props); };
86+
}
87+
88+
AuthManagerRegistry CreateDefaultRegistry() {
89+
return {
90+
{AuthProperties::kAuthTypeNone, MakeAuthFactory<NoopAuthManager>()},
91+
};
92+
}
93+
5194
// Get the global registry of auth manager factories.
5295
AuthManagerRegistry& GetRegistry() {
53-
static AuthManagerRegistry registry;
96+
static AuthManagerRegistry registry = CreateDefaultRegistry();
5497
return registry;
5598
}
5699

@@ -68,8 +111,10 @@ Result<std::unique_ptr<AuthManager>> AuthManagers::Load(
68111
auto& registry = GetRegistry();
69112
auto it = registry.find(auth_type);
70113
if (it == registry.end()) {
71-
// TODO(Li Shuxu): Fallback to default auth manager implementations
72-
return NotImplemented("Authentication type '{}' is not supported", auth_type);
114+
if (KnownAuthTypes().contains(auth_type)) {
115+
return NotImplemented("Authentication type '{}' is not yet supported", auth_type);
116+
}
117+
return InvalidArgument("Unknown authentication type: '{}'", auth_type);
73118
}
74119

75120
return it->second(name, properties);

src/iceberg/catalog/rest/http_client.cc

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <cpr/cpr.h>
2323
#include <nlohmann/json.hpp>
2424

25+
#include "iceberg/catalog/rest/auth/auth_session.h"
2526
#include "iceberg/catalog/rest/constant.h"
2627
#include "iceberg/catalog/rest/error_handlers.h"
2728
#include "iceberg/catalog/rest/json_serde_internal.h"
@@ -67,19 +68,17 @@ namespace {
6768
/// \brief Default error type for unparseable REST responses.
6869
constexpr std::string_view kRestExceptionType = "RESTException";
6970

70-
/// \brief Merges global default headers with request-specific headers.
71-
///
72-
/// Combines the global headers derived from RestCatalogProperties with the headers
73-
/// passed in the specific request. Request-specific headers have higher priority
74-
/// and will override global defaults if the keys conflict (e.g., overriding
75-
/// the default "Content-Type").
76-
cpr::Header MergeHeaders(const std::unordered_map<std::string, std::string>& defaults,
77-
const std::unordered_map<std::string, std::string>& overrides) {
78-
cpr::Header combined_headers = {defaults.begin(), defaults.end()};
79-
for (const auto& [key, val] : overrides) {
80-
combined_headers.insert_or_assign(key, val);
71+
/// \brief Prepare headers for an HTTP request.
72+
Result<cpr::Header> BuildHeaders(
73+
const std::unordered_map<std::string, std::string>& request_headers,
74+
const std::unordered_map<std::string, std::string>& default_headers,
75+
auth::AuthSession& session) {
76+
std::unordered_map<std::string, std::string> headers(default_headers);
77+
for (const auto& [key, val] : request_headers) {
78+
headers.emplace(key, val);
8179
}
82-
return combined_headers;
80+
ICEBERG_RETURN_UNEXPECTED(session.Authenticate(headers));
81+
return cpr::Header(headers.begin(), headers.end());
8382
}
8483

8584
/// \brief Converts a map of string key-value pairs to cpr::Parameters.
@@ -149,10 +148,11 @@ HttpClient::~HttpClient() = default;
149148
Result<HttpResponse> HttpClient::Get(
150149
const std::string& path, const std::unordered_map<std::string, std::string>& params,
151150
const std::unordered_map<std::string, std::string>& headers,
152-
const ErrorHandler& error_handler) {
153-
auto final_headers = MergeHeaders(default_headers_, headers);
151+
const ErrorHandler& error_handler, auth::AuthSession& session) {
152+
ICEBERG_ASSIGN_OR_RAISE(auto all_headers,
153+
BuildHeaders(headers, default_headers_, session));
154154
cpr::Response response =
155-
cpr::Get(cpr::Url{path}, GetParameters(params), final_headers, *connection_pool_);
155+
cpr::Get(cpr::Url{path}, GetParameters(params), all_headers, *connection_pool_);
156156

157157
ICEBERG_RETURN_UNEXPECTED(HandleFailureResponse(response, error_handler));
158158
HttpResponse http_response;
@@ -163,10 +163,11 @@ Result<HttpResponse> HttpClient::Get(
163163
Result<HttpResponse> HttpClient::Post(
164164
const std::string& path, const std::string& body,
165165
const std::unordered_map<std::string, std::string>& headers,
166-
const ErrorHandler& error_handler) {
167-
auto final_headers = MergeHeaders(default_headers_, headers);
166+
const ErrorHandler& error_handler, auth::AuthSession& session) {
167+
ICEBERG_ASSIGN_OR_RAISE(auto all_headers,
168+
BuildHeaders(headers, default_headers_, session));
168169
cpr::Response response =
169-
cpr::Post(cpr::Url{path}, cpr::Body{body}, final_headers, *connection_pool_);
170+
cpr::Post(cpr::Url{path}, cpr::Body{body}, all_headers, *connection_pool_);
170171

171172
ICEBERG_RETURN_UNEXPECTED(HandleFailureResponse(response, error_handler));
172173
HttpResponse http_response;
@@ -178,17 +179,19 @@ Result<HttpResponse> HttpClient::PostForm(
178179
const std::string& path,
179180
const std::unordered_map<std::string, std::string>& form_data,
180181
const std::unordered_map<std::string, std::string>& headers,
181-
const ErrorHandler& error_handler) {
182-
auto final_headers = MergeHeaders(default_headers_, headers);
183-
final_headers.insert_or_assign(kHeaderContentType, kMimeTypeFormUrlEncoded);
182+
const ErrorHandler& error_handler, auth::AuthSession& session) {
183+
std::unordered_map<std::string, std::string> form_headers(headers);
184+
form_headers.insert_or_assign(kHeaderContentType, kMimeTypeFormUrlEncoded);
185+
ICEBERG_ASSIGN_OR_RAISE(auto all_headers,
186+
BuildHeaders(form_headers, default_headers_, session));
184187
std::vector<cpr::Pair> pair_list;
185188
pair_list.reserve(form_data.size());
186189
for (const auto& [key, val] : form_data) {
187190
pair_list.emplace_back(key, val);
188191
}
189192
cpr::Response response =
190193
cpr::Post(cpr::Url{path}, cpr::Payload(pair_list.begin(), pair_list.end()),
191-
final_headers, *connection_pool_);
194+
all_headers, *connection_pool_);
192195

193196
ICEBERG_RETURN_UNEXPECTED(HandleFailureResponse(response, error_handler));
194197
HttpResponse http_response;
@@ -198,9 +201,10 @@ Result<HttpResponse> HttpClient::PostForm(
198201

199202
Result<HttpResponse> HttpClient::Head(
200203
const std::string& path, const std::unordered_map<std::string, std::string>& headers,
201-
const ErrorHandler& error_handler) {
202-
auto final_headers = MergeHeaders(default_headers_, headers);
203-
cpr::Response response = cpr::Head(cpr::Url{path}, final_headers, *connection_pool_);
204+
const ErrorHandler& error_handler, auth::AuthSession& session) {
205+
ICEBERG_ASSIGN_OR_RAISE(auto all_headers,
206+
BuildHeaders(headers, default_headers_, session));
207+
cpr::Response response = cpr::Head(cpr::Url{path}, all_headers, *connection_pool_);
204208

205209
ICEBERG_RETURN_UNEXPECTED(HandleFailureResponse(response, error_handler));
206210
HttpResponse http_response;
@@ -211,10 +215,11 @@ Result<HttpResponse> HttpClient::Head(
211215
Result<HttpResponse> HttpClient::Delete(
212216
const std::string& path, const std::unordered_map<std::string, std::string>& params,
213217
const std::unordered_map<std::string, std::string>& headers,
214-
const ErrorHandler& error_handler) {
215-
auto final_headers = MergeHeaders(default_headers_, headers);
216-
cpr::Response response = cpr::Delete(cpr::Url{path}, GetParameters(params),
217-
final_headers, *connection_pool_);
218+
const ErrorHandler& error_handler, auth::AuthSession& session) {
219+
ICEBERG_ASSIGN_OR_RAISE(auto all_headers,
220+
BuildHeaders(headers, default_headers_, session));
221+
cpr::Response response =
222+
cpr::Delete(cpr::Url{path}, GetParameters(params), all_headers, *connection_pool_);
218223

219224
ICEBERG_RETURN_UNEXPECTED(HandleFailureResponse(response, error_handler));
220225
HttpResponse http_response;

src/iceberg/catalog/rest/http_client.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,30 +82,33 @@ class ICEBERG_REST_EXPORT HttpClient {
8282
Result<HttpResponse> Get(const std::string& path,
8383
const std::unordered_map<std::string, std::string>& params,
8484
const std::unordered_map<std::string, std::string>& headers,
85-
const ErrorHandler& error_handler);
85+
const ErrorHandler& error_handler, auth::AuthSession& session);
8686

8787
/// \brief Sends a POST request.
8888
Result<HttpResponse> Post(const std::string& path, const std::string& body,
8989
const std::unordered_map<std::string, std::string>& headers,
90-
const ErrorHandler& error_handler);
90+
const ErrorHandler& error_handler,
91+
auth::AuthSession& session);
9192

9293
/// \brief Sends a POST request with form data.
9394
Result<HttpResponse> PostForm(
9495
const std::string& path,
9596
const std::unordered_map<std::string, std::string>& form_data,
9697
const std::unordered_map<std::string, std::string>& headers,
97-
const ErrorHandler& error_handler);
98+
const ErrorHandler& error_handler, auth::AuthSession& session);
9899

99100
/// \brief Sends a HEAD request.
100101
Result<HttpResponse> Head(const std::string& path,
101102
const std::unordered_map<std::string, std::string>& headers,
102-
const ErrorHandler& error_handler);
103+
const ErrorHandler& error_handler,
104+
auth::AuthSession& session);
103105

104106
/// \brief Sends a DELETE request.
105107
Result<HttpResponse> Delete(const std::string& path,
106108
const std::unordered_map<std::string, std::string>& params,
107109
const std::unordered_map<std::string, std::string>& headers,
108-
const ErrorHandler& error_handler);
110+
const ErrorHandler& error_handler,
111+
auth::AuthSession& session);
109112

110113
private:
111114
std::unordered_map<std::string, std::string> default_headers_;

0 commit comments

Comments
 (0)