pub mod config;
pub mod jwt;
pub mod handler;
pub mod oauth;
pub mod api_keys;
pub mod role_mapper;
pub mod credentials;
pub mod session;
pub use config::{
AuthConfig, AuthMethod, Identity, AgentIdentity, AgentQuota,
JwtConfig, JwtClaims, OAuthConfig, LdapConfig, ApiKeyConfig,
RoleMappingRule, RoleMappingCondition, CredentialConfig, SessionConfig,
AuthRateLimitConfig,
};
pub use jwt::{JwtValidator, JwtError, JwtHeader, Jwks, Jwk, TokenCache};
pub use handler::{
AuthenticationHandler, AuthenticationHandlerBuilder,
AuthRequest, AuthResult, AuthError, CacheStats,
};
pub use oauth::{
OAuthClient, OAuthError, IntrospectionResponse,
TokenExchange, TokenResponse,
};
pub use api_keys::{
ApiKeyManager, ApiKey, ApiKeyError, ApiKeyStats, ApiKeyBuilder,
};
pub use role_mapper::{
RoleMapper, RoleMapperBuilder, PermissionSet, Operation,
AuthorizationContext,
};
pub use credentials::{
CredentialManager, CredentialManagerBuilder, CredentialProvider,
DatabaseCredential, CredentialSource, CredentialError,
StaticCredentialProvider, EnvironmentCredentialProvider,
VaultCredentialProvider, AwsSecretsManagerProvider,
};
pub use session::{
SessionManager, SessionManagerBuilder, Session, SessionError,
SessionStats, CookieOptions, SameSite,
};
pub struct AuthProxy {
handler: AuthenticationHandler,
role_mapper: RoleMapper,
session_manager: SessionManager,
credential_manager: Option<CredentialManager>,
}
impl AuthProxy {
pub fn new(
handler: AuthenticationHandler,
role_mapper: RoleMapper,
session_manager: SessionManager,
) -> Self {
Self {
handler,
role_mapper,
session_manager,
credential_manager: None,
}
}
pub fn builder() -> AuthProxyBuilder {
AuthProxyBuilder::new()
}
pub async fn authenticate(&self, request: &AuthRequest) -> Result<AuthResult, AuthError> {
self.handler.authenticate(request).await
}
pub async fn authenticate_and_create_session(
&self,
request: &AuthRequest,
) -> Result<(AuthResult, Session), AuthError> {
let result = self.handler.authenticate(request).await?;
let session = self.session_manager.create_session(
result.identity.clone(),
request.client_ip,
request.headers.get("user-agent").cloned(),
).map_err(|e| AuthError::Session(e.to_string()))?;
Ok((result, session))
}
pub fn validate_session(&self, token: &str) -> Result<Identity, AuthError> {
self.session_manager.validate_token(token)
.map_err(|e| AuthError::Session(e.to_string()))
}
pub fn map_roles(&self, identity: &Identity) -> Vec<String> {
self.role_mapper.map_roles(identity)
}
pub fn get_credentials(&self, key: &str) -> Result<DatabaseCredential, AuthError> {
self.credential_manager
.as_ref()
.ok_or_else(|| AuthError::Configuration("Credential manager not configured".to_string()))?
.get_credential(key)
.map_err(|e| AuthError::Configuration(e.to_string()))
}
pub fn invalidate_session(&self, token: &str) -> Result<(), AuthError> {
self.session_manager.invalidate_session(token)
.map_err(|e| AuthError::Session(e.to_string()))
}
pub fn handler(&self) -> &AuthenticationHandler {
&self.handler
}
pub fn role_mapper(&self) -> &RoleMapper {
&self.role_mapper
}
pub fn session_manager(&self) -> &SessionManager {
&self.session_manager
}
}
pub struct AuthProxyBuilder {
handler: Option<AuthenticationHandler>,
role_mapper: Option<RoleMapper>,
session_manager: Option<SessionManager>,
credential_manager: Option<CredentialManager>,
}
impl AuthProxyBuilder {
pub fn new() -> Self {
Self {
handler: None,
role_mapper: None,
session_manager: None,
credential_manager: None,
}
}
pub fn handler(mut self, handler: AuthenticationHandler) -> Self {
self.handler = Some(handler);
self
}
pub fn role_mapper(mut self, mapper: RoleMapper) -> Self {
self.role_mapper = Some(mapper);
self
}
pub fn session_manager(mut self, manager: SessionManager) -> Self {
self.session_manager = Some(manager);
self
}
pub fn credential_manager(mut self, manager: CredentialManager) -> Self {
self.credential_manager = Some(manager);
self
}
pub fn build(self) -> AuthProxy {
let handler = self.handler.unwrap_or_else(|| {
AuthenticationHandler::builder()
.enabled(false)
.build()
});
let role_mapper = self.role_mapper.unwrap_or_else(|| {
RoleMapper::new()
});
let session_manager = self.session_manager.unwrap_or_else(|| {
SessionManager::new(SessionConfig::default())
});
let mut proxy = AuthProxy::new(handler, role_mapper, session_manager);
proxy.credential_manager = self.credential_manager;
proxy
}
}
impl Default for AuthProxyBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
fn test_identity() -> Identity {
Identity {
user_id: "testuser".to_string(),
name: Some("Test User".to_string()),
email: Some("test@example.com".to_string()),
roles: vec!["user".to_string()],
groups: vec!["developers".to_string()],
tenant_id: None,
claims: HashMap::new(),
auth_method: "test".to_string(),
authenticated_at: chrono::Utc::now(),
}
}
#[test]
fn test_auth_proxy_builder() {
let proxy = AuthProxy::builder()
.handler(AuthenticationHandler::builder().enabled(false).build())
.role_mapper(RoleMapper::builder().default_role("user").build())
.session_manager(SessionManager::new(SessionConfig::default()))
.build();
assert!(!proxy.handler().is_enabled());
}
#[test]
fn test_auth_proxy_map_roles() {
let proxy = AuthProxy::builder()
.role_mapper(
RoleMapper::builder()
.group_role("developers", "db_developer")
.build()
)
.build();
let roles = proxy.map_roles(&test_identity());
assert!(roles.contains(&"db_developer".to_string()));
}
#[tokio::test]
async fn test_auth_proxy_disabled() {
let proxy = AuthProxy::builder().build();
let request = AuthRequest::new();
let result = proxy.authenticate(&request).await.unwrap();
assert_eq!(result.identity.auth_method, "anonymous");
}
#[test]
fn test_session_integration() {
let proxy = AuthProxy::builder()
.session_manager(
SessionManager::builder()
.max_sessions_per_user(5)
.build()
)
.build();
let session = proxy.session_manager()
.create_session(test_identity(), None, None)
.unwrap();
let identity = proxy.validate_session(&session.token).unwrap();
assert_eq!(identity.user_id, "testuser");
proxy.invalidate_session(&session.token).unwrap();
assert!(proxy.validate_session(&session.token).is_err());
}
}