-
Notifications
You must be signed in to change notification settings - Fork 80
feat: add restcatalog authentication api #479
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?
Conversation
| /// \brief Convert a string to lowercase for case-insensitive comparison. | ||
| std::string ToLower(std::string_view str) { | ||
| std::string result(str); | ||
| std::ranges::transform(result, result.begin(), | ||
| [](unsigned char c) { return std::tolower(c); }); | ||
| return result; | ||
| } |
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.
There is a StringUtils::ToLower func in util/string_util.h
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.
Good catch, fixed.
| /// (i.e., request headers take precedence). | ||
| /// | ||
| /// \param headers The headers map to add authentication information to. | ||
| void Authenticate(std::unordered_map<std::string, std::string>& headers) override; |
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.
Do you think it is worth adding a class HttpRequest? We don't have it yet.
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.
I don't think we need an HttpRequest class at this stage. The current design is sufficient for most auth schemes (Basic, Bearer, OAuth2, API keys), but we can add can add it later if needed.
| /// OAuth2 authentication. Otherwise, defaults to no authentication. | ||
| /// | ||
| /// This behavior is consistent with Java Iceberg's AuthManagers. | ||
| std::string InferAuthType( |
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.
Should we add a enum class for the auth type?
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.
Auth types come from external config files/properties as strings, so the string-based approach allows users to register custom auth types without modifying core code. It also matches the Iceberg REST spec.
|
|
||
| private: | ||
| /// \brief Get the global registry of auth manager factories. | ||
| static std::unordered_map<std::string, AuthManagerFactory>& GetRegistry(); |
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.
We don't need this function in the header file, right?
| for (const auto& [key, value] : headers_) { | ||
| headers.try_emplace(key, value); | ||
| } |
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.
| for (const auto& [key, value] : headers_) { | |
| headers.try_emplace(key, value); | |
| } | |
| headers.merge(headers_); |
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.
merge() is destructive - it moves nodes from headers_ to headers, leaving headers_ empty or partially empty.
so, I think the current implementation can be retained.
| rest_util.cc | ||
| types.cc) | ||
| types.cc | ||
| auth/auth_manager.cc |
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.
Sort them alphabetically
| #include "iceberg/catalog/rest/iceberg_rest_export.h" | ||
| #include "iceberg/catalog/rest/type_fwd.h" | ||
| #include "iceberg/result.h" | ||
| #include "iceberg/table_identifier.h" |
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.
| #include "iceberg/table_identifier.h" |
We can remove this
| /// | ||
| /// AuthManager is responsible for creating authentication sessions at different scopes: | ||
| /// - InitSession: Short-lived session for catalog initialization (optional) | ||
| /// - CatalogSession: Long-lived session for catalog-level operations (required) | ||
| /// - TableSession: Optional table-specific session or reuse of catalog session | ||
| /// | ||
| /// Implementations are registered via AuthManagers::Register() and loaded by auth type. |
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.
| /// | |
| /// AuthManager is responsible for creating authentication sessions at different scopes: | |
| /// - InitSession: Short-lived session for catalog initialization (optional) | |
| /// - CatalogSession: Long-lived session for catalog-level operations (required) | |
| /// - TableSession: Optional table-specific session or reuse of catalog session | |
| /// | |
| /// Implementations are registered via AuthManagers::Register() and loaded by auth type. |
We don't need such verbose comments.
|
|
||
| class AuthManager; | ||
| class AuthSession; | ||
| class DefaultAuthSession; |
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 to forward declare DefaultAuthSession? Isn't it used only by the implementation?
| /// \return Status indicating success or failure of authentication. | ||
| /// - Success: Returns Status::OK | ||
| /// - Failure: Returns one of the following errors: |
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.
| /// \return Status indicating success or failure of authentication. | |
| /// - Success: Returns Status::OK | |
| /// - Failure: Returns one of the following errors: | |
| /// \return Status indicating success or one of the following errors: |
|
|
||
| // Infer from OAuth2 properties (credential or token) | ||
| bool has_credential = | ||
| properties.contains(std::string(AuthProperties::kOAuth2Credential)); |
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.
Do we need to define them as const std::string? It creates a string every time it is used.
| auto& registry = GetRegistry(); | ||
| auto it = registry.find(auth_type); | ||
| if (it == registry.end()) { | ||
| return NotImplemented("Authentication type '{}' is not supported", auth_type); |
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.
Please add a TODO to say we need to fallback to our default auth manager implementations.
| } // namespace | ||
|
|
||
| void AuthManagers::Register(std::string_view auth_type, AuthManagerFactory factory) { | ||
| GetRegistry()[StringUtils::ToLower(std::string(auth_type))] = std::move(factory); |
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.
It seems that we have unnecessary string allocations here.
| /// \param table Target table identifier. | ||
| /// \param properties Table-specific auth properties returned by the server. | ||
| /// \param parent Catalog session to inherit from or extract information from. | ||
| /// \return A new session for the table, nullptr to reuse parent session, or an error. |
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.
It looks weird to return nullptr to reuse parent session. I'm not sure if it is a good idea to use std::unique_ptr<AuthSession>&& parent to make the ownership explicit: either directly return parent or the created session owns its parent. I didn't any case to share the session, am I missing something?
| /// \param properties Catalog properties (client config + server defaults). | ||
| /// \return Session for catalog operations or an error if authentication cannot be set | ||
| /// up. | ||
| virtual Result<std::unique_ptr<AuthSession>> CatalogSession( |
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 we don't have contextual session as the Java impl? I think this is used by each rest endpoint before sending the http request.
feat: add restcatalog authentication api