use crate::v3::{AuthProtocol, MasterKeys, PrivProtocol};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum CommunityVersion {
V1,
#[default]
V2c,
}
#[derive(Debug, Clone)]
pub enum Auth {
Community {
version: CommunityVersion,
community: String,
},
Usm(UsmAuth),
}
impl Default for Auth {
fn default() -> Self {
Auth::v2c("public")
}
}
impl Auth {
pub fn v1(community: impl Into<String>) -> Self {
Auth::Community {
version: CommunityVersion::V1,
community: community.into(),
}
}
pub fn v2c(community: impl Into<String>) -> Self {
Auth::Community {
version: CommunityVersion::V2c,
community: community.into(),
}
}
pub fn usm(username: impl Into<String>) -> UsmBuilder {
UsmBuilder::new(username)
}
}
#[derive(Debug, Clone)]
pub struct UsmAuth {
pub username: String,
pub auth_protocol: Option<AuthProtocol>,
pub auth_password: Option<String>,
pub priv_protocol: Option<PrivProtocol>,
pub priv_password: Option<String>,
pub context_name: Option<String>,
pub master_keys: Option<MasterKeys>,
}
#[derive(Debug)]
pub struct UsmBuilder {
username: String,
auth: Option<(AuthProtocol, String)>,
privacy: Option<(PrivProtocol, String)>,
context_name: Option<String>,
master_keys: Option<MasterKeys>,
}
impl UsmBuilder {
pub fn new(username: impl Into<String>) -> Self {
Self {
username: username.into(),
auth: None,
privacy: None,
context_name: None,
master_keys: None,
}
}
pub fn auth(mut self, protocol: AuthProtocol, password: impl Into<String>) -> Self {
self.auth = Some((protocol, password.into()));
self
}
pub fn privacy(mut self, protocol: PrivProtocol, password: impl Into<String>) -> Self {
self.privacy = Some((protocol, password.into()));
self
}
pub fn with_master_keys(mut self, master_keys: MasterKeys) -> Self {
self.master_keys = Some(master_keys);
self
}
pub fn context_name(mut self, name: impl Into<String>) -> Self {
self.context_name = Some(name.into());
self
}
}
impl From<UsmBuilder> for Auth {
fn from(b: UsmBuilder) -> Auth {
Auth::Usm(UsmAuth {
username: b.username,
auth_protocol: b
.master_keys
.as_ref()
.map(|m| m.auth_protocol())
.or(b.auth.as_ref().map(|(p, _)| *p)),
auth_password: b.auth.map(|(_, pw)| pw),
priv_protocol: b
.master_keys
.as_ref()
.and_then(|m| m.priv_protocol())
.or(b.privacy.as_ref().map(|(p, _)| *p)),
priv_password: b.privacy.map(|(_, pw)| pw),
context_name: b.context_name,
master_keys: b.master_keys,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_auth() {
let auth = Auth::default();
match auth {
Auth::Community { version, community } => {
assert_eq!(version, CommunityVersion::V2c);
assert_eq!(community, "public");
}
_ => panic!("expected Community variant"),
}
}
#[test]
fn test_v1_auth() {
let auth = Auth::v1("private");
match auth {
Auth::Community { version, community } => {
assert_eq!(version, CommunityVersion::V1);
assert_eq!(community, "private");
}
_ => panic!("expected Community variant"),
}
}
#[test]
fn test_v2c_auth() {
let auth = Auth::v2c("secret");
match auth {
Auth::Community { version, community } => {
assert_eq!(version, CommunityVersion::V2c);
assert_eq!(community, "secret");
}
_ => panic!("expected Community variant"),
}
}
#[test]
fn test_community_version_default() {
let version = CommunityVersion::default();
assert_eq!(version, CommunityVersion::V2c);
}
#[test]
fn test_usm_no_auth_no_priv() {
let auth: Auth = Auth::usm("readonly").into();
match auth {
Auth::Usm(usm) => {
assert_eq!(usm.username, "readonly");
assert!(usm.auth_protocol.is_none());
assert!(usm.auth_password.is_none());
assert!(usm.priv_protocol.is_none());
assert!(usm.priv_password.is_none());
assert!(usm.context_name.is_none());
}
_ => panic!("expected Usm variant"),
}
}
#[test]
fn test_usm_auth_no_priv() {
let auth: Auth = Auth::usm("admin")
.auth(AuthProtocol::Sha256, "authpass123")
.into();
match auth {
Auth::Usm(usm) => {
assert_eq!(usm.username, "admin");
assert_eq!(usm.auth_protocol, Some(AuthProtocol::Sha256));
assert_eq!(usm.auth_password, Some("authpass123".to_string()));
assert!(usm.priv_protocol.is_none());
assert!(usm.priv_password.is_none());
}
_ => panic!("expected Usm variant"),
}
}
#[test]
fn test_usm_auth_priv() {
let auth: Auth = Auth::usm("admin")
.auth(AuthProtocol::Sha256, "authpass")
.privacy(PrivProtocol::Aes128, "privpass")
.into();
match auth {
Auth::Usm(usm) => {
assert_eq!(usm.username, "admin");
assert_eq!(usm.auth_protocol, Some(AuthProtocol::Sha256));
assert_eq!(usm.auth_password, Some("authpass".to_string()));
assert_eq!(usm.priv_protocol, Some(PrivProtocol::Aes128));
assert_eq!(usm.priv_password, Some("privpass".to_string()));
}
_ => panic!("expected Usm variant"),
}
}
#[test]
fn test_usm_with_context_name() {
let auth: Auth = Auth::usm("admin")
.auth(AuthProtocol::Sha256, "authpass")
.context_name("vlan100")
.into();
match auth {
Auth::Usm(usm) => {
assert_eq!(usm.username, "admin");
assert_eq!(usm.context_name, Some("vlan100".to_string()));
}
_ => panic!("expected Usm variant"),
}
}
#[test]
fn test_usm_builder_chaining() {
let auth: Auth = Auth::usm("user")
.auth(AuthProtocol::Sha512, "auth")
.privacy(PrivProtocol::Aes256, "priv")
.context_name("ctx")
.into();
match auth {
Auth::Usm(usm) => {
assert_eq!(usm.username, "user");
assert_eq!(usm.auth_protocol, Some(AuthProtocol::Sha512));
assert_eq!(usm.auth_password, Some("auth".to_string()));
assert_eq!(usm.priv_protocol, Some(PrivProtocol::Aes256));
assert_eq!(usm.priv_password, Some("priv".to_string()));
assert_eq!(usm.context_name, Some("ctx".to_string()));
}
_ => panic!("expected Usm variant"),
}
}
}