use chrono::Duration;
use mockforge_platform_signing::{
PlatformSigner, RotationError, RotationEvent, RotationStateMachine,
};
use mockforge_registry_core::models::{audit_log::record_audit_event, AuditEventType};
use serde_json::json;
use sqlx::PgPool;
use uuid::Uuid;
#[derive(Debug, Clone)]
pub struct OperatorIdentity {
pub org_id: Uuid,
pub user_id: Uuid,
pub ip_address: Option<String>,
pub user_agent: Option<String>,
}
pub async fn audited_begin_handover<C, N>(
pool: &PgPool,
operator: &OperatorIdentity,
state_machine: &RotationStateMachine<C>,
next: &N,
transition_window: Duration,
) -> Result<RotationEvent, RotationError>
where
C: PlatformSigner,
N: PlatformSigner,
{
let event = state_machine.begin_handover(next, transition_window).await?;
let metadata = json!({
"from_key_id": event.payload.from_key_id,
"to_key_id": event.payload.to_key_id,
"from_algorithm": event.payload.from_algorithm,
"to_algorithm": event.payload.to_algorithm,
"issued_at": event.payload.issued_at,
"transition_until": event.payload.transition_until,
"from_public_key_b64": event.payload.from_public_key_b64,
"to_public_key_b64": event.payload.to_public_key_b64,
});
record_audit_event(
pool,
operator.org_id,
Some(operator.user_id),
AuditEventType::PlatformSigningRotationStarted,
format!(
"Platform signing-root rotation started: {} → {} (transition until {})",
event.payload.from_key_id, event.payload.to_key_id, event.payload.transition_until
),
Some(metadata),
operator.ip_address.as_deref(),
operator.user_agent.as_deref(),
)
.await;
Ok(event)
}
pub async fn audited_retire_old<C: PlatformSigner>(
pool: &PgPool,
operator: &OperatorIdentity,
state_machine: &RotationStateMachine<C>,
) -> Result<(), RotationError> {
let last_event = state_machine.last_event().await;
state_machine.retire_old().await?;
let metadata = last_event.map(|ev| {
json!({
"from_key_id": ev.payload.from_key_id,
"to_key_id": ev.payload.to_key_id,
"transition_closed_at": ev.payload.transition_until,
})
});
record_audit_event(
pool,
operator.org_id,
Some(operator.user_id),
AuditEventType::PlatformSigningKeyRetired,
"Platform signing-root: previous key retired after transition window".to_string(),
metadata,
operator.ip_address.as_deref(),
operator.user_agent.as_deref(),
)
.await;
Ok(())
}
pub async fn audited_emergency_revoke<C: PlatformSigner>(
pool: &PgPool,
operator: &OperatorIdentity,
state_machine: &RotationStateMachine<C>,
reason: &str,
) -> Result<(), RotationError> {
state_machine.emergency_revoke_current().await?;
let metadata = json!({
"reason": reason,
"key_id_revoked": state_machine_current_key_id(state_machine),
});
record_audit_event(
pool,
operator.org_id,
Some(operator.user_id),
AuditEventType::PlatformSigningKeyRevoked,
format!("Platform signing-root: emergency revocation — {reason}"),
Some(metadata),
operator.ip_address.as_deref(),
operator.user_agent.as_deref(),
)
.await;
Ok(())
}
fn state_machine_current_key_id<C: PlatformSigner>(
_sm: &RotationStateMachine<C>,
) -> Option<String> {
None
}