use uuid::Uuid; use crate::context::facade::GitContext; use crate::service::GitService; use crate::share::errors::GitError; /// Per-repo authorization — the central ACL that was previously only on /// the SSH path (`ssh/handler/command.rs::authorize_repo`) and missing /// from every HTTP/LFS path. Centralized so HTTP, SSH, and the LFS router /// enforce identical membership rules. /// /// Rules: /// - space owner of the repo's owning space: always allowed /// - read on public repo: allowed /// - read on private repo: requires `repo_collaborations` row /// - write: requires `repo_collaborations.mode ∈ {Write, Admin}` /// - admin: requires `repo_collaborations.mode == Admin` pub async fn authorize_repo_access( svc: &GitService, uid: i64, ctx: &GitContext, want_write: bool, require_admin: bool, ) -> Result<(), GitError> { let owner = find_space_for_repo(svc, &ctx.model).await?; let capability = if require_admin { space::Capability::RepositoryAdmin { repository: ctx.model.id, } } else if want_write { space::Capability::RepositoryWrite { repository: ctx.model.id, } } else { space::Capability::RepositoryRead { repository: ctx.model.id, private: ctx.model.is_private, } }; Ok(space::require(&owner, uid, capability).await?) } async fn find_space_for_repo( service: &GitService, repo: &models::repo::repos::Model, ) -> Result { space_for_dir(&service.access, repo.dir_id) .await? .ok_or_else(|| GitError::Internal(format!("space for repo {} not found", repo.id))) } pub async fn space_for_dir( access: &space::Access, dir_id: Uuid, ) -> Result, GitError> { Ok(access.for_dir(dir_id).await?) }