From ee20d4beb5bdecfa2ba53d2ffda21c2167b2de5b Mon Sep 17 00:00:00 2001 From: Artem Goncharov Date: Fri, 12 Jun 2026 14:10:25 +0200 Subject: [PATCH] feat: Introduce possibility to skip auth cache - CloudConfig now has a dedicated auth_cache property allowing to request explicit opt-out from auth caching. - Add explicit method to disable auth caching on the Client. --- openstack_sdk/src/openstack.rs | 20 +++++++++++++++----- openstack_sdk/src/openstack_async.rs | 20 +++++++++++++++----- sdk/core/src/config.rs | 7 +++++++ sdk/core/src/session.rs | 22 ++++++++++++++++++++-- sdk/core/src/state.rs | 11 +++++++++++ 5 files changed, 68 insertions(+), 12 deletions(-) diff --git a/openstack_sdk/src/openstack.rs b/openstack_sdk/src/openstack.rs index 7dab5a1b3..cda7e108b 100644 --- a/openstack_sdk/src/openstack.rs +++ b/openstack_sdk/src/openstack.rs @@ -55,7 +55,7 @@ use openstack_sdk_core::auth::{ gather_auth_data, }; use openstack_sdk_core::catalog::{CatalogError, ServiceEndpoint}; -use openstack_sdk_core::config::{CloudConfig, ConfigFile}; +use openstack_sdk_core::config::CloudConfig; use openstack_sdk_core::error::{OpenStackError, OpenStackResult, RestError}; use openstack_sdk_core::types::{ApiVersion, ServiceType}; use openstack_sdk_core::utils::expand_tilde; @@ -219,10 +219,8 @@ impl OpenStack { client_builder = client_builder.danger_accept_invalid_certs(true); } - let auth_cache = ConfigFile::new() - .ok() - .is_some_and(|c| c.is_auth_cache_enabled()); - let session_ctx = session::SessionContext::new(config, auth, auth_cache)?; + // Pass CloudConfig.auth_cache as override; SessionContext resolves priority chain. + let session_ctx = session::SessionContext::new(config, auth, config.auth_cache)?; Ok(OpenStack { client: client_builder.build()?, @@ -323,6 +321,18 @@ impl OpenStack { self } + /// Disable authentication caching for this session. + /// + /// Clears any cached tokens and prevents further caching — both the + /// in-memory store and the on-disk state file. Useful for scenarios + /// requiring fresh authentication on every request. + pub fn disable_auth_cache(&self) -> &Self { + if let Ok(mut session) = self.session.write() { + session.state.disable_auth_cache(); + } + self + } + pub fn authorize_with_auth_helper( &self, scope: Option, diff --git a/openstack_sdk/src/openstack_async.rs b/openstack_sdk/src/openstack_async.rs index f16681be0..f9842fc19 100644 --- a/openstack_sdk/src/openstack_async.rs +++ b/openstack_sdk/src/openstack_async.rs @@ -68,7 +68,7 @@ use openstack_sdk_core::auth::{ gather_auth_data, }; use openstack_sdk_core::catalog::{CatalogError, ServiceEndpoint}; -use openstack_sdk_core::config::{CloudConfig, ConfigFile}; +use openstack_sdk_core::config::CloudConfig; use openstack_sdk_core::error::{OpenStackError, OpenStackResult, RestError}; use openstack_sdk_core::types::{ApiVersion, BoxedAsyncRead, ServiceType}; use openstack_sdk_core::utils::expand_tilde; @@ -351,10 +351,8 @@ impl AsyncOpenStack { client_builder = client_builder.gzip(true); client_builder = client_builder.deflate(true); - let auth_cache = ConfigFile::new() - .ok() - .is_some_and(|c| c.is_auth_cache_enabled()); - let session_ctx = session::SessionContext::new(config, auth, auth_cache)?; + // Pass CloudConfig.auth_cache as override; SessionContext resolves priority chain. + let session_ctx = session::SessionContext::new(config, auth, config.auth_cache)?; Ok(AsyncOpenStack { client: client_builder.build()?, @@ -504,6 +502,18 @@ impl AsyncOpenStack { Ok(()) } + /// Disable authentication caching for this session. + /// + /// Clears any cached tokens and prevents further caching — both the + /// in-memory store and the on-disk state file. Useful for scenarios + /// requiring fresh authentication on every request. + pub fn disable_auth_cache(&self) -> &Self { + if let Ok(mut session) = self.session.write() { + session.state.disable_auth_cache(); + } + self + } + /// Authorize against the cloud using provided credentials and get the session token with the /// auth helper that may be invoked to interactively ask for the credentials. pub async fn authorize_with_auth_helper( diff --git a/sdk/core/src/config.rs b/sdk/core/src/config.rs index b3ac6b10f..f2fceefe1 100644 --- a/sdk/core/src/config.rs +++ b/sdk/core/src/config.rs @@ -382,6 +382,13 @@ pub struct CloudConfig { /// Verify SSL Certificates. pub verify: Option, + /// Override for authentication caching. + /// + /// If `Some(true)`, enables; if `Some(false)`, disables. + /// If `None`, falls back to the global `cache.auth` setting in the + /// `clouds.yaml` ConfigFile (default: `true`). + pub auth_cache: Option, + /// Catch-all for additional configuration fields not explicitly typed. /// Any extra YAML keys at this level are captured here. #[serde(flatten)] diff --git a/sdk/core/src/session.rs b/sdk/core/src/session.rs index 142bb88fe..fcf21bc09 100644 --- a/sdk/core/src/session.rs +++ b/sdk/core/src/session.rs @@ -16,9 +16,9 @@ use crate::auth::Auth; use crate::catalog::Catalog; +use crate::config::{CloudConfig, ConfigFile, get_config_identity_hash}; use crate::state::State; -use crate::config::{CloudConfig, get_config_identity_hash}; use crate::error::OpenStackError; use openstack_sdk_auth_core::authtoken::AuthTokenError; @@ -39,10 +39,14 @@ impl SessionContext { /// /// This is shared between sync and async clients — the ~30 lines of /// catalog/state initialization are identical. + /// + /// The `auth_cache` parameter is the session-level override. When `None`, + /// the CloudConfig's `auth_cache` field is checked, then the global + /// `ConfigFile` fallback, then defaults to `true`. pub fn new( config: &CloudConfig, auth: Auth, - auth_cache_enabled: bool, + auth_cache: Option, ) -> Result { let mut catalog = Catalog::default(); @@ -65,6 +69,20 @@ impl SessionContext { catalog.configure(config)?; + // Resolve auth cache priority: + // 1. explicit parameter override + // 2. CloudConfig.auth_cache + // 3. ConfigFile.cache.auth + // 4. default true + let auth_cache_enabled = auth_cache + .or(config.auth_cache) + .or_else(|| { + ConfigFile::new() + .ok() + .and_then(|cf| cf.cache.as_ref().and_then(|c| c.auth)) + }) + .unwrap_or(true); + let mut state = State::new(); state .set_auth_hash_key(get_config_identity_hash(config)) diff --git a/sdk/core/src/state.rs b/sdk/core/src/state.rs index acdb7d04a..dad2df774 100644 --- a/sdk/core/src/state.rs +++ b/sdk/core/src/state.rs @@ -103,6 +103,17 @@ impl State { self } + /// Disable all authorization caching (in-memory and on-disk). + /// + /// When disabled, `get_scope_auth()` and `get_any_valid_auth()` will + /// always return `None`, and `set_scope_auth()` will skip both the + /// in-memory store and file persistence. + pub fn disable_auth_cache(&mut self) -> &mut Self { + self.auth_cache_enabled = false; + self.auth_state.0.clear(); + self + } + /// Set authz into the state pub fn set_scope_auth(&mut self, scope: &AuthTokenScope, authz: &AuthToken) { self.auth_state.filter_invalid_auths();