use super::types::{AuthMethod, AuthResult, AuthzResult};
use crate::auth::jwt::types::TokenType;
use crate::config::models::auth::AuthConfig;
use crate::core::models::user::types::{User, UserRole};
use crate::core::types::context::RequestContext;
use crate::storage::StorageLayer;
use crate::utils::error::gateway_error::Result;
use std::sync::Arc;
use tracing::{debug, info};
#[derive(Clone)]
pub struct AuthSystem {
pub(super) config: Arc<AuthConfig>,
pub(super) storage: Arc<StorageLayer>,
pub(super) jwt: Arc<crate::auth::jwt::types::JwtHandler>,
pub(super) api_key: Arc<crate::auth::api_key::creation::ApiKeyHandler>,
pub(super) rbac: Arc<crate::auth::rbac::RbacSystem>,
}
impl AuthSystem {
pub async fn new(config: &AuthConfig, storage: Arc<StorageLayer>) -> Result<Self> {
info!("Initializing authentication system");
let config = Arc::new(config.clone());
let jwt = Arc::new(crate::auth::jwt::types::JwtHandler::new(&config).await?);
let hmac_secret = config.api_key_hmac_secret.clone();
let api_key = Arc::new(
crate::auth::api_key::creation::ApiKeyHandler::new(storage.clone(), hmac_secret)
.await?,
);
let rbac = Arc::new(crate::auth::rbac::RbacSystem::new(&config.rbac).await?);
info!("Authentication system initialized successfully");
Ok(Self {
config,
storage,
jwt,
api_key,
rbac,
})
}
pub async fn authenticate(
&self,
auth_method: AuthMethod,
context: RequestContext,
) -> Result<AuthResult> {
debug!("Authenticating request: {:?}", auth_method);
match auth_method {
AuthMethod::Jwt(token) => self.authenticate_jwt(&token, context).await,
AuthMethod::ApiKey(key) => self.authenticate_api_key(&key, context).await,
AuthMethod::Session(session_id) => {
self.authenticate_session(&session_id, context).await
}
AuthMethod::None => Ok(AuthResult {
success: false,
user: None,
api_key: None,
session: None,
error: Some("No authentication provided".to_string()),
context,
}),
}
}
async fn authenticate_jwt(
&self,
token: &str,
mut context: RequestContext,
) -> Result<AuthResult> {
match self.jwt.verify_access_token(token).await {
Ok(claims) => {
if !matches!(claims.token_type, TokenType::Access) {
return Ok(AuthResult {
success: false,
user: None,
api_key: None,
session: None,
error: Some("Invalid token type: expected access token".to_string()),
context,
});
}
if let Ok(Some(user)) = self.storage.db().find_user_by_id(claims.sub).await {
if user.is_active() {
context.user_id = Some(user.id().to_string());
if let Some(team_id) = user.team_ids.first().copied() {
context.set_team_id(team_id);
} else {
context.clear_team_id();
}
Ok(AuthResult {
success: true,
user: Some(user),
api_key: None,
session: None,
error: None,
context,
})
} else {
Ok(AuthResult {
success: false,
user: None,
api_key: None,
session: None,
error: Some("User account is not active".to_string()),
context,
})
}
} else {
Ok(AuthResult {
success: false,
user: None,
api_key: None,
session: None,
error: Some("User not found".to_string()),
context,
})
}
}
Err(e) => Ok(AuthResult {
success: false,
user: None,
api_key: None,
session: None,
error: Some(format!("Invalid JWT token: {}", e)),
context,
}),
}
}
async fn authenticate_api_key(
&self,
key: &str,
mut context: RequestContext,
) -> Result<AuthResult> {
match self.api_key.verify_key(key).await {
Ok(Some((api_key, user))) => {
context.set_api_key_id(api_key.metadata.id);
context.user_id = api_key.user_id.map(|id| id.to_string());
if let Some(team_id) = api_key.team_id {
context.set_team_id(team_id);
} else {
context.clear_team_id();
}
Ok(AuthResult {
success: true,
user,
api_key: Some(api_key),
session: None,
error: None,
context,
})
}
Ok(None) => Ok(AuthResult {
success: false,
user: None,
api_key: None,
session: None,
error: Some("Invalid API key".to_string()),
context,
}),
Err(e) => Ok(AuthResult {
success: false,
user: None,
api_key: None,
session: None,
error: Some(format!("API key verification failed: {}", e)),
context,
}),
}
}
async fn authenticate_session(
&self,
_session_id: &str,
context: RequestContext,
) -> Result<AuthResult> {
Ok(AuthResult {
success: false,
user: None,
api_key: None,
session: None,
error: Some("Session authentication is not yet implemented".to_string()),
context,
})
}
pub async fn authorize(&self, user: &User, permissions: &[String]) -> Result<AuthzResult> {
debug!(
"Authorizing user {} for permissions: {:?}",
user.username, permissions
);
let user_permissions = self.rbac.get_user_permissions(user).await?;
let allowed = self.rbac.check_permissions(&user_permissions, permissions);
Ok(AuthzResult {
allowed,
required_permissions: permissions.to_vec(),
user_permissions,
reason: if !allowed {
Some("Insufficient permissions".to_string())
} else {
None
},
})
}
pub(super) async fn get_user_permissions(&self, user: &User) -> Result<Vec<String>> {
let permissions = match user.role {
UserRole::Admin => vec![
"read:all".to_string(),
"write:all".to_string(),
"delete:all".to_string(),
"manage:users".to_string(),
"manage:api_keys".to_string(),
"manage:teams".to_string(),
],
UserRole::SuperAdmin => vec![
"read:all".to_string(),
"write:all".to_string(),
"delete:all".to_string(),
"manage:users".to_string(),
"manage:api_keys".to_string(),
"manage:teams".to_string(),
"manage:system".to_string(),
],
UserRole::Manager => vec![
"read:team".to_string(),
"write:team".to_string(),
"manage:team_users".to_string(),
"manage:team_api_keys".to_string(),
],
UserRole::User => vec![
"read:own".to_string(),
"write:own".to_string(),
"use:api".to_string(),
],
UserRole::ApiUser => vec!["use:api".to_string()],
UserRole::Viewer => vec!["read:own".to_string()],
};
Ok(permissions)
}
pub fn config(&self) -> &AuthConfig {
&self.config
}
pub fn jwt(&self) -> &crate::auth::jwt::types::JwtHandler {
&self.jwt
}
pub fn api_key(&self) -> &crate::auth::api_key::creation::ApiKeyHandler {
&self.api_key
}
pub fn rbac(&self) -> &crate::auth::rbac::RbacSystem {
&self.rbac
}
}