fix(graylog): use basic auth with base64 token fallback
This commit is contained in:
parent
d6834cf648
commit
09d26d62f8
1 changed files with 76 additions and 3 deletions
|
|
@ -1,4 +1,5 @@
|
|||
use crate::services::graylog_scoring::GraylogEvent;
|
||||
use base64::{engine::general_purpose::STANDARD, Engine};
|
||||
use serde_json::Value;
|
||||
use std::time::Instant;
|
||||
use tokio::time::{sleep, Duration};
|
||||
|
|
@ -18,7 +19,11 @@ impl GraylogClient {
|
|||
}
|
||||
}
|
||||
|
||||
async fn send_get(&self, url: &str) -> Result<reqwest::Response, String> {
|
||||
async fn send_get_with_auth(
|
||||
&self,
|
||||
url: &str,
|
||||
auth_token: &str,
|
||||
) -> Result<reqwest::Response, String> {
|
||||
const MAX_ATTEMPTS: u32 = 3;
|
||||
const BASE_DELAY_MS: u64 = 500;
|
||||
|
||||
|
|
@ -32,7 +37,7 @@ impl GraylogClient {
|
|||
let response = self
|
||||
.http
|
||||
.get(url)
|
||||
.header("Authorization", format!("Bearer {}", self.token))
|
||||
.basic_auth(auth_token, Some("token"))
|
||||
.header("X-Requested-By", "orchai")
|
||||
.send()
|
||||
.await;
|
||||
|
|
@ -88,6 +93,25 @@ impl GraylogClient {
|
|||
Err("graylog request failed after retries".to_string())
|
||||
}
|
||||
|
||||
async fn send_get(&self, url: &str) -> Result<reqwest::Response, String> {
|
||||
let auth_tokens = build_auth_tokens(&self.token);
|
||||
|
||||
for (index, auth_token) in auth_tokens.iter().enumerate() {
|
||||
let response = self.send_get_with_auth(url, auth_token).await?;
|
||||
if response.status() == reqwest::StatusCode::UNAUTHORIZED
|
||||
&& index + 1 < auth_tokens.len()
|
||||
{
|
||||
eprintln!(
|
||||
"[graylog] auth token rejected (401), retrying with base64-decoded token"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
Err("graylog request failed after auth fallback".to_string())
|
||||
}
|
||||
|
||||
pub async fn test_connection(&self) -> Result<(), String> {
|
||||
let url = format!("{}/api/system", self.base_url);
|
||||
let resp = self.send_get(&url).await?;
|
||||
|
|
@ -95,7 +119,10 @@ impl GraylogClient {
|
|||
if resp.status().is_success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("graylog connection test failed: HTTP {}", resp.status()))
|
||||
Err(format!(
|
||||
"graylog connection test failed: HTTP {}",
|
||||
resp.status()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -132,6 +159,33 @@ impl GraylogClient {
|
|||
}
|
||||
}
|
||||
|
||||
fn decode_token_candidate(token: &str) -> Option<String> {
|
||||
if token.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let decoded_bytes = STANDARD.decode(token).ok()?;
|
||||
let decoded = String::from_utf8(decoded_bytes).ok()?;
|
||||
let decoded = decoded.trim();
|
||||
if decoded.is_empty() {
|
||||
return None;
|
||||
}
|
||||
if !decoded.chars().all(|c| c.is_ascii_alphanumeric()) {
|
||||
return None;
|
||||
}
|
||||
Some(decoded.to_string())
|
||||
}
|
||||
|
||||
fn build_auth_tokens(token: &str) -> Vec<String> {
|
||||
let token = token.trim().to_string();
|
||||
let mut candidates = vec![token.clone()];
|
||||
if let Some(decoded) = decode_token_candidate(&token) {
|
||||
if decoded != token {
|
||||
candidates.push(decoded);
|
||||
}
|
||||
}
|
||||
candidates
|
||||
}
|
||||
|
||||
fn level_to_string(value: &Value) -> String {
|
||||
match value {
|
||||
Value::String(s) => s.to_string(),
|
||||
|
|
@ -255,4 +309,23 @@ mod tests {
|
|||
let events = parse_search_response(&payload);
|
||||
assert!(events.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_decodes_base64_token_for_stackhero_format() {
|
||||
let encoded = "OTk2c2l2aGY1Z243Zm9xNzh2ajVrbmlkZWQ4bW9idHZrZ3Nhb2lvNDRrbTY3MnZkaWc2";
|
||||
let candidates = build_auth_tokens(encoded);
|
||||
assert_eq!(candidates.len(), 2);
|
||||
assert_eq!(candidates[0], encoded);
|
||||
assert_eq!(
|
||||
candidates[1],
|
||||
"996sivhf5gn7foq78vj5knided8mobtvkgsaoio44km672vdig6"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_auth_tokens_keeps_raw_when_not_base64() {
|
||||
let raw = "996sivhf5gn7foq78vj5knided8mobtvkgsaoio44km672vdig6";
|
||||
let candidates = build_auth_tokens(raw);
|
||||
assert_eq!(candidates, vec![raw.to_string()]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue