137 lines
3.7 KiB
Rust
137 lines
3.7 KiB
Rust
|
|
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<u8>,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[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<KeyId>,
|
||
|
|
pub users: Vec<UserId>,
|
||
|
|
pub devices: Vec<DeviceId>,
|
||
|
|
pub nodes: Vec<NodeId>,
|
||
|
|
}
|
||
|
|
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
}
|