- Migrate access key auth from custom hash to Argon2 password verification - Check all un-revoked tokens with expiry validation - Add branch protection checks to HTTP push handlers
79 lines
2.4 KiB
Rust
79 lines
2.4 KiB
Rust
use crate::http::utils::extract_basic_credentials;
|
|
use crate::ssh::authz::SshAuthService;
|
|
use actix_web::{Error, HttpRequest};
|
|
use argon2::Argon2;
|
|
use argon2::password_hash::{PasswordHash, PasswordVerifier};
|
|
use db::database::AppDatabase;
|
|
use models::repos::repo;
|
|
use models::users::{user, user_token};
|
|
use sea_orm::sqlx::types::chrono;
|
|
use sea_orm::*;
|
|
|
|
pub async fn verify_access_token(
|
|
db: &AppDatabase,
|
|
username: &str,
|
|
access_key: &str,
|
|
) -> Result<user::Model, Error> {
|
|
let user = user::Entity::find()
|
|
.filter(user::Column::Username.eq(username))
|
|
.one(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 = user_token::Entity::find()
|
|
.filter(user_token::Column::User.eq(user.uid))
|
|
.filter(user_token::Column::IsRevoked.eq(false))
|
|
.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)
|
|
});
|
|
|
|
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: &repo::Model,
|
|
is_write: bool,
|
|
) -> Result<(), Error> {
|
|
if !is_write && !repo.is_private {
|
|
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(())
|
|
}
|