reddb_server/cluster/
identity.rs1use rustls::pki_types::CertificateDer;
4
5#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub struct NodeIdentity(String);
13
14pub type ReplicationPeerIdentity = NodeIdentity;
16pub type ClusterVoterIdentity = NodeIdentity;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub enum NodeIdentityError {
20 EmptySubject,
21 CertificateParse(String),
22}
23
24impl std::fmt::Display for NodeIdentityError {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 match self {
27 Self::EmptySubject => write!(f, "certificate subject is empty"),
28 Self::CertificateParse(err) => write!(f, "certificate parse error: {err}"),
29 }
30 }
31}
32
33impl std::error::Error for NodeIdentityError {}
34
35impl NodeIdentity {
36 pub fn from_certificate_subject(subject: impl AsRef<str>) -> Result<Self, NodeIdentityError> {
37 let subject = subject.as_ref().trim();
38 if subject.is_empty() {
39 return Err(NodeIdentityError::EmptySubject);
40 }
41 Ok(Self(subject.to_string()))
42 }
43
44 pub fn from_peer_certificate_der(cert: &CertificateDer<'_>) -> Result<Self, NodeIdentityError> {
45 let (_, parsed) = x509_parser::parse_x509_certificate(cert.as_ref())
46 .map_err(|err| NodeIdentityError::CertificateParse(format!("{err:?}")))?;
47 Self::from_certificate_subject(parsed.subject().to_string())
48 }
49
50 pub fn as_str(&self) -> &str {
51 &self.0
52 }
53}
54
55impl std::fmt::Display for NodeIdentity {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 f.write_str(&self.0)
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn node_identity_rejects_empty_certificate_subjects() {
67 assert_eq!(
68 NodeIdentity::from_certificate_subject(" ").unwrap_err(),
69 NodeIdentityError::EmptySubject
70 );
71 }
72
73 #[test]
74 fn cluster_voter_and_replication_peer_share_node_identity() {
75 let voter = ClusterVoterIdentity::from_certificate_subject("CN=node-a").unwrap();
76 let replica = ReplicationPeerIdentity::from_certificate_subject("CN=node-a").unwrap();
77
78 assert_eq!(voter, replica);
79 assert_eq!(voter.as_str(), "CN=node-a");
80 }
81}