use std::time::Duration;
use tenuo::{
constraints::{Cidr, ConstraintSet, UrlPattern},
crypto::SigningKey,
warrant::Warrant,
wire, Any, Constraint, Exact, Pattern, Range,
};
#[test]
fn test_cidr_ipv4_basic_matching() {
let cidr = Cidr::new("10.0.0.0/8").unwrap();
let constraint = Constraint::Cidr(cidr);
assert!(constraint.matches(&"10.0.0.1".into()).unwrap());
assert!(constraint.matches(&"10.255.255.255".into()).unwrap());
assert!(constraint.matches(&"10.123.45.67".into()).unwrap());
assert!(!constraint.matches(&"11.0.0.1".into()).unwrap());
assert!(!constraint.matches(&"192.168.1.1".into()).unwrap());
assert!(!constraint.matches(&"9.255.255.255".into()).unwrap());
}
#[test]
fn test_cidr_ipv4_specific_subnet() {
let cidr = Cidr::new("192.168.1.0/24").unwrap();
let constraint = Constraint::Cidr(cidr);
assert!(constraint.matches(&"192.168.1.0".into()).unwrap());
assert!(constraint.matches(&"192.168.1.255".into()).unwrap());
assert!(constraint.matches(&"192.168.1.100".into()).unwrap());
assert!(!constraint.matches(&"192.168.0.1".into()).unwrap());
assert!(!constraint.matches(&"192.168.2.1".into()).unwrap());
}
#[test]
fn test_cidr_ipv6_basic_matching() {
let cidr = Cidr::new("2001:db8::/32").unwrap();
let constraint = Constraint::Cidr(cidr);
assert!(constraint.matches(&"2001:db8::1".into()).unwrap());
assert!(constraint
.matches(&"2001:db8:ffff:ffff:ffff:ffff:ffff:ffff".into())
.unwrap());
assert!(!constraint.matches(&"2001:db9::1".into()).unwrap());
assert!(!constraint.matches(&"::1".into()).unwrap());
}
#[test]
fn test_cidr_invalid_network() {
assert!(Cidr::new("not-a-cidr").is_err());
assert!(Cidr::new("10.0.0.0/33").is_err()); assert!(Cidr::new("10.0.0.0/-1").is_err());
}
#[test]
fn test_cidr_in_warrant_constraint_set() {
let keypair = SigningKey::generate();
let mut constraints = ConstraintSet::new();
constraints.insert("source_ip", Cidr::new("10.0.0.0/8").unwrap());
let warrant = Warrant::builder()
.capability("network_call", constraints)
.ttl(Duration::from_secs(3600))
.holder(keypair.public_key())
.build(&keypair)
.unwrap();
assert!(warrant.capabilities().unwrap().contains_key("network_call"));
}
#[test]
fn test_cidr_attenuation_narrowing() {
let keypair = SigningKey::generate();
let child_keypair = SigningKey::generate();
let mut parent_constraints = ConstraintSet::new();
parent_constraints.insert("ip", Cidr::new("10.0.0.0/8").unwrap());
let parent = Warrant::builder()
.capability("api_call", parent_constraints)
.ttl(Duration::from_secs(3600))
.holder(keypair.public_key())
.build(&keypair)
.unwrap();
let mut child_constraints = ConstraintSet::new();
child_constraints.insert("ip", Cidr::new("10.1.0.0/16").unwrap());
let child_result = parent
.attenuate()
.capability("api_call", child_constraints)
.holder(child_keypair.public_key())
.build(&keypair);
assert!(child_result.is_ok(), "Narrowing CIDR should be allowed");
}
#[test]
fn test_urlpattern_basic_matching() {
let pattern = UrlPattern::new("https://example.com/*").unwrap();
let constraint = Constraint::UrlPattern(pattern);
assert!(constraint.matches(&"https://example.com/".into()).unwrap());
assert!(constraint
.matches(&"https://example.com/api/v1".into())
.unwrap());
assert!(constraint
.matches(&"https://example.com/users/123".into())
.unwrap());
assert!(!constraint.matches(&"http://example.com/".into()).unwrap());
assert!(!constraint.matches(&"https://evil.com/".into()).unwrap());
}
#[test]
fn test_urlpattern_wildcard_subdomain() {
let pattern = UrlPattern::new("https://*.example.com/*").unwrap();
let constraint = Constraint::UrlPattern(pattern);
assert!(constraint
.matches(&"https://api.example.com/".into())
.unwrap());
assert!(constraint
.matches(&"https://www.example.com/path".into())
.unwrap());
assert!(!constraint.matches(&"https://api.evil.com/".into()).unwrap());
}
#[test]
fn test_urlpattern_path_constraints() {
let pattern = UrlPattern::new("https://api.example.com/v1/*").unwrap();
let constraint = Constraint::UrlPattern(pattern);
assert!(constraint
.matches(&"https://api.example.com/v1/users".into())
.unwrap());
assert!(constraint
.matches(&"https://api.example.com/v1/orders/123".into())
.unwrap());
assert!(!constraint
.matches(&"https://api.example.com/v2/users".into())
.unwrap());
assert!(!constraint
.matches(&"https://api.example.com/admin".into())
.unwrap());
}
#[test]
fn test_urlpattern_in_warrant() {
let keypair = SigningKey::generate();
let mut constraints = ConstraintSet::new();
constraints.insert(
"endpoint",
UrlPattern::new("https://api.example.com/*").unwrap(),
);
let warrant = Warrant::builder()
.capability("http_request", constraints)
.ttl(Duration::from_secs(3600))
.holder(keypair.public_key())
.build(&keypair)
.unwrap();
assert!(warrant.capabilities().unwrap().contains_key("http_request"));
}
#[test]
fn test_any_basic_or_logic() {
let any_constraint = Constraint::Any(Any::new([
Constraint::Exact(Exact::new("value1")),
Constraint::Exact(Exact::new("value2")),
Constraint::Exact(Exact::new("value3")),
]));
assert!(any_constraint.matches(&"value1".into()).unwrap());
assert!(any_constraint.matches(&"value2".into()).unwrap());
assert!(any_constraint.matches(&"value3".into()).unwrap());
assert!(!any_constraint.matches(&"value4".into()).unwrap());
assert!(!any_constraint.matches(&"other".into()).unwrap());
}
#[test]
fn test_any_with_mixed_constraint_types() {
let any_constraint = Constraint::Any(Any::new([
Constraint::Exact(Exact::new("exact_match")),
Constraint::Pattern(Pattern::new("prefix_*").unwrap()),
Constraint::Range(Range::new(Some(0.0), Some(100.0)).unwrap()),
]));
assert!(any_constraint.matches(&"exact_match".into()).unwrap());
assert!(any_constraint.matches(&"prefix_something".into()).unwrap());
assert!(any_constraint.matches(&50.0.into()).unwrap());
assert!(!any_constraint.matches(&"no_match".into()).unwrap());
assert!(!any_constraint.matches(&200.0.into()).unwrap());
}
#[test]
fn test_any_empty_always_fails() {
let any_constraint = Constraint::Any(Any::new([]));
assert!(!any_constraint.matches(&"anything".into()).unwrap());
assert!(!any_constraint.matches(&42.0.into()).unwrap());
}
#[test]
fn test_any_in_warrant() {
let keypair = SigningKey::generate();
let mut constraints = ConstraintSet::new();
constraints.insert(
"env",
Any::new([
Constraint::Exact(Exact::new("dev")),
Constraint::Exact(Exact::new("staging")),
]),
);
let warrant = Warrant::builder()
.capability("deploy", constraints)
.ttl(Duration::from_secs(3600))
.holder(keypair.public_key())
.build(&keypair)
.unwrap();
assert!(warrant.capabilities().unwrap().contains_key("deploy"));
}
#[test]
fn test_any_attenuation() {
let keypair = SigningKey::generate();
let child_keypair = SigningKey::generate();
let mut parent_constraints = ConstraintSet::new();
parent_constraints.insert(
"env",
Any::new([
Constraint::Exact(Exact::new("dev")),
Constraint::Exact(Exact::new("staging")),
Constraint::Exact(Exact::new("prod")),
]),
);
let parent = Warrant::builder()
.capability("deploy", parent_constraints)
.ttl(Duration::from_secs(3600))
.holder(keypair.public_key())
.build(&keypair)
.unwrap();
let mut child_constraints = ConstraintSet::new();
child_constraints.insert(
"env",
Any::new([
Constraint::Exact(Exact::new("dev")),
Constraint::Exact(Exact::new("staging")),
]),
);
let child_result = parent
.attenuate()
.capability("deploy", child_constraints)
.holder(child_keypair.public_key())
.build(&keypair);
if child_result.is_err() {
eprintln!(
"Note: Any constraint narrowing not supported: {:?}",
child_result.err()
);
}
}
#[test]
fn test_cidr_wire_roundtrip() {
let keypair = SigningKey::generate();
let mut constraints = ConstraintSet::new();
constraints.insert("ip", Cidr::new("192.168.0.0/16").unwrap());
let warrant = Warrant::builder()
.capability("network", constraints)
.ttl(Duration::from_secs(3600))
.holder(keypair.public_key())
.build(&keypair)
.unwrap();
let encoded = wire::encode(&warrant).unwrap();
let decoded = wire::decode(&encoded).unwrap();
let caps = decoded.capabilities().unwrap();
assert!(caps.contains_key("network"));
let network_caps = caps.get("network").unwrap();
let ip_constraint = network_caps.get("ip").unwrap();
assert!(ip_constraint.matches(&"192.168.1.100".into()).unwrap());
assert!(!ip_constraint.matches(&"10.0.0.1".into()).unwrap());
}
#[test]
fn test_urlpattern_wire_roundtrip() {
let keypair = SigningKey::generate();
let mut constraints = ConstraintSet::new();
constraints.insert("url", UrlPattern::new("https://api.example.com/*").unwrap());
let warrant = Warrant::builder()
.capability("fetch", constraints)
.ttl(Duration::from_secs(3600))
.holder(keypair.public_key())
.build(&keypair)
.unwrap();
let encoded = wire::encode(&warrant).unwrap();
let decoded = wire::decode(&encoded).unwrap();
let caps = decoded.capabilities().unwrap();
let url_constraint = caps.get("fetch").unwrap().get("url").unwrap();
assert!(url_constraint
.matches(&"https://api.example.com/data".into())
.unwrap());
assert!(!url_constraint
.matches(&"https://evil.com/steal".into())
.unwrap());
}
#[test]
fn test_any_wire_roundtrip() {
let keypair = SigningKey::generate();
let mut constraints = ConstraintSet::new();
constraints.insert(
"status",
Any::new([
Constraint::Exact(Exact::new("pending")),
Constraint::Exact(Exact::new("approved")),
]),
);
let warrant = Warrant::builder()
.capability("update_order", constraints)
.ttl(Duration::from_secs(3600))
.holder(keypair.public_key())
.build(&keypair)
.unwrap();
let encoded = wire::encode(&warrant).unwrap();
let decoded = wire::decode(&encoded).unwrap();
let caps = decoded.capabilities().unwrap();
let status_constraint = caps.get("update_order").unwrap().get("status").unwrap();
assert!(status_constraint.matches(&"pending".into()).unwrap());
assert!(status_constraint.matches(&"approved".into()).unwrap());
assert!(!status_constraint.matches(&"rejected".into()).unwrap());
}
#[test]
fn test_cidr_single_host() {
let cidr = Cidr::new("10.0.0.1/32").unwrap();
let constraint = Constraint::Cidr(cidr);
assert!(constraint.matches(&"10.0.0.1".into()).unwrap());
assert!(!constraint.matches(&"10.0.0.2".into()).unwrap());
}
#[test]
fn test_any_single_option() {
let any_constraint = Constraint::Any(Any::new([Constraint::Exact(Exact::new("only_option"))]));
assert!(any_constraint.matches(&"only_option".into()).unwrap());
assert!(!any_constraint.matches(&"other".into()).unwrap());
}
#[test]
fn test_nested_any_in_all() {
use tenuo::All;
let constraint = Constraint::All(All::new([
Constraint::Any(Any::new([
Constraint::Exact(Exact::new("a")),
Constraint::Exact(Exact::new("b")),
])),
Constraint::Any(Any::new([
Constraint::Exact(Exact::new("c")),
Constraint::Exact(Exact::new("d")),
])),
]));
assert!(!constraint.matches(&"a".into()).unwrap());
assert!(!constraint.matches(&"c".into()).unwrap());
}