/// Integration test: JWT validation with mocked Authentik JWKS use mem_cli::jwt_validator::{JwtValidator, JwtClaims}; use serde_json::json; /// Mock JWKS response from Authentik fn mock_jwks_response() -> String { json!({ "keys": [ { "kty": "RSA", "use": "sig", "kid": "test-key-1", "alg": "RS256", "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", "e": "AQAB" } ] }).to_string() } /// Mock OIDC discovery endpoint fn mock_discovery_response() -> String { json!({ "issuer": "https://authentik.test/application/o/memory/", "token_endpoint": "https://authentik.test/application/o/token/", "jwks_uri": "https://authentik.test/application/o/memory/jwks/", "id_token_signing_alg_values_supported": ["RS256"] }).to_string() } #[test] fn test_bearer_token_extraction() { // Test bearer token extraction from header let header = "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIn0.test"; match JwtValidator::extract_bearer_token(header) { Ok(token) => { assert_eq!(token, "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIn0.test"); } Err(e) => panic!("Failed to extract bearer token: {}", e), } } #[test] fn test_jwt_claims_with_read_permission() { let claims = JwtClaims { sub: "user123".to_string(), iss: "https://authentik.test/application/o/memory/".to_string(), aud: "poimen-memory".to_string(), exp: 9999999999, iat: 1000000000, nbf: None, permissions: Some(vec!["memory:read".to_string()]), groups: Some(vec!["users".to_string()]), }; // Verify read permission exists assert!(claims.permissions.is_some()); let perms = claims.permissions.unwrap(); assert!(perms.iter().any(|p| p == "memory:read")); } #[test] fn test_jwt_claims_with_write_permission() { let claims = JwtClaims { sub: "admin".to_string(), iss: "https://authentik.test/application/o/memory/".to_string(), aud: "poimen-memory".to_string(), exp: 9999999999, iat: 1000000000, nbf: None, permissions: Some(vec!["memory:read".to_string(), "memory:write".to_string()]), groups: Some(vec!["admins".to_string()]), }; // Verify both permissions exist assert!(claims.permissions.is_some()); let perms = claims.permissions.unwrap(); assert!(perms.iter().any(|p| p == "memory:read")); assert!(perms.iter().any(|p| p == "memory:write")); } #[test] fn test_jwt_claims_with_wildcard_permission() { let claims = JwtClaims { sub: "homelab-admin".to_string(), iss: "https://authentik.test/application/o/memory/".to_string(), aud: "poimen-memory".to_string(), exp: 9999999999, iat: 1000000000, nbf: None, permissions: Some(vec!["*".to_string()]), groups: Some(vec!["homelab-admins".to_string()]), }; // Verify wildcard permission assert!(claims.permissions.is_some()); let perms = claims.permissions.unwrap(); assert!(perms.contains(&"*".to_string())); } #[test] fn test_jwt_validator_configuration() { let issuer = "https://authentik.test/application/o/memory/".to_string(); let audience = "poimen-memory".to_string(); let cache_ttl = 3600; let validator = JwtValidator::new(issuer.clone(), audience.clone(), cache_ttl); assert_eq!(validator.issuer, issuer); assert_eq!(validator.audience, audience); } #[test] fn test_mock_jwks_response_structure() { let jwks_json = mock_jwks_response(); let jwks: serde_json::Value = serde_json::from_str(&jwks_json).unwrap(); // Verify JWKS has required structure assert!(jwks.get("keys").is_some()); let keys = jwks["keys"].as_array().unwrap(); assert!(!keys.is_empty()); let key = &keys[0]; assert_eq!(key["kty"], "RSA"); assert_eq!(key["alg"], "RS256"); assert!(key.get("kid").is_some()); assert!(key.get("n").is_some()); assert!(key.get("e").is_some()); } #[test] fn test_mock_discovery_response_structure() { let discovery_json = mock_discovery_response(); let discovery: serde_json::Value = serde_json::from_str(&discovery_json).unwrap(); // Verify discovery doc has required endpoints assert!(discovery.get("issuer").is_some()); assert!(discovery.get("token_endpoint").is_some()); assert!(discovery.get("jwks_uri").is_some()); assert!(discovery.get("id_token_signing_alg_values_supported").is_some()); assert_eq!(discovery["issuer"], "https://authentik.test/application/o/memory/"); assert_eq!(discovery["id_token_signing_alg_values_supported"][0], "RS256"); } #[test] fn test_capability_check_logic() { // Simulate capability checking fn has_capability(permissions: &Option>, required: &str) -> bool { if let Some(perms) = permissions { if perms.contains(&"*".to_string()) { return true; } perms.contains(&required.to_string()) } else { false } } // Test with read permission let read_perms = Some(vec!["memory:read".to_string()]); assert!(has_capability(&read_perms, "memory:read")); assert!(!has_capability(&read_perms, "memory:write")); // Test with write permission let write_perms = Some(vec!["memory:write".to_string()]); assert!(!has_capability(&write_perms, "memory:read")); assert!(has_capability(&write_perms, "memory:write")); // Test with wildcard let wildcard = Some(vec!["*".to_string()]); assert!(has_capability(&wildcard, "memory:read")); assert!(has_capability(&wildcard, "memory:write")); assert!(has_capability(&wildcard, "any:permission")); // Test with no permissions let no_perms: Option> = None; assert!(!has_capability(&no_perms, "memory:read")); } #[test] fn test_jwt_auth_modes() { // Test enum variants use mem_cli::http_server::AuthMode; let _jwt_mode = AuthMode::Jwt; let _apikey_mode = AuthMode::ApiKey; // Both should be created without panic println!("Auth modes created successfully"); }