core: add community audit sink seam
This commit is contained in:
@@ -145,5 +145,6 @@ Progress:
|
|||||||
- Phase 0 / task `0.1`: создан `crank-core::ext` module skeleton
|
- Phase 0 / task `0.1`: создан `crank-core::ext` module skeleton
|
||||||
- Phase 0 / task `0.2`: `MachineCredentialVerifier` и связанные типы вынесены из `apps/mcp-server` в public seam `crank_core::ext::auth`
|
- Phase 0 / task `0.2`: `MachineCredentialVerifier` и связанные типы вынесены из `apps/mcp-server` в public seam `crank_core::ext::auth`
|
||||||
- Phase 0 / task `0.5`: введены `PolicyEngine`, `SessionActor`, `PolicyAction`, `PolicyScope`, `PolicyDecision` и default `OwnerOnlyPolicyEngine` в public seam `crank_core::ext::access`
|
- Phase 0 / task `0.5`: введены `PolicyEngine`, `SessionActor`, `PolicyAction`, `PolicyScope`, `PolicyDecision` и default `OwnerOnlyPolicyEngine` в public seam `crank_core::ext::access`
|
||||||
|
- Phase 0 / task `0.6`: введены `AuditSink`, `NoopAuditSink`, `AuditEventId` и базовые audit-типы в public seam `crank_core::ext::audit`
|
||||||
- Phase 0 / task `0.7`: введены `CapabilityProfile` и `CommunityCapabilityProfile`, а `admin-api` capability payload теперь собирается через public seam вместо локального literal
|
- Phase 0 / task `0.7`: введены `CapabilityProfile` и `CommunityCapabilityProfile`, а `admin-api` capability payload теперь собирается через public seam вместо локального literal
|
||||||
- backward-compatible alias `CommunityMachineCredentialVerifier` сохранен, поведение Community не изменено
|
- backward-compatible alias `CommunityMachineCredentialVerifier` сохранен, поведение Community не изменено
|
||||||
|
|||||||
@@ -1 +1,94 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::Value;
|
||||||
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
|
use crate::{AuditEventId, UserId, UserSessionId, WorkspaceId};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct AuditActor {
|
||||||
|
pub user_id: UserId,
|
||||||
|
pub email: String,
|
||||||
|
pub session_id: Option<UserSessionId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum AuditTargetKind {
|
||||||
|
Workspace,
|
||||||
|
Membership,
|
||||||
|
Invitation,
|
||||||
|
Operation,
|
||||||
|
Agent,
|
||||||
|
PlatformApiKey,
|
||||||
|
Secret,
|
||||||
|
AuthProfile,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct AuditTarget {
|
||||||
|
pub workspace_id: WorkspaceId,
|
||||||
|
pub kind: AuditTargetKind,
|
||||||
|
pub id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct AuditEvent {
|
||||||
|
pub id: AuditEventId,
|
||||||
|
#[serde(with = "time::serde::rfc3339")]
|
||||||
|
pub occurred_at: OffsetDateTime,
|
||||||
|
pub actor: AuditActor,
|
||||||
|
pub action: String,
|
||||||
|
pub target: AuditTarget,
|
||||||
|
pub payload: Value,
|
||||||
|
pub source_ip: Option<String>,
|
||||||
|
pub user_agent: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct AuditQuery {
|
||||||
|
pub workspace_id: Option<WorkspaceId>,
|
||||||
|
pub action: Option<String>,
|
||||||
|
pub cursor: Option<String>,
|
||||||
|
pub limit: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct AuditEventPage {
|
||||||
|
pub items: Vec<AuditEvent>,
|
||||||
|
pub next_cursor: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum AuditError {
|
||||||
|
#[error("audit sink failed: {0}")]
|
||||||
|
Backend(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait AuditSink: Send + Sync {
|
||||||
|
async fn record(&self, event: AuditEvent) -> Result<(), AuditError>;
|
||||||
|
|
||||||
|
async fn list(&self, _query: AuditQuery) -> Result<AuditEventPage, AuditError> {
|
||||||
|
Ok(AuditEventPage::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get(&self, _id: &AuditEventId) -> Result<Option<AuditEvent>, AuditError> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type SharedAuditSink = Arc<dyn AuditSink>;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct NoopAuditSink;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AuditSink for NoopAuditSink {
|
||||||
|
async fn record(&self, _event: AuditEvent) -> Result<(), AuditError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ define_id!(AgentId);
|
|||||||
define_id!(InvitationId);
|
define_id!(InvitationId);
|
||||||
define_id!(PlatformApiKeyId);
|
define_id!(PlatformApiKeyId);
|
||||||
define_id!(InvocationLogId);
|
define_id!(InvocationLogId);
|
||||||
|
define_id!(AuditEventId);
|
||||||
define_id!(SecretId);
|
define_id!(SecretId);
|
||||||
define_id!(StreamSessionId);
|
define_id!(StreamSessionId);
|
||||||
define_id!(AsyncJobId);
|
define_id!(AsyncJobId);
|
||||||
|
|||||||
@@ -35,15 +35,19 @@ pub use edition::{
|
|||||||
pub use ext::access::{
|
pub use ext::access::{
|
||||||
OwnerOnlyPolicyEngine, PolicyAction, PolicyDecision, PolicyEngine, PolicyScope, SessionActor,
|
OwnerOnlyPolicyEngine, PolicyAction, PolicyDecision, PolicyEngine, PolicyScope, SessionActor,
|
||||||
};
|
};
|
||||||
|
pub use ext::audit::{
|
||||||
|
AuditActor, AuditError, AuditEvent, AuditEventPage, AuditQuery, AuditSink, AuditTarget,
|
||||||
|
AuditTargetKind, NoopAuditSink, SharedAuditSink,
|
||||||
|
};
|
||||||
pub use ext::auth::{
|
pub use ext::auth::{
|
||||||
MachineCredentialVerifier, MachineCredentialVerifierError, SharedMachineCredentialVerifier,
|
MachineCredentialVerifier, MachineCredentialVerifierError, SharedMachineCredentialVerifier,
|
||||||
VerifiedMachineCredential,
|
VerifiedMachineCredential,
|
||||||
};
|
};
|
||||||
pub use ext::capability::{CapabilityProfile, CommunityCapabilityProfile};
|
pub use ext::capability::{CapabilityProfile, CommunityCapabilityProfile};
|
||||||
pub use ids::{
|
pub use ids::{
|
||||||
AgentId, AsyncJobId, AuthProfileId, DescriptorId, InvitationId, InvocationLogId, OperationId,
|
AgentId, AsyncJobId, AuditEventId, AuthProfileId, DescriptorId, InvitationId, InvocationLogId,
|
||||||
PlatformApiKeyId, SampleId, SecretId, StreamSessionId, ToolId, UserId, UserSessionId,
|
OperationId, PlatformApiKeyId, SampleId, SecretId, StreamSessionId, ToolId, UserId,
|
||||||
WorkspaceId,
|
UserSessionId, WorkspaceId,
|
||||||
};
|
};
|
||||||
pub use observability::{
|
pub use observability::{
|
||||||
InvocationLevel, InvocationLog, InvocationSource, InvocationStatus, UsagePeriod, UsageRollup,
|
InvocationLevel, InvocationLog, InvocationSource, InvocationStatus, UsagePeriod, UsageRollup,
|
||||||
|
|||||||
Reference in New Issue
Block a user