audit_log.rs 92 lignes · 2848 octets
use chrono::{DateTime, Utc};
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

/// AuditLog entity model
///
/// Represents audit logs for admin operations.
/// Optional table for critical operations (hybrid logging approach).
#[derive(Clone, Debug, Serialize, Deserialize, DeriveEntityModel)]
#[sea_orm(table_name = "audit_logs")]
pub struct Model {
    /// Unique identifier for the log entry
    #[sea_orm(primary_key)]
    pub id: Uuid,
    /// User ID who performed the action (nullable, SET NULL on delete)
    pub user_id: Option<Uuid>,
    /// Action performed (e.g., "create", "update", "delete", "reset_password")
    pub action: String,
    /// Target user ID if action targets a user (nullable)
    pub target_user_id: Option<Uuid>,
    /// Log level: "DEBUG", "INFO", "WARN", "ERROR"
    pub level: String,
    /// Log message
    pub message: String,
    /// Creation timestamp
    pub created_at: DateTime<Utc>,
    /// RFC 0008: actor `remote_identities.id` when the action was
    /// performed by a federated identity (mutually exclusive with
    /// `user_id` at the application level).
    pub actor_remote_identity_id: Option<Uuid>,
    /// RFC 0008: target `remote_identities.id` when the action targets
    /// a federated identity (mutually exclusive with `target_user_id`).
    pub target_remote_identity_id: Option<Uuid>,
}

/// SeaORM relations for the AuditLog entity.
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
    /// The user who performed the logged action.
    #[sea_orm(
        belongs_to = "super::user::Entity",
        from = "Column::UserId",
        to = "super::user::Column::Id"
    )]
    User,
    /// The user targeted by the logged action (if any).
    #[sea_orm(
        belongs_to = "super::user::Entity",
        from = "Column::TargetUserId",
        to = "super::user::Column::Id"
    )]
    TargetUser,
}

impl Related<super::user::Entity> for Entity {
    fn to() -> RelationDef {
        Relation::User.def()
    }
}

impl ActiveModelBehavior for ActiveModel {}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_audit_log_model() {
        let id = Uuid::new_v4();
        let user_id = Some(Uuid::new_v4());
        let target_user_id = Some(Uuid::new_v4());
        let now = Utc::now();

        let log = Model {
            id,
            user_id,
            action: "create".to_string(),
            target_user_id,
            level: "INFO".to_string(),
            message: "User created".to_string(),
            created_at: now,
            actor_remote_identity_id: None,
            target_remote_identity_id: None,
        };

        assert_eq!(log.id, id);
        assert_eq!(log.user_id, user_id);
        assert_eq!(log.action, "create");
        assert_eq!(log.level, "INFO");
    }
}