From 50abd346a4db5c39be7f1e5542ddc1ebbb88486c Mon Sep 17 00:00:00 2001 From: "glm-5.2" Date: Sun, 28 Jun 2026 21:36:15 +0000 Subject: [PATCH] feat(core): wire ConfigIdentityProvider to PeerEntry multi-credential path (core/config-identity-provider-peerentry) --- crates/alknet-core/src/auth.rs | 108 +++++++++++++++++++++++++++++++-- 1 file changed, 102 insertions(+), 6 deletions(-) diff --git a/crates/alknet-core/src/auth.rs b/crates/alknet-core/src/auth.rs index 8eac476..97ad002 100644 --- a/crates/alknet-core/src/auth.rs +++ b/crates/alknet-core/src/auth.rs @@ -152,7 +152,7 @@ mod tests { } } - fn config_with_fingerprint(peer_id: &str, fingerprint: &str) -> DynamicConfig { + fn config_with_peer_entry(peer_id: &str, fingerprint: &str) -> DynamicConfig { DynamicConfig { auth: AuthPolicy { peers: vec![peer_entry_with_fingerprint(peer_id, fingerprint)], @@ -172,6 +172,14 @@ mod tests { } } + fn compute_token_hash(token: &str) -> String { + use sha2::{Digest, Sha256}; + let mut hasher = Sha256::new(); + hasher.update(token.as_bytes()); + let result = hasher.finalize(); + format!("sha256:{}", hex::encode(result)) + } + #[test] fn identity_fields_and_equality() { let mut resources = HashMap::new(); @@ -213,7 +221,7 @@ mod tests { #[test] fn fingerprint_resolution_known_returns_some() { - let (provider, _) = make_provider(config_with_fingerprint("worker-a", "SHA256:abc123")); + let (provider, _) = make_provider(config_with_peer_entry("worker-a", "SHA256:abc123")); let identity = provider .resolve_from_fingerprint("SHA256:abc123") .expect("known fingerprint resolves"); @@ -224,7 +232,7 @@ mod tests { #[test] fn fingerprint_resolution_unknown_returns_none() { - let (provider, _) = make_provider(config_with_fingerprint("worker-a", "SHA256:abc123")); + let (provider, _) = make_provider(config_with_peer_entry("worker-a", "SHA256:abc123")); assert!(provider .resolve_from_fingerprint("SHA256:unknown") .is_none()); @@ -326,7 +334,7 @@ mod tests { let (provider, arc_swap) = make_provider(DynamicConfig::default()); assert!(provider.resolve_from_fingerprint("SHA256:abc123").is_none()); - let new_config = config_with_fingerprint("worker-a", "SHA256:abc123"); + let new_config = config_with_peer_entry("worker-a", "SHA256:abc123"); arc_swap.store(Arc::new(new_config)); assert!(provider.resolve_from_fingerprint("SHA256:abc123").is_some()); @@ -335,7 +343,7 @@ mod tests { #[test] fn config_reload_removes_fingerprint_access_immediately() { let (provider, arc_swap) = - make_provider(config_with_fingerprint("worker-a", "SHA256:abc123")); + make_provider(config_with_peer_entry("worker-a", "SHA256:abc123")); assert!(provider.resolve_from_fingerprint("SHA256:abc123").is_some()); arc_swap.store(Arc::new(DynamicConfig::default())); @@ -352,7 +360,7 @@ mod tests { assert!(provider.resolve_from_fingerprint("SHA256:abc123").is_none()); - handle.reload(config_with_fingerprint("worker-a", "SHA256:abc123")); + handle.reload(config_with_peer_entry("worker-a", "SHA256:abc123")); assert!(provider.resolve_from_fingerprint("SHA256:abc123").is_some()); } @@ -364,6 +372,94 @@ mod tests { assert_provider::(); assert_not_store::(); } + + #[test] + fn token_resolution_via_peer_entry_auth_token_hash_returns_peer_id() { + let token_str = "peer-bearer-secret"; + let mut entry = peer_entry_with_fingerprint("worker-a", "SHA256:abc123"); + entry.auth_token_hash = Some(compute_token_hash(token_str)); + let config = DynamicConfig { + auth: AuthPolicy { + peers: vec![entry], + api_keys: Vec::new(), + }, + rate_limits: RateLimitConfig::default(), + }; + let (provider, _) = make_provider(config); + let token = AuthToken { + raw: token_str.as_bytes().to_vec(), + }; + let identity = provider + .resolve_from_token(&token) + .expect("matching PeerEntry.auth_token_hash resolves"); + assert_eq!(identity.id, "worker-a"); + assert_eq!(identity.scopes, vec!["relay:connect".to_string()]); + } + + #[test] + fn token_resolution_falls_through_to_api_key_when_no_peer_entry_matches() { + let api_token = "alk_test_secret"; + let mut entry = peer_entry_with_fingerprint("worker-a", "SHA256:abc123"); + entry.auth_token_hash = Some(compute_token_hash("different-token")); + let api_entry = ApiKeyEntry { + prefix: "alk_test".to_string(), + hash: compute_api_key_hash(api_token), + scopes: vec!["admin".to_string()], + description: "fall-through key".to_string(), + expires_at: None, + }; + let config = DynamicConfig { + auth: AuthPolicy { + peers: vec![entry], + api_keys: vec![api_entry], + }, + rate_limits: RateLimitConfig::default(), + }; + let (provider, _) = make_provider(config); + let token = AuthToken { + raw: api_token.as_bytes().to_vec(), + }; + let identity = provider + .resolve_from_token(&token) + .expect("api key fall-through resolves"); + assert_eq!(identity.id, "alk_test"); + assert_eq!(identity.scopes, vec!["admin".to_string()]); + } + + #[test] + fn disabled_peer_entry_returns_none_on_fingerprint_resolution() { + let mut entry = peer_entry_with_fingerprint("worker-a", "SHA256:abc123"); + entry.enabled = false; + let config = DynamicConfig { + auth: AuthPolicy { + peers: vec![entry], + api_keys: Vec::new(), + }, + rate_limits: RateLimitConfig::default(), + }; + let (provider, _) = make_provider(config); + assert!(provider.resolve_from_fingerprint("SHA256:abc123").is_none()); + } + + #[test] + fn disabled_peer_entry_returns_none_on_token_resolution() { + let token_str = "peer-bearer-secret"; + let mut entry = peer_entry_with_fingerprint("worker-a", "SHA256:abc123"); + entry.auth_token_hash = Some(compute_token_hash(token_str)); + entry.enabled = false; + let config = DynamicConfig { + auth: AuthPolicy { + peers: vec![entry], + api_keys: Vec::new(), + }, + rate_limits: RateLimitConfig::default(), + }; + let (provider, _) = make_provider(config); + let token = AuthToken { + raw: token_str.as_bytes().to_vec(), + }; + assert!(provider.resolve_from_token(&token).is_none()); + } } #[cfg(test)]