pub struct Capability;
impl Capability {
pub const CHAT_SEND: &str = "chat.send";
pub const CHAT_RECEIVE: &str = "chat.receive";
pub const PAGE_BROWSE: &str = "page.browse";
pub const RPC_PING: &str = "rpc.ping";
pub const RPC_STATUS: &str = "rpc.status";
pub const DATALINK_PING: &str = "datalink.ping";
pub const DATALINK_META: &str = "datalink.meta";
pub const DATALINK_INFO: &str = "datalink.info";
pub const DATALINK_STATUS: &str = "datalink.status";
pub const RELAY_REQUEST: &str = "relay.request";
pub const RELAY_LIST: &str = "relay.list";
pub const RELAY_TEARDOWN: &str = "relay.teardown";
pub const RELAY_ACCEPT: &str = "relay.accept";
pub const RPC_INBOX_READ: &str = "rpc.inbox_read";
pub const WEB_READ: &str = "web.read";
pub const DATALINK_ESTABLISH: &str = "datalink.establish";
pub const DATALINK_SPEEDTEST: &str = "datalink.speedtest";
pub const RPC_CONFIG_UPDATE: &str = "rpc.config_update";
pub const TERMINAL_RESTRICTED: &str = "terminal.restricted";
pub const WEB_WRITE: &str = "web.write";
pub const RELAY_REQUEST_PERMANENT: &str = "relay.request_permanent";
pub const RELAY_ACCEPT_PERMANENT: &str = "relay.accept_permanent";
pub const RELAY_PRIORITIZE: &str = "relay.prioritize";
pub const RELAY_BRIDGE: &str = "relay.bridge";
pub const RPC_EXEC: &str = "rpc.exec";
pub const RPC_REBOOT: &str = "rpc.reboot";
pub const RPC_SELF_UPDATE: &str = "rpc.self_update";
pub const TERMINAL_FULL: &str = "terminal.full";
pub const ADAPTER_PROVISION: &str = "adapter.provision";
pub const RELAY_ADMIN: &str = "relay.admin";
pub const TUNNEL_STATUS: &str = "tunnel.status";
pub const TUNNEL_ESTABLISH: &str = "tunnel.establish";
pub const TUNNEL_TEARDOWN: &str = "tunnel.teardown";
pub const VPN_HANDSHAKE: &str = "vpn.handshake";
pub const RELAY_REJECT: &str = "relay.reject";
pub const I2P_PROXY: &str = "i2p.proxy";
pub const AETHER_DELEGATE: &str = "aether.delegate";
pub const AETHER_QUERY: &str = "aether.query";
pub const AETHER_REPORT: &str = "aether.report";
}
pub const ALL_CAPABILITIES: &[&str] = &[
Capability::CHAT_SEND,
Capability::CHAT_RECEIVE,
Capability::PAGE_BROWSE,
Capability::RPC_PING,
Capability::RPC_STATUS,
Capability::DATALINK_PING,
Capability::DATALINK_META,
Capability::DATALINK_INFO,
Capability::DATALINK_STATUS,
Capability::RELAY_REQUEST,
Capability::RELAY_LIST,
Capability::RELAY_TEARDOWN,
Capability::RELAY_ACCEPT,
Capability::RPC_INBOX_READ,
Capability::WEB_READ,
Capability::DATALINK_ESTABLISH,
Capability::DATALINK_SPEEDTEST,
Capability::RPC_CONFIG_UPDATE,
Capability::TERMINAL_RESTRICTED,
Capability::WEB_WRITE,
Capability::RELAY_REQUEST_PERMANENT,
Capability::RELAY_ACCEPT_PERMANENT,
Capability::RELAY_PRIORITIZE,
Capability::RELAY_BRIDGE,
Capability::RPC_EXEC,
Capability::RPC_REBOOT,
Capability::RPC_SELF_UPDATE,
Capability::TERMINAL_FULL,
Capability::ADAPTER_PROVISION,
Capability::RELAY_ADMIN,
Capability::TUNNEL_STATUS,
Capability::TUNNEL_ESTABLISH,
Capability::TUNNEL_TEARDOWN,
Capability::VPN_HANDSHAKE,
Capability::RELAY_REJECT,
Capability::I2P_PROXY,
Capability::AETHER_DELEGATE,
Capability::AETHER_QUERY,
Capability::AETHER_REPORT,
];
pub const PEER_CAPS: &[&str] = &[
Capability::CHAT_SEND,
Capability::CHAT_RECEIVE,
Capability::PAGE_BROWSE,
Capability::RPC_PING,
Capability::RPC_STATUS,
Capability::DATALINK_PING,
Capability::DATALINK_META,
Capability::DATALINK_INFO,
Capability::DATALINK_STATUS,
Capability::RELAY_REQUEST,
Capability::RELAY_LIST,
Capability::RELAY_TEARDOWN,
Capability::RELAY_ACCEPT,
Capability::TUNNEL_STATUS,
Capability::AETHER_QUERY,
Capability::AETHER_REPORT,
];
pub const MONITOR_CAPS: &[&str] = &[
Capability::CHAT_SEND,
Capability::CHAT_RECEIVE,
Capability::PAGE_BROWSE,
Capability::RPC_PING,
Capability::RPC_STATUS,
Capability::DATALINK_PING,
Capability::DATALINK_META,
Capability::DATALINK_INFO,
Capability::DATALINK_STATUS,
Capability::RELAY_REQUEST,
Capability::RELAY_LIST,
Capability::RELAY_TEARDOWN,
Capability::RELAY_ACCEPT,
Capability::TUNNEL_STATUS,
Capability::AETHER_QUERY,
Capability::AETHER_REPORT,
Capability::RPC_INBOX_READ,
Capability::WEB_READ,
Capability::DATALINK_ESTABLISH,
Capability::DATALINK_SPEEDTEST,
];
pub const OPERATOR_CAPS: &[&str] = &[
Capability::CHAT_SEND,
Capability::CHAT_RECEIVE,
Capability::PAGE_BROWSE,
Capability::RPC_PING,
Capability::RPC_STATUS,
Capability::DATALINK_PING,
Capability::DATALINK_META,
Capability::DATALINK_INFO,
Capability::DATALINK_STATUS,
Capability::RELAY_REQUEST,
Capability::RELAY_LIST,
Capability::RELAY_TEARDOWN,
Capability::RELAY_ACCEPT,
Capability::TUNNEL_STATUS,
Capability::AETHER_QUERY,
Capability::AETHER_REPORT,
Capability::RPC_INBOX_READ,
Capability::WEB_READ,
Capability::DATALINK_ESTABLISH,
Capability::DATALINK_SPEEDTEST,
Capability::RPC_CONFIG_UPDATE,
Capability::TERMINAL_RESTRICTED,
Capability::WEB_WRITE,
Capability::RELAY_REQUEST_PERMANENT,
Capability::RELAY_ACCEPT_PERMANENT,
Capability::RELAY_PRIORITIZE,
Capability::RELAY_BRIDGE,
Capability::TUNNEL_ESTABLISH,
Capability::TUNNEL_TEARDOWN,
Capability::AETHER_DELEGATE,
];
pub const ADMIN_CAPS: &[&str] = &[
Capability::CHAT_SEND,
Capability::CHAT_RECEIVE,
Capability::PAGE_BROWSE,
Capability::RPC_PING,
Capability::RPC_STATUS,
Capability::DATALINK_PING,
Capability::DATALINK_META,
Capability::DATALINK_INFO,
Capability::DATALINK_STATUS,
Capability::RELAY_REQUEST,
Capability::RELAY_LIST,
Capability::RELAY_TEARDOWN,
Capability::RELAY_ACCEPT,
Capability::TUNNEL_STATUS,
Capability::AETHER_QUERY,
Capability::AETHER_REPORT,
Capability::RPC_INBOX_READ,
Capability::WEB_READ,
Capability::DATALINK_ESTABLISH,
Capability::DATALINK_SPEEDTEST,
Capability::RPC_CONFIG_UPDATE,
Capability::TERMINAL_RESTRICTED,
Capability::WEB_WRITE,
Capability::RELAY_REQUEST_PERMANENT,
Capability::RELAY_ACCEPT_PERMANENT,
Capability::RELAY_PRIORITIZE,
Capability::RELAY_BRIDGE,
Capability::TUNNEL_ESTABLISH,
Capability::TUNNEL_TEARDOWN,
Capability::AETHER_DELEGATE,
Capability::RPC_EXEC,
Capability::RPC_REBOOT,
Capability::RPC_SELF_UPDATE,
Capability::TERMINAL_FULL,
Capability::ADAPTER_PROVISION,
Capability::RELAY_ADMIN,
];
use crate::Role;
pub fn capabilities_for_role(role: Role) -> &'static [&'static str] {
match role {
Role::Blocked | Role::None => &[],
Role::Peer => PEER_CAPS,
Role::Monitor => MONITOR_CAPS,
Role::Operator => OPERATOR_CAPS,
Role::Admin => ADMIN_CAPS,
}
}
pub fn is_valid_capability(cap: &str) -> bool {
ALL_CAPABILITIES.contains(&cap)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cumulative_inclusion() {
for cap in PEER_CAPS {
assert!(MONITOR_CAPS.contains(cap), "monitor missing peer cap: {cap}");
assert!(OPERATOR_CAPS.contains(cap), "operator missing peer cap: {cap}");
assert!(ADMIN_CAPS.contains(cap), "admin missing peer cap: {cap}");
}
for cap in MONITOR_CAPS {
assert!(OPERATOR_CAPS.contains(cap), "operator missing monitor cap: {cap}");
assert!(ADMIN_CAPS.contains(cap), "admin missing monitor cap: {cap}");
}
for cap in OPERATOR_CAPS {
assert!(ADMIN_CAPS.contains(cap), "admin missing operator cap: {cap}");
}
}
#[test]
fn orthogonal_not_in_admin() {
assert!(!ADMIN_CAPS.contains(&Capability::VPN_HANDSHAKE));
assert!(!ADMIN_CAPS.contains(&Capability::RELAY_REJECT));
}
#[test]
fn aether_caps_in_hierarchy() {
assert!(PEER_CAPS.contains(&Capability::AETHER_QUERY));
assert!(PEER_CAPS.contains(&Capability::AETHER_REPORT));
assert!(!PEER_CAPS.contains(&Capability::AETHER_DELEGATE));
assert!(OPERATOR_CAPS.contains(&Capability::AETHER_DELEGATE));
}
#[test]
fn all_capabilities_contains_everything() {
for cap in ADMIN_CAPS {
assert!(ALL_CAPABILITIES.contains(cap), "ALL missing admin cap: {cap}");
}
assert!(ALL_CAPABILITIES.contains(&Capability::VPN_HANDSHAKE));
assert!(ALL_CAPABILITIES.contains(&Capability::RELAY_REJECT));
}
#[test]
fn tunnel_caps_in_hierarchy() {
assert!(PEER_CAPS.contains(&Capability::TUNNEL_STATUS));
assert!(MONITOR_CAPS.contains(&Capability::TUNNEL_STATUS));
assert!(OPERATOR_CAPS.contains(&Capability::TUNNEL_STATUS));
assert!(ADMIN_CAPS.contains(&Capability::TUNNEL_STATUS));
assert!(!PEER_CAPS.contains(&Capability::TUNNEL_ESTABLISH));
assert!(!MONITOR_CAPS.contains(&Capability::TUNNEL_ESTABLISH));
assert!(OPERATOR_CAPS.contains(&Capability::TUNNEL_ESTABLISH));
assert!(ADMIN_CAPS.contains(&Capability::TUNNEL_ESTABLISH));
assert!(!PEER_CAPS.contains(&Capability::TUNNEL_TEARDOWN));
assert!(OPERATOR_CAPS.contains(&Capability::TUNNEL_TEARDOWN));
assert!(ADMIN_CAPS.contains(&Capability::TUNNEL_TEARDOWN));
}
#[test]
fn capabilities_for_blocked_is_empty() {
assert!(capabilities_for_role(Role::Blocked).is_empty());
assert!(capabilities_for_role(Role::None).is_empty());
}
}