agent_graph_mcp/
operator_auth.rs1use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4use tokio::net::UnixStream;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(rename_all = "snake_case")]
8pub enum OperatorAction {
9 Approve,
10 Reject,
11 DeleteGraph,
12 SetGraphRetention,
13 ApproveGraphDeletion,
14 ClearExecutionLineage,
15 PurgeGraph,
16 PromoteTemplate,
17 Migrate,
18 Install,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
22#[serde(deny_unknown_fields)]
23pub struct AuthenticatedOperator {
24 pub uid: u32,
25 pub gid: u32,
26 pub action: OperatorAction,
27 pub resource_kind: String,
28 pub resource_id: String,
29 pub expected_state_digest: String,
30 pub nonce: String,
31 pub issued_at: DateTime<Utc>,
32 pub expires_at: DateTime<Utc>,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36#[serde(deny_unknown_fields)]
37pub struct AuthorizationReceipt {
38 pub receipt_id: String,
39 pub request_digest: String,
40 pub action: OperatorAction,
41 pub resource: String,
42 pub state_digest: String,
43 pub operator_uid: u32,
44 pub daemon_instance_id: String,
45 pub nonce: String,
46 pub issued_at: DateTime<Utc>,
47 pub expires_at: DateTime<Utc>,
48 pub consumed_at: Option<DateTime<Utc>>,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct PeerCredentials {
53 pub uid: u32,
54 pub gid: u32,
55}
56
57pub async fn peer_credentials(stream: &UnixStream) -> std::io::Result<PeerCredentials> {
59 let c = stream.peer_cred()?;
60 Ok(PeerCredentials {
61 uid: c.uid(),
62 gid: c.gid(),
63 })
64}
65
66pub fn validate_window(op: &AuthenticatedOperator, now: DateTime<Utc>) -> Result<(), &'static str> {
67 if op.nonce.is_empty() {
68 return Err("AUTHORIZATION_NONCE_REQUIRED");
69 }
70 if op.expires_at <= op.issued_at || op.expires_at <= now {
71 return Err("AUTHORIZATION_EXPIRED");
72 }
73 if op.issued_at > now {
74 return Err("AUTHORIZATION_NOT_YET_VALID");
75 }
76 Ok(())
77}