| 153 | /// Wrap filter for HTTP basic authentication |
| 154 | #[cfg(feature = "http")] |
| 155 | pub fn http_basic_auth( |
| 156 | access_token: Option<String>, |
| 157 | ) -> warp::filters::BoxedFilter<(Result<(), Error>,)> { |
| 158 | use bitcoin::base64; |
| 159 | use std::sync::Arc; |
| 160 | use warp::http::StatusCode; |
| 161 | use warp::Filter; |
| 162 | |
| 163 | fn parse_header(header_val: String) -> Option<(String, String)> { |
| 164 | if header_val.to_ascii_lowercase().starts_with("basic ") { |
| 165 | let auth_base64 = &header_val[6..]; |
| 166 | let auth_decoded = String::from_utf8(base64::decode(&auth_base64).ok()?).ok()?; |
| 167 | let mut parts = auth_decoded.splitn(2, ':'); |
| 168 | Some((parts.next()?.into(), parts.next()?.into())) |
| 169 | } else { |
| 170 | None |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | if let Some(access_token) = access_token { |
| 175 | let access_token = Arc::new(access_token); |
| 176 | warp::any() |
| 177 | .and(warp::any().map(move || access_token.clone())) |
| 178 | .and(warp::header::optional("authorization")) |
| 179 | .map(|access_token: Arc<String>, auth_header: Option<String>| { |
| 180 | // We only care about the password, the username can be anything. |
| 181 | let password = auth_header.and_then(parse_header).map(|creds| creds.1); |
| 182 | ensure!( |
| 183 | password == Some(access_token.to_string()), |
| 184 | StatusCode::UNAUTHORIZED |
| 185 | ); |
| 186 | Ok(()) |
| 187 | }) |
| 188 | .boxed() |
| 189 | } else { |
| 190 | // Return a pass-through filter if authentication is disabled |
| 191 | warp::any().map(|| Ok(())).boxed() |
| 192 | } |
| 193 | } |