use securitydept_oauth_provider::OidcSharedConfig;
use securitydept_oauth_resource_server::OAuthResourceServerConfig;
use serde::Deserialize;
use super::capabilities::TokenPropagation;
use crate::orchestration::BackendConfigError;
pub trait AccessTokenSubstrateConfigSource {
fn resource_server_config(&self) -> &OAuthResourceServerConfig;
fn token_propagation(&self) -> &TokenPropagation;
fn resolve_resource_server(
&self,
shared: &OidcSharedConfig,
) -> Result<OAuthResourceServerConfig, BackendConfigError> {
let mut rs = self.resource_server_config().clone();
rs.resolve_config(shared)?;
Ok(rs)
}
fn resolve_all(
&self,
shared: Option<&OidcSharedConfig>,
) -> Result<ResolvedAccessTokenSubstrateConfig, BackendConfigError> {
let resource_server = if let Some(shared) = shared {
self.resolve_resource_server(shared)?
} else {
self.resource_server_config().clone()
};
Ok(ResolvedAccessTokenSubstrateConfig {
resource_server,
token_propagation: self.token_propagation().clone(),
})
}
}
#[cfg_attr(feature = "config-schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Deserialize, Default)]
pub struct AccessTokenSubstrateConfig {
#[serde(default, flatten)]
pub resource_server: OAuthResourceServerConfig,
#[serde(default)]
pub token_propagation: TokenPropagation,
}
impl AccessTokenSubstrateConfigSource for AccessTokenSubstrateConfig {
fn resource_server_config(&self) -> &OAuthResourceServerConfig {
&self.resource_server
}
fn token_propagation(&self) -> &TokenPropagation {
&self.token_propagation
}
}
#[derive(Debug, Clone)]
pub struct ResolvedAccessTokenSubstrateConfig {
pub resource_server: OAuthResourceServerConfig,
pub token_propagation: TokenPropagation,
}
#[cfg(test)]
mod tests {
use securitydept_oauth_provider::{OAuthProviderRemoteConfig, OidcSharedConfig};
use securitydept_oauth_resource_server::OAuthResourceServerIntrospectionConfig;
use securitydept_utils::secret::SecretString;
use super::*;
#[test]
fn resolve_all_inherits_shared_defaults() {
let shared = OidcSharedConfig {
remote: OAuthProviderRemoteConfig {
well_known_url: Some(
"https://auth.example.com/.well-known/openid-configuration".to_string(),
),
..Default::default()
},
client_id: Some("shared-app".to_string()),
client_secret: Some(SecretString::from("shared-secret")),
..Default::default()
};
let raw = AccessTokenSubstrateConfig {
resource_server: OAuthResourceServerConfig {
introspection: Some(OAuthResourceServerIntrospectionConfig::default()),
..Default::default()
},
..Default::default()
};
let resolved = raw.resolve_all(Some(&shared)).expect("should resolve");
assert_eq!(
resolved
.resource_server
.introspection
.as_ref()
.unwrap()
.client_id
.as_deref(),
Some("shared-app"),
"introspection.client_id should inherit from [oidc]"
);
}
#[test]
fn resolve_all_without_shared_returns_raw() {
let raw = AccessTokenSubstrateConfig {
resource_server: OAuthResourceServerConfig::default(),
token_propagation: TokenPropagation::Disabled,
};
let resolved = raw
.resolve_all(None)
.expect("should resolve without shared");
assert!(
resolved.resource_server.remote.well_known_url.is_none(),
"no shared defaults should be applied"
);
assert!(matches!(
resolved.token_propagation,
TokenPropagation::Disabled
));
}
#[test]
fn resolve_all_propagation_axis_passes_through() {
use crate::access_token_substrate::propagation::{
PropagationDestinationPolicy, TokenPropagatorConfig,
};
let raw = AccessTokenSubstrateConfig {
resource_server: OAuthResourceServerConfig::default(),
token_propagation: TokenPropagation::Enabled {
config: TokenPropagatorConfig {
destination_policy: PropagationDestinationPolicy {
allowed_targets: vec![],
..Default::default()
},
..Default::default()
},
},
};
let resolved = raw.resolve_all(None).expect("should resolve");
assert!(matches!(
resolved.token_propagation,
TokenPropagation::Enabled { .. }
));
}
}