gitdataai/lib/git/http/auth.rs
2026-05-30 01:38:40 +08:00

95 lines
2.7 KiB
Rust

use actix_web::{Error, HttpRequest};
use argon2::{
Argon2,
password_hash::{PasswordHash, PasswordVerifier},
};
use db::database::AppDatabase;
use model::{
repos::RepoModel,
users::{user::UserModel, user_token::UserTokenModel},
};
use crate::{
http::utils::extract_basic_credentials, ssh::authz::SshAuthService,
};
pub async fn verify_access_token(
db: &AppDatabase,
username: &str,
access_key: &str,
) -> Result<UserModel, Error> {
let user = sqlx::query_as::<_, UserModel>(
"SELECT id, username, display_name, avatar_url, website_url, allow_use, can_search, \
last_sign_in_at, created_at, updated_at \
FROM \"user\" \
WHERE username = $1",
)
.bind(username)
.fetch_optional(db.reader())
.await
.map_err(|_| actix_web::error::ErrorUnauthorized("Invalid username or access key"))?
.ok_or_else(|| actix_web::error::ErrorUnauthorized("Invalid username or access key"))?;
let tokens: Vec<UserTokenModel> = sqlx::query_as::<_, UserTokenModel>(
"SELECT id, \"user\", name, token_hash, scopes, expires_at, is_revoked, created_at, updated_at \
FROM user_token \
WHERE \"user\" = $1 AND is_revoked = false",
)
.bind(user.id)
.fetch_all(db.reader())
.await
.map_err(|_| actix_web::error::ErrorUnauthorized("Invalid username or access key"))?
.into_iter()
.filter(|token| {
token
.expires_at
.map(|expires_at| expires_at >= chrono::Utc::now())
.unwrap_or(true)
})
.collect();
for token in tokens {
let Ok(hash) = PasswordHash::new(&token.token_hash) else {
tracing::warn!(
token_id = token.id,
"invalid stored access key hash"
);
continue;
};
if Argon2::default()
.verify_password(access_key.as_bytes(), &hash)
.is_ok()
{
return Ok(user);
}
}
Err(actix_web::error::ErrorUnauthorized(
"Invalid username or access key",
))
}
pub async fn authorize_repo_access(
req: &HttpRequest,
db: &AppDatabase,
repo: &RepoModel,
is_write: bool,
) -> Result<(), Error> {
if !is_write && repo.visibility == "public" {
return Ok(());
}
let (username, access_key) = extract_basic_credentials(req)?;
let user = verify_access_token(db, &username, &access_key).await?;
let authz = SshAuthService::new(db.clone());
let can_access = authz.check_repo_permission(&user, repo, is_write).await;
if !can_access {
return Err(actix_web::error::ErrorForbidden(
"No permission for repository",
));
}
Ok(())
}