fix: explicit SQLx type + debug tracing for HTTP git auth
Fixes potential silent failure where sqlx::query_scalar couldn't infer the return type at runtime. Also adds step-by-step tracing so the exact failure point (no header / bad base64 / key not found / db error) is visible in `docker compose logs server`. https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
This commit is contained in:
parent
a2627a3e2f
commit
4504c22af8
2 changed files with 62 additions and 14 deletions
|
|
@ -92,11 +92,19 @@ pub async fn revoke(
|
|||
/// Returns the user_id if the raw key matches a stored hash.
|
||||
pub async fn verify_key(s: &AppState, raw_key: &str) -> Option<String> {
|
||||
let hash = hex::encode(Sha256::digest(raw_key.as_bytes()));
|
||||
sqlx::query_scalar("SELECT user_id FROM api_keys WHERE key_hash = ?")
|
||||
match sqlx::query_scalar::<_, String>(
|
||||
"SELECT user_id FROM api_keys WHERE key_hash = ?",
|
||||
)
|
||||
.bind(&hash)
|
||||
.fetch_optional(&s.db)
|
||||
.await
|
||||
.unwrap_or(None)
|
||||
{
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
tracing::error!("api_key verify db error: {e}");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Tiny CSPRNG using uuid entropy ───────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -35,15 +35,55 @@ fn check_token(state: &AppState, headers: &HeaderMap) -> bool {
|
|||
/// git sends Basic Auth where the password field is the API key.
|
||||
/// Returns the user_id on success.
|
||||
async fn http_authenticate(s: &AppState, headers: &HeaderMap) -> Option<String> {
|
||||
let value = headers.get("authorization")?.to_str().ok()?;
|
||||
let encoded = value.strip_prefix("Basic ")?;
|
||||
let decoded = base64::engine::general_purpose::STANDARD
|
||||
.decode(encoded)
|
||||
.ok()?;
|
||||
let credentials = std::str::from_utf8(&decoded).ok()?;
|
||||
// credentials is "username:password" — the password IS the API key.
|
||||
let api_key = credentials.splitn(2, ':').nth(1)?;
|
||||
api_keys::verify_key(s, api_key).await
|
||||
let value = headers
|
||||
.get("authorization")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.unwrap_or("");
|
||||
|
||||
if value.is_empty() {
|
||||
tracing::debug!("git http auth: no Authorization header");
|
||||
return None;
|
||||
}
|
||||
|
||||
let encoded = match value.strip_prefix("Basic ") {
|
||||
Some(e) => e,
|
||||
None => {
|
||||
tracing::debug!("git http auth: not Basic (got: {})", &value[..value.len().min(20)]);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let decoded = match base64::engine::general_purpose::STANDARD.decode(encoded) {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
tracing::debug!("git http auth: base64 error: {e}");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let credentials = match std::str::from_utf8(&decoded) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
tracing::debug!("git http auth: utf8 error: {e}");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// credentials = "username:password" — the password IS the API key.
|
||||
let api_key = match credentials.splitn(2, ':').nth(1) {
|
||||
Some(k) => k,
|
||||
None => {
|
||||
tracing::debug!("git http auth: no colon in credentials");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let result = api_keys::verify_key(s, api_key).await;
|
||||
tracing::debug!(
|
||||
"git http auth: key lookup → {}",
|
||||
if result.is_some() { "ok" } else { "not found" }
|
||||
);
|
||||
result
|
||||
}
|
||||
|
||||
fn unauthorized() -> Response<Body> {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue