spiffe_rustls/
authorizer.rs1use crate::error::{AuthorizerConfigError, Result};
4use spiffe::{SpiffeId, TrustDomain};
5use std::collections::BTreeSet;
6use std::sync::Arc;
7
8pub trait Authorizer: Send + Sync + 'static {
13 fn authorize(&self, peer: &SpiffeId) -> bool;
15}
16
17impl<F> Authorizer for F
20where
21 F: Fn(&SpiffeId) -> bool + Send + Sync + 'static,
22{
23 fn authorize(&self, peer: &SpiffeId) -> bool {
24 self(peer)
25 }
26}
27
28impl Authorizer for Arc<dyn Authorizer> {
29 fn authorize(&self, peer: &SpiffeId) -> bool {
30 (**self).authorize(peer)
31 }
32}
33
34impl Authorizer for Box<dyn Authorizer> {
35 fn authorize(&self, peer: &SpiffeId) -> bool {
36 (**self).authorize(peer)
37 }
38}
39
40#[must_use]
49#[derive(Debug, Clone, Copy, Default)]
50pub struct Any;
51
52impl Authorizer for Any {
53 fn authorize(&self, _peer: &SpiffeId) -> bool {
54 true
55 }
56}
57
58#[must_use]
60#[derive(Debug, Clone)]
61pub struct Exact {
62 allowed: BTreeSet<SpiffeId>,
63}
64
65impl Exact {
66 pub fn new<I>(ids: I) -> Result<Self>
74 where
75 I: IntoIterator,
76 I::Item: TryInto<SpiffeId>,
77 <I::Item as TryInto<SpiffeId>>::Error: std::fmt::Display,
78 {
79 let mut allowed = BTreeSet::new();
80
81 for id in ids {
82 let spiffe_id = id
83 .try_into()
84 .map_err(|e| AuthorizerConfigError::InvalidSpiffeId(e.to_string()))?;
85 allowed.insert(spiffe_id);
86 }
87
88 Ok(Self { allowed })
89 }
90}
91
92impl Authorizer for Exact {
93 fn authorize(&self, peer: &SpiffeId) -> bool {
94 self.allowed.contains(peer)
95 }
96}
97
98#[must_use]
100#[derive(Debug, Clone)]
101pub struct TrustDomainAllowList {
102 allowed: BTreeSet<TrustDomain>,
103}
104
105#[deprecated(
110 since = "0.4.1",
111 note = "Renamed to TrustDomainAllowList; TrustDomains remains as a compatibility alias."
112)]
113pub type TrustDomains = TrustDomainAllowList;
114
115impl TrustDomainAllowList {
116 pub fn new<I>(domains: I) -> Result<Self>
124 where
125 I: IntoIterator,
126 I::Item: TryInto<TrustDomain>,
127 <I::Item as TryInto<TrustDomain>>::Error: std::fmt::Display,
128 {
129 let mut allowed = BTreeSet::new();
130
131 for domain in domains {
132 let td = domain
133 .try_into()
134 .map_err(|e| AuthorizerConfigError::InvalidTrustDomain(e.to_string()))?;
135 allowed.insert(td);
136 }
137
138 Ok(Self { allowed })
139 }
140}
141
142impl Authorizer for TrustDomainAllowList {
143 fn authorize(&self, peer: &SpiffeId) -> bool {
144 self.allowed.contains(peer.trust_domain())
145 }
146}
147
148pub const fn any() -> Any {
171 Any
172}
173
174pub fn exact<I>(ids: I) -> Result<Exact>
200where
201 I: IntoIterator,
202 I::Item: TryInto<SpiffeId>,
203 <I::Item as TryInto<SpiffeId>>::Error: std::fmt::Display,
204{
205 Exact::new(ids)
206}
207
208pub fn trust_domains<I>(domains: I) -> Result<TrustDomainAllowList>
234where
235 I: IntoIterator,
236 I::Item: TryInto<TrustDomain>,
237 <I::Item as TryInto<TrustDomain>>::Error: std::fmt::Display,
238{
239 TrustDomainAllowList::new(domains)
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 #[test]
247 fn test_exact_authorizer() {
248 let id1 = SpiffeId::new("spiffe://example.org/service1").unwrap();
249 let id2 = SpiffeId::new("spiffe://example.org/service2").unwrap();
250 let id3 = SpiffeId::new("spiffe://other.org/service1").unwrap();
251
252 let auth = Exact::new([id1.clone(), id2.clone()]).unwrap();
253 assert!(auth.authorize(&id1));
254 assert!(auth.authorize(&id2));
255 assert!(!auth.authorize(&id3));
256 }
257
258 #[test]
259 fn test_exact_authorizer_rejects_invalid() {
260 let result = Exact::new(["invalid-spiffe-id", "also-invalid"]);
261 result.unwrap_err();
262 }
263
264 #[test]
265 fn test_trust_domains_authorizer() {
266 let td1 = TrustDomain::new("example.org").unwrap();
267 let td2 = TrustDomain::new("other.org").unwrap();
268
269 let id1 = SpiffeId::new("spiffe://example.org/service1").unwrap();
270 let id2 = SpiffeId::new("spiffe://example.org/service2").unwrap();
271 let id3 = SpiffeId::new("spiffe://other.org/service1").unwrap();
272 let id4 = SpiffeId::new("spiffe://third.org/service1").unwrap();
273
274 let auth = TrustDomainAllowList::new([td1, td2]).unwrap();
275 assert!(auth.authorize(&id1));
276 assert!(auth.authorize(&id2));
277 assert!(auth.authorize(&id3));
278 assert!(!auth.authorize(&id4));
279 }
280
281 #[test]
282 fn test_trust_domains_authorizer_rejects_invalid() {
283 let result = TrustDomainAllowList::new(["Invalid@Trust#Domain"]);
286 result.unwrap_err();
287
288 let valid = TrustDomainAllowList::new(["example.org", "other.org"]).unwrap();
290 let id1 = SpiffeId::new("spiffe://example.org/service").unwrap();
291 let id2 = SpiffeId::new("spiffe://other.org/service").unwrap();
292 let id3 = SpiffeId::new("spiffe://rejected.org/service").unwrap();
293 assert!(valid.authorize(&id1));
294 assert!(valid.authorize(&id2));
295 assert!(!valid.authorize(&id3));
296 }
297
298 #[test]
299 fn test_any_authorizer_always_authorizes() {
300 let auth = any();
302 let id1 = SpiffeId::new("spiffe://example.org/service").unwrap();
303 let id2 = SpiffeId::new("spiffe://other.org/another").unwrap();
304 let id3 = SpiffeId::new("spiffe://test.domain/path/to/resource").unwrap();
305
306 assert!(auth.authorize(&id1));
308 assert!(auth.authorize(&id2));
309 assert!(auth.authorize(&id3));
310 }
311}