use geth_types::{AgentId, DeviceId, KeyId, NodeId, UnixMillis, UserId}; use serde::{Deserialize, Serialize}; pub const KEYCHAIN_SIGNATURE_NAMESPACE: &str = "geth.keychain.v1@geth.local"; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct SignedKeychainOp { pub op: KeychainOp, pub signer: KeyId, pub signature_namespace: String, pub signature: Vec, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct KeychainOp { pub id: geth_types::AuthOpId, pub created_at: UnixMillis, pub kind: KeychainOpKind, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(tag = "kind", rename_all = "kebab-case")] pub enum KeychainOpKind { KeychainInit, AdminKeyAdd { key: KeyId, }, AdminKeyRevoke { key: KeyId, }, UserAdd { user: UserId, name: String, }, UserRename { user: UserId, name: String, }, UserRevoke { user: UserId, }, DeviceAdd { device: DeviceId, user: UserId, }, DeviceRevoke { device: DeviceId, }, DeviceKeyAdd { device: DeviceId, key: KeyId, }, DeviceKeyRevoke { device: DeviceId, key: KeyId, }, NodeAdd { node: NodeId, device: DeviceId, name: String, }, NodeRename { node: NodeId, name: String, }, NodeRevoke { node: NodeId, }, NodeEndpointAdd { node: NodeId, endpoint: String, }, NodeEndpointRevoke { node: NodeId, endpoint: String, }, AgentBind { agent: AgentId, node: NodeId, }, } #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct KeychainView { pub initialized: bool, pub admin_keys: Vec, pub users: Vec, pub devices: Vec, pub nodes: Vec, } pub fn reduce_keychain_ops(ops: &[KeychainOp]) -> KeychainView { let mut view = KeychainView::default(); for op in ops { match &op.kind { KeychainOpKind::KeychainInit => view.initialized = true, KeychainOpKind::AdminKeyAdd { key } if !view.admin_keys.contains(key) => { view.admin_keys.push(key.clone()); } KeychainOpKind::AdminKeyRevoke { key } => view.admin_keys.retain(|item| item != key), KeychainOpKind::UserAdd { user, .. } if !view.users.contains(user) => { view.users.push(user.clone()); } KeychainOpKind::UserRevoke { user } => view.users.retain(|item| item != user), KeychainOpKind::DeviceAdd { device, .. } if !view.devices.contains(device) => { view.devices.push(device.clone()); } KeychainOpKind::DeviceRevoke { device } => view.devices.retain(|item| item != device), KeychainOpKind::NodeAdd { node, .. } if !view.nodes.contains(node) => { view.nodes.push(node.clone()); } KeychainOpKind::NodeRevoke { node } => view.nodes.retain(|item| item != node), _ => {} } } view } #[cfg(test)] mod tests { use super::*; #[test] fn keychain_structs_roundtrip() { let op = KeychainOp { id: "op:1".into(), created_at: UnixMillis(1), kind: KeychainOpKind::UserAdd { user: "user:eric".into(), name: "Eric".to_owned(), }, }; let json = serde_json::to_string(&op).expect("json"); let decoded: KeychainOp = serde_json::from_str(&json).expect("decode"); assert_eq!(decoded, op); } }