Skip to main content

iceberg_catalog_rest/auth/
mod.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Pluggable authentication for the REST catalog, mirroring Iceberg Java's
19//! `AuthManager`/`AuthSession` API.
20
21mod oauth2;
22#[cfg(feature = "sigv4")]
23mod sigv4;
24
25use std::collections::HashMap;
26use std::fmt::Debug;
27use std::sync::Arc;
28
29use async_trait::async_trait;
30#[cfg(feature = "sigv4")]
31pub use aws_credential_types::Credentials;
32use iceberg::{Result, SessionContext};
33pub use oauth2::OAuth2Manager;
34#[cfg(feature = "sigv4")]
35pub use sigv4::{PayloadHashMode, SigV4Signer};
36
37use crate::client::HttpClient;
38use crate::request::HttpRequest;
39
40/// `rest.auth.type` value disabling authentication.
41pub const AUTH_TYPE_NONE: &str = "none";
42/// `rest.auth.type` value selecting OAuth2 token authentication.
43pub const AUTH_TYPE_OAUTH2: &str = "oauth2";
44
45/// Creates the [`AuthSession`]s used to authenticate REST catalog requests.
46///
47/// A manager is exclusively scoped to one catalog and must not be reused by
48/// other catalogs. It is either created from the `rest.auth.type` property or
49/// injected through
50/// [`RestCatalogBuilder::with_auth_manager`](crate::RestCatalogBuilder::with_auth_manager) or
51/// [`RestSessionCatalogBuilder::with_auth_manager`](crate::RestSessionCatalogBuilder::with_auth_manager).
52/// Catalog initialization calls [`AuthManager::catalog_session`] exactly once;
53/// the context-specific sessions that [`AuthManager::contextual_session`]
54/// derives from it may rely on the state established by that call.
55///
56/// [`Self::init_session`] and [`Self::catalog_session`] are handed the
57/// catalog's [`HttpClient`], which an implementation may clone and store for
58/// its own requests (e.g. a token exchange) so that they share the catalog's
59/// connection pool and configuration.
60#[async_trait]
61pub trait AuthManager: Debug + Send + Sync {
62    /// Session used for the initial `/v1/config` handshake, given the
63    /// user-supplied properties.
64    ///
65    /// Returns a [`Box`]: an init session is used once and released, unlike
66    /// the shared [`AuthManager::catalog_session`].
67    async fn init_session(
68        &self,
69        client: &HttpClient,
70        props: &HashMap<String, String>,
71    ) -> Result<Box<dyn AuthSession>>;
72
73    /// Session used for all subsequent catalog requests, given the properties
74    /// merged from the user configuration and the server's config response.
75    ///
76    /// Returns an [`Arc`]: this session is shared by concurrent requests for
77    /// the rest of the catalog's lifetime. Implementations may carry state
78    /// (e.g. a cached token) over from the init session.
79    async fn catalog_session(
80        &self,
81        client: &HttpClient,
82        props: &HashMap<String, String>,
83    ) -> Result<Arc<dyn AuthSession>>;
84
85    /// Returns the authentication session for a specific context.
86    ///
87    /// The catalog calls this method only after [`Self::catalog_session`] has
88    /// succeeded. `catalog_session` is the catalog session returned by this
89    /// manager. If the context does not require different authentication,
90    /// implementations should return `catalog_session` unchanged.
91    ///
92    /// The catalog does not cache the returned session. Implementations should
93    /// cache context-specific sessions internally using [`SessionContext::session_id`]
94    /// and are responsible for eviction. Reusing a session ID with different
95    /// context may therefore return the previously cached session.
96    ///
97    /// The catalog calls this method for every request without serializing
98    /// calls that share a session ID. Caching implementations must guard
99    /// against concurrently creating multiple sessions for the same context.
100    ///
101    /// No [`HttpClient`] is passed. Implementations that need one, e.g. for a
102    /// token exchange, should store the client handed to [`Self::catalog_session`].
103    async fn contextual_session(
104        &self,
105        context: &SessionContext,
106        catalog_session: Arc<dyn AuthSession>,
107    ) -> Result<Arc<dyn AuthSession>> {
108        let _ = context;
109        Ok(catalog_session)
110    }
111}
112
113/// Authenticates outgoing REST catalog requests.
114#[async_trait]
115pub trait AuthSession: Debug + Send + Sync {
116    /// Applies authentication to the request (adds headers, signs, ...).
117    async fn authenticate(&self, request: &mut HttpRequest) -> Result<()>;
118}
119
120/// [`AuthManager`] that performs no authentication.
121#[derive(Debug)]
122pub struct NoopAuthManager;
123
124/// [`AuthSession`] that performs no authentication.
125#[derive(Debug)]
126pub(crate) struct NoopSession;
127
128#[async_trait]
129impl AuthManager for NoopAuthManager {
130    async fn init_session(
131        &self,
132        _client: &HttpClient,
133        _props: &HashMap<String, String>,
134    ) -> Result<Box<dyn AuthSession>> {
135        Ok(Box::new(NoopSession))
136    }
137
138    async fn catalog_session(
139        &self,
140        _client: &HttpClient,
141        _props: &HashMap<String, String>,
142    ) -> Result<Arc<dyn AuthSession>> {
143        Ok(Arc::new(NoopSession))
144    }
145}
146
147#[async_trait]
148impl AuthSession for NoopSession {
149    async fn authenticate(&self, _request: &mut HttpRequest) -> Result<()> {
150        Ok(())
151    }
152}