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}