Skip to main content

sigil_protocol/
identity.rs

1//! Identity — binding users to trust levels.
2//!
3//! SIGIL defines a standard identity model with trust levels and bindings.
4//! Implementations choose their identity storage and provider integration
5//! (SOUL.md, OIDC, SSI wallets, LDAP, etc.).
6
7use serde::{Deserialize, Serialize};
8
9/// Trust level for identity bindings.
10///
11/// The SIGIL protocol defines three tiers:
12/// - `Low` — anonymous or unverified
13/// - `Medium` — verified identity (email, OIDC, social login)
14/// - `High` — strong verification (eIDAS, government ID, hardware key)
15///
16/// Numeric ordering is used for comparison: Low(1) < Medium(2) < High(3).
17#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
18pub enum TrustLevel {
19    /// Anonymous or unverified user.
20    Low = 1,
21    /// Verified identity (email, OIDC, social login).
22    Medium = 2,
23    /// Strong verification (eIDAS, government ID, hardware key).
24    High = 3,
25}
26
27impl Default for TrustLevel {
28    fn default() -> Self {
29        Self::Low
30    }
31}
32
33impl std::fmt::Display for TrustLevel {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        match self {
36            TrustLevel::Low => write!(f, "Low (Level 1)"),
37            TrustLevel::Medium => write!(f, "Medium (Level 2)"),
38            TrustLevel::High => write!(f, "High (Level 3)"),
39        }
40    }
41}
42
43/// A SIGIL identity binding record.
44///
45/// Binds a user to an identity provider with a trust level.
46/// Multiple bindings can exist for the same user (e.g., Google + eIDAS).
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct IdentityBinding {
49    /// Identity provider name (e.g., "google", "eidas", "did:key", "hoodik").
50    pub provider: String,
51    /// Provider-specific user identifier.
52    pub id: String,
53    /// Trust level of this binding.
54    pub trust_level: TrustLevel,
55    /// ISO 8601 timestamp of when the binding was created.
56    pub bound_at: String,
57}
58
59/// Trait for identity management.
60///
61/// Implementations manage identity storage, provider integration,
62/// and trust level computation.
63///
64/// # Example
65///
66/// ```rust,no_run
67/// use sigil_protocol::{IdentityProvider, IdentityBinding, TrustLevel};
68///
69/// struct LdapIdentity { /* ... */ }
70///
71/// impl IdentityProvider for LdapIdentity {
72///     fn bindings(&self) -> Vec<IdentityBinding> { vec![] }
73///     fn add_binding(&mut self, provider: &str, id: &str, level: TrustLevel) -> anyhow::Result<()> {
74///         todo!()
75///     }
76///     fn max_trust_level(&self) -> TrustLevel { TrustLevel::Low }
77///     fn has_binding(&self, provider: &str) -> bool { false }
78/// }
79/// ```
80pub trait IdentityProvider: Send + Sync {
81    /// List all identity bindings.
82    fn bindings(&self) -> Vec<IdentityBinding>;
83
84    /// Add a new identity binding.
85    fn add_binding(&mut self, provider: &str, id: &str, level: TrustLevel) -> anyhow::Result<()>;
86
87    /// Compute the maximum trust level across all bindings.
88    fn max_trust_level(&self) -> TrustLevel;
89
90    /// Check if a binding exists for the given provider.
91    fn has_binding(&self, provider: &str) -> bool;
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn trust_level_ordering() {
100        assert_eq!(TrustLevel::Low as u8, 1);
101        assert_eq!(TrustLevel::High as u8, 3);
102    }
103
104    #[test]
105    fn trust_level_display() {
106        assert_eq!(format!("{}", TrustLevel::Low), "Low (Level 1)");
107        assert_eq!(format!("{}", TrustLevel::High), "High (Level 3)");
108    }
109
110    #[test]
111    fn identity_binding_serializes() {
112        let binding = IdentityBinding {
113            provider: "google".to_string(),
114            id: "user@gmail.com".to_string(),
115            trust_level: TrustLevel::Low,
116            bound_at: "2026-01-01T00:00:00Z".to_string(),
117        };
118        let json = serde_json::to_string(&binding).unwrap();
119        let parsed: IdentityBinding = serde_json::from_str(&json).unwrap();
120        assert_eq!(parsed.provider, "google");
121        assert_eq!(parsed.trust_level, TrustLevel::Low);
122    }
123
124    #[test]
125    fn default_trust_level_is_low() {
126        assert_eq!(TrustLevel::default(), TrustLevel::Low);
127    }
128}