Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
8 changes: 8 additions & 0 deletions src/iceberg/catalog/rest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
# specific language governing permissions and limitations
# under the License.

# Include auth subdirectory
add_subdirectory(auth)

set(ICEBERG_REST_SOURCES
catalog_properties.cc
endpoint.cc
Expand All @@ -26,6 +29,11 @@ set(ICEBERG_REST_SOURCES
rest_util.cc
types.cc)

# Add auth sources with proper path prefix
foreach(AUTH_SOURCE ${ICEBERG_REST_AUTH_SOURCES})
list(APPEND ICEBERG_REST_SOURCES "auth/${AUTH_SOURCE}")
endforeach()

set(ICEBERG_REST_STATIC_BUILD_INTERFACE_LIBS)
set(ICEBERG_REST_SHARED_BUILD_INTERFACE_LIBS)
set(ICEBERG_REST_STATIC_INSTALL_INTERFACE_LIBS)
Expand Down
23 changes: 23 additions & 0 deletions src/iceberg/catalog/rest/auth/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

set(ICEBERG_REST_AUTH_SOURCES auth_session.cc auth_manager.cc auth_managers.cc)

# Expose sources to parent scope for inclusion in iceberg_rest library
set(ICEBERG_REST_AUTH_SOURCES
${ICEBERG_REST_AUTH_SOURCES}
PARENT_SCOPE)
39 changes: 39 additions & 0 deletions src/iceberg/catalog/rest/auth/auth_manager.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

#include "iceberg/catalog/rest/auth/auth_manager.h"

namespace iceberg::rest::auth {

std::shared_ptr<AuthSession> AuthManager::InitSession(
HttpClient* init_client,
const std::unordered_map<std::string, std::string>& properties) {
// By default, use the catalog session for initialization
return CatalogSession(init_client, properties);
}

std::shared_ptr<AuthSession> AuthManager::TableSession(
[[maybe_unused]] const TableIdentifier& table,
[[maybe_unused]] const std::unordered_map<std::string, std::string>& properties,
std::shared_ptr<AuthSession> parent) {
// By default, return the parent session
return parent;
}

} // namespace iceberg::rest::auth
122 changes: 122 additions & 0 deletions src/iceberg/catalog/rest/auth/auth_manager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

#pragma once

#include <memory>
#include <string>
#include <unordered_map>

#include "iceberg/catalog/rest/auth/auth_session.h"
#include "iceberg/catalog/rest/iceberg_rest_export.h"
#include "iceberg/table_identifier.h"

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

namespace iceberg::rest {
class HttpClient;
} // namespace iceberg::rest

namespace iceberg::rest::auth {

/// \brief Manager for authentication sessions.
///
/// This interface is used to create sessions for the catalog, tables/views,
/// and any other context that requires authentication.
///
/// Managers are typically stateful and may require initialization and cleanup.
/// The manager is created by the catalog and is closed when the catalog is closed.
///
/// This interface is modeled after Java Iceberg's AuthManager interface.
class ICEBERG_REST_EXPORT AuthManager {
public:
virtual ~AuthManager() = default;

/// \brief Return a temporary session for contacting the configuration endpoint.
///
/// This session is used only during catalog initialization to fetch server
/// configuration. The returned session will be closed after the configuration
/// endpoint is contacted and should not be cached.
///
/// The provided HTTP client is a short-lived client; it should only be used
/// to fetch initial credentials if required, and must be discarded after that.
///
/// This method cannot return null. By default, it returns the catalog session.
///
/// \param init_client A short-lived HTTP client for initialization.
/// \param properties Configuration properties.
/// \return A session for initialization, or the catalog session by default.
virtual std::shared_ptr<AuthSession> InitSession(
HttpClient* init_client,
const std::unordered_map<std::string, std::string>& properties);

/// \brief Return a long-lived session for catalog operations.
///
/// This session's lifetime is tied to the owning catalog. It serves as the
/// parent session for all other sessions (contextual and table-specific).
/// It is closed when the owning catalog is closed.
///
/// The provided HTTP client is a long-lived, shared client. Implementors may
/// store it and reuse it for subsequent requests to the authorization server
/// (e.g., for renewing or refreshing credentials). It is not necessary to
/// close it when Close() is called.
///
/// This method cannot return null.
///
/// It is not required to cache the returned session internally, as the catalog
/// will keep it alive for the lifetime of the catalog.
///
/// \param shared_client A long-lived, shared HTTP client.
/// \param properties Configuration properties (merged with server config).
/// \return A session for catalog operations.
virtual std::shared_ptr<AuthSession> CatalogSession(
HttpClient* shared_client,
const std::unordered_map<std::string, std::string>& properties) = 0;

/// \brief Return a session for a specific table or view.
///
/// If the table or view requires a specific AuthSession (e.g., vended credentials),
/// this method should return a new AuthSession instance. Otherwise, it should
/// return the parent session.
///
/// This method cannot return null. By default, it returns the parent session.
///
/// Implementors should cache table sessions internally, as the catalog will not
/// cache them. Also, the owning catalog never closes table sessions; implementations
/// should manage their lifecycle and close them when they are no longer needed.
///
/// \param table The table identifier.
/// \param properties Properties returned by the table/view endpoint.
/// \param parent The parent session (typically the catalog session).
/// \return A session for the table, or the parent session by default.
virtual std::shared_ptr<AuthSession> TableSession(
const TableIdentifier& table,
const std::unordered_map<std::string, std::string>& properties,
std::shared_ptr<AuthSession> parent);

/// \brief Close the manager and release any resources.
///
/// This method is called when the owning catalog is closed. Implementations
/// should release any resources held by the manager, such as cached sessions
/// or background threads.
virtual void Close() {}
};

} // namespace iceberg::rest::auth
91 changes: 91 additions & 0 deletions src/iceberg/catalog/rest/auth/auth_managers.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

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

#include <algorithm>
#include <cctype>

#include "iceberg/catalog/rest/auth/auth_properties.h"

namespace iceberg::rest::auth {

namespace {

/// \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;
}

/// \brief Infer the authentication type from properties.
///
/// If no explicit auth type is set, this function tries to infer it from
/// other properties. If "credential" or "token" is present, it implies
/// OAuth2 authentication. Otherwise, defaults to no authentication.
///
/// This behavior is consistent with Java Iceberg's AuthManagers.
std::string InferAuthType(
const std::unordered_map<std::string, std::string>& properties) {
// Check for explicit auth type first
auto it = properties.find(std::string(AuthProperties::kAuthType));
if (it != properties.end() && !it->second.empty()) {
return ToLower(it->second);
}

// Infer from OAuth2 properties (credential or token)
bool has_credential =
properties.contains(std::string(AuthProperties::kOAuth2Credential));
bool has_token = properties.contains(std::string(AuthProperties::kOAuth2Token));
if (has_credential || has_token) {
return std::string(AuthProperties::kAuthTypeOAuth2);
}

// Default to no authentication
return std::string(AuthProperties::kAuthTypeNone);
}

} // namespace

std::unordered_map<std::string, AuthManagerFactory>& AuthManagers::GetRegistry() {
static std::unordered_map<std::string, AuthManagerFactory> registry;
return registry;
}

void AuthManagers::Register(const std::string& auth_type, AuthManagerFactory factory) {
GetRegistry()[ToLower(auth_type)] = std::move(factory);
}

Result<std::unique_ptr<AuthManager>> AuthManagers::Load(
const std::string& name,
const std::unordered_map<std::string, std::string>& properties) {
std::string auth_type = InferAuthType(properties);

auto& registry = GetRegistry();
auto it = registry.find(auth_type);
if (it == registry.end()) {
return NotImplemented("Authentication type '{}' is not supported", auth_type);
}

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

} // namespace iceberg::rest::auth
88 changes: 88 additions & 0 deletions src/iceberg/catalog/rest/auth/auth_managers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

#pragma once

#include <functional>
#include <memory>
#include <string>
#include <unordered_map>

#include "iceberg/catalog/rest/auth/auth_manager.h"
#include "iceberg/catalog/rest/iceberg_rest_export.h"
#include "iceberg/result.h"

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

namespace iceberg::rest::auth {

/// \brief Factory function type for creating AuthManager instances.
///
/// \param name The name of the manager (used for logging).
/// \param properties Configuration properties.
/// \return A unique pointer to the created AuthManager.
using AuthManagerFactory = std::function<std::unique_ptr<AuthManager>(
const std::string& name,
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const std::string& name,
std::string_view name,

const std::unordered_map<std::string, std::string>& properties)>;

/// \brief Factory class for loading authentication managers.
///
/// This class provides a registry-based approach to create AuthManager instances
/// based on the configured authentication type. It supports built-in types
/// (none, basic, oauth2) and allows registration of custom types.
///
/// This class is modeled after Java Iceberg's AuthManagers class.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
///
/// This class is modeled after Java Iceberg's AuthManagers class.

class ICEBERG_REST_EXPORT AuthManagers {
public:
/// \brief Load an authentication manager based on configuration.
///
/// This method reads the "rest.auth.type" property to determine which
/// AuthManager implementation to create. Supported types include:
/// - "none": NoopAuthManager (no authentication)
/// - "basic": BasicAuthManager (HTTP Basic authentication)
/// - "oauth2": OAuth2AuthManager (OAuth2 authentication)
/// - "sigv4": SigV4AuthManager (AWS Signature V4)
///
/// If no auth type is specified, the method will infer the type based on
/// other properties (e.g., presence of "credential" or "token" implies oauth2).
/// If no auth-related properties are found, it defaults to "none".
///
/// \param name A name for the manager (used for logging).
/// \param properties Configuration properties.
/// \return A unique pointer to the created AuthManager, or an error.
static Result<std::unique_ptr<AuthManager>> Load(
const std::string& name,
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const std::string& name,
std::string_view name,

const std::unordered_map<std::string, std::string>& properties);

/// \brief Register a custom authentication manager factory.
///
/// This allows users to extend the supported authentication types by
/// registering their own AuthManager implementations.
///
/// \param auth_type The authentication type name (e.g., "custom").
/// \param factory The factory function to create the AuthManager.
static void Register(const std::string& auth_type, AuthManagerFactory factory);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
static void Register(const std::string& auth_type, AuthManagerFactory factory);
static void Register(std::string_view auth_type, AuthManagerFactory factory);


private:
/// \brief Get the global registry of auth manager factories.
static std::unordered_map<std::string, AuthManagerFactory>& GetRegistry();
};

} // namespace iceberg::rest::auth
Loading
Loading