1use crate::http::{HttpRequest, HttpResponse};
2use crate::state::ControlPlaneConfigHandle;
3
4pub fn check_auth(
7 req: &HttpRequest,
8 config: &ControlPlaneConfigHandle,
9) -> Result<(), HttpResponse> {
10 let config = config.read().unwrap();
11 if config.disable_auth {
12 return Ok(());
13 }
14
15 let expected = match &config.auth_token {
16 Some(t) => t.as_str(),
17 None => return Ok(()), };
19
20 let auth_header = req.headers.get("authorization");
21 match auth_header {
22 Some(val) => {
23 if let Some(token) = val.strip_prefix("Bearer ") {
24 if token == expected {
25 Ok(())
26 } else {
27 Err(HttpResponse::unauthorized("Invalid token"))
28 }
29 } else {
30 Err(HttpResponse::unauthorized("Expected Bearer token"))
31 }
32 }
33 None => Err(HttpResponse::unauthorized("Missing Authorization header")),
34 }
35}
36
37pub fn check_ws_auth(query: &str, config: &ControlPlaneConfigHandle) -> Result<(), HttpResponse> {
39 let config = config.read().unwrap();
40 if config.disable_auth {
41 return Ok(());
42 }
43
44 let expected = match &config.auth_token {
45 Some(t) => t.as_str(),
46 None => return Ok(()),
47 };
48
49 let params = crate::http::parse_query(query);
50 match params.get("token") {
51 Some(token) if token == expected => Ok(()),
52 _ => Err(HttpResponse::unauthorized("Missing or invalid token")),
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59 use std::collections::HashMap;
60
61 fn make_config(token: Option<&str>, disable: bool) -> crate::state::ControlPlaneConfigHandle {
62 std::sync::Arc::new(std::sync::RwLock::new(crate::config::CtlConfig {
63 auth_token: token.map(String::from),
64 disable_auth: disable,
65 ..crate::config::CtlConfig::default()
66 }))
67 }
68
69 fn make_req(auth_header: Option<&str>) -> HttpRequest {
70 let mut headers = HashMap::new();
71 if let Some(val) = auth_header {
72 headers.insert("authorization".into(), val.into());
73 }
74 HttpRequest {
75 method: "GET".into(),
76 path: "/api/info".into(),
77 query: String::new(),
78 headers,
79 body: Vec::new(),
80 }
81 }
82
83 #[test]
84 fn auth_disabled() {
85 let config = make_config(Some("secret"), true);
86 assert!(check_auth(&make_req(None), &config).is_ok());
87 }
88
89 #[test]
90 fn auth_no_token_configured() {
91 let config = make_config(None, false);
92 assert!(check_auth(&make_req(None), &config).is_ok());
93 }
94
95 #[test]
96 fn auth_valid_token() {
97 let config = make_config(Some("secret"), false);
98 assert!(check_auth(&make_req(Some("Bearer secret")), &config).is_ok());
99 }
100
101 #[test]
102 fn auth_invalid_token() {
103 let config = make_config(Some("secret"), false);
104 assert!(check_auth(&make_req(Some("Bearer wrong")), &config).is_err());
105 }
106
107 #[test]
108 fn auth_missing_header() {
109 let config = make_config(Some("secret"), false);
110 assert!(check_auth(&make_req(None), &config).is_err());
111 }
112
113 #[test]
114 fn ws_auth_valid() {
115 let config = make_config(Some("abc"), false);
116 assert!(check_ws_auth("token=abc", &config).is_ok());
117 }
118
119 #[test]
120 fn ws_auth_invalid() {
121 let config = make_config(Some("abc"), false);
122 assert!(check_ws_auth("token=xyz", &config).is_err());
123 }
124}