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 PromoteTemplate,
13 Migrate,
14 Install,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
18#[serde(deny_unknown_fields)]
19pub struct AuthenticatedOperator {
20 pub uid: u32,
21 pub gid: u32,
22 pub action: OperatorAction,
23 pub resource_kind: String,
24 pub resource_id: String,
25 pub expected_state_digest: String,
26 pub nonce: String,
27 pub issued_at: DateTime<Utc>,
28 pub expires_at: DateTime<Utc>,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
32#[serde(deny_unknown_fields)]
33pub struct AuthorizationReceipt {
34 pub receipt_id: String,
35 pub request_digest: String,
36 pub action: OperatorAction,
37 pub resource: String,
38 pub state_digest: String,
39 pub operator_uid: u32,
40 pub daemon_instance_id: String,
41 pub nonce: String,
42 pub issued_at: DateTime<Utc>,
43 pub expires_at: DateTime<Utc>,
44 pub consumed_at: Option<DateTime<Utc>>,
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct PeerCredentials {
49 pub uid: u32,
50 pub gid: u32,
51}
52
53pub async fn peer_credentials(stream: &UnixStream) -> std::io::Result<PeerCredentials> {
55 let c = stream.peer_cred()?;
56 Ok(PeerCredentials {
57 uid: c.uid(),
58 gid: c.gid(),
59 })
60}
61
62pub fn validate_window(op: &AuthenticatedOperator, now: DateTime<Utc>) -> Result<(), &'static str> {
63 if op.nonce.is_empty() {
64 return Err("AUTHORIZATION_NONCE_REQUIRED");
65 }
66 if op.expires_at <= op.issued_at || op.expires_at <= now {
67 return Err("AUTHORIZATION_EXPIRED");
68 }
69 if op.issued_at > now {
70 return Err("AUTHORIZATION_NOT_YET_VALID");
71 }
72 Ok(())
73}