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}