blueprint_auth/
request_auth.rs1use std::collections::HashMap;
2
3#[derive(Clone, Debug, Default)]
5pub struct AuthContext {
6 inner: HashMap<String, String>,
7}
8
9impl AuthContext {
10 pub fn from_headers<H>(headers: &H) -> Self
12 where
13 H: std::ops::Deref<Target = std::collections::HashMap<String, String>>,
14 {
15 let mut inner = HashMap::new();
16
17 if let Some(tenant_hash) = headers.get("x-tenant-id") {
19 inner.insert("tenant_hash".to_string(), tenant_hash.clone());
20 }
21
22 if let Some(scopes_str) = headers.get("x-scopes") {
24 inner.insert("scopes".to_string(), scopes_str.clone());
25 }
26
27 AuthContext { inner }
28 }
29
30 pub fn from_axum_headers(headers: &axum::http::HeaderMap) -> Self {
32 let mut inner = HashMap::new();
33
34 if let Some(tenant_hash) = headers.get("x-tenant-id").and_then(|v| v.to_str().ok()) {
36 inner.insert("tenant_hash".to_string(), tenant_hash.to_string());
37 }
38
39 if let Some(scopes_str) = headers.get("x-scopes").and_then(|v| v.to_str().ok()) {
41 inner.insert("scopes".to_string(), scopes_str.to_string());
42 }
43
44 AuthContext { inner }
45 }
46
47 pub fn tenant_hash(&self) -> Option<&str> {
48 self.inner.get("tenant_hash").map(|s| s.as_str())
49 }
50
51 pub fn scopes(&self) -> Vec<String> {
52 self.inner
53 .get("scopes")
54 .map(|s| {
55 s.split(' ')
56 .filter(|p| !p.is_empty())
57 .map(|p| p.to_ascii_lowercase())
58 .collect()
59 })
60 .unwrap_or_default()
61 }
62
63 pub fn has_scope(&self, scope: &str) -> bool {
64 let scope = scope.to_ascii_lowercase();
65 self.scopes().contains(&scope)
66 }
67
68 pub fn has_any_scope<'a>(&self, names_or_prefixes: impl IntoIterator<Item = &'a str>) -> bool {
69 let scopes = self.scopes();
70 for n in names_or_prefixes {
71 let n = n.to_ascii_lowercase();
72 if n.ends_with(':') {
73 if scopes.iter().any(|s| s.starts_with(&n)) {
75 return true;
76 }
77 } else if scopes.contains(&n) {
78 return true;
79 }
80 }
81 false
82 }
83}