diff --git a/giterated-daemon/src/backend/git.rs b/giterated-daemon/src/backend/git.rs index 337602f..983c880 100644 --- a/giterated-daemon/src/backend/git.rs +++ b/giterated-daemon/src/backend/git.rs @@ -8,7 +8,7 @@ use giterated_models::repository::{ Commit, DefaultBranch, Description, IssueLabel, LatestCommit, Repository, RepositoryFileInspectRequest, RepositoryIssue, RepositoryIssueLabelsRequest, RepositoryIssuesCountRequest, RepositoryIssuesRequest, RepositoryObjectType, - RepositoryTreeEntry, RepositoryVisibility, Visibility, RepositoryFile, RepositoryFileFromIdRequest, RepositoryDiffRequest, RepositoryDiff, + RepositoryTreeEntry, RepositoryVisibility, Visibility, RepositoryFile, RepositoryFileFromIdRequest, RepositoryDiffRequest, RepositoryDiff, RepositoryCommitBeforeRequest, }; use giterated_models::settings::{AnySetting, Setting}; use giterated_models::user::{User, UserParseError}; @@ -97,6 +97,10 @@ pub enum GitBackendError { BlobNotFound(String), #[error("Tree with ID `{0}` not found")] TreeNotFound(String), + #[error("Commit with ID `{0}` not found")] + CommitNotFound(String), + #[error("Parent for commit with ID `{0}` not found")] + CommitParentNotFound(String), #[error("Failed diffing tree with ID `{0}` to tree with ID `{1}`")] FailedDiffing(String, String), } @@ -174,6 +178,42 @@ impl GitBackend { } } + pub async fn open_repository_and_check_permissions(&self, owner: &User, name: &str, requester: Option<&User>) -> Result { + let repository = match self + .find_by_owner_user_name( + // &request.owner.instance.url, + owner, + name, + ) + .await + { + Ok(repository) => repository, + Err(err) => return Err(err), + }; + + if let Some(requester) = requester { + if !repository.can_user_view_repository(Some(requester)) { + return Err(GitBackendError::RepositoryNotFound { + owner_user: repository.owner_user.to_string(), + name: repository.name.clone(), + }); + } + } else if matches!(repository.visibility, RepositoryVisibility::Private) { + info!("Unauthenticated"); + // Unauthenticated users can never view private repositories + + return Err(GitBackendError::RepositoryNotFound { + owner_user: repository.owner_user.to_string(), + name: repository.name.clone(), + }); + } + + match repository.open_git2_repository(&self.repository_folder) { + Ok(git) => Ok(git), + Err(err) => return Err(err), + } + } + // TODO: Find where this fits // TODO: Cache this and general repository tree and invalidate select files on push // TODO: Find better and faster technique for this @@ -787,6 +827,35 @@ impl RepositoryBackend for GitBackend { patch, }) } + + async fn repository_commit_before( + &mut self, + requester: Option<&User>, + repository: &Repository, + request: &RepositoryCommitBeforeRequest, + ) -> Result { + let git = self.open_repository_and_check_permissions(&repository.owner, &repository.name, requester).await?; + + // Parse the passed object id + let oid = match git2::Oid::from_str(request.0.as_str()) { + Ok(oid) => oid, + Err(_) => return Err(Box::new(GitBackendError::InvalidObjectId(request.0.clone())).into()), + }; + + // Find the commit using the parsed oid + let commit = match git.find_commit(oid) { + Ok(commit) => commit, + Err(_) => return Err(Box::new(GitBackendError::CommitNotFound(oid.to_string())).into()), + }; + + // Get the first parent it has + let parent = match commit.parent(0) { + Ok(parent) => Commit::from(parent), + Err(_) => return Err(Box::new(GitBackendError::CommitParentNotFound(oid.to_string())).into()), + }; + + Ok(parent) + } } impl IssuesBackend for GitBackend { diff --git a/giterated-daemon/src/backend/mod.rs b/giterated-daemon/src/backend/mod.rs index a09ee36..4a98352 100644 --- a/giterated-daemon/src/backend/mod.rs +++ b/giterated-daemon/src/backend/mod.rs @@ -18,7 +18,7 @@ use giterated_models::instance::{ use giterated_models::repository::{ IssueLabel, Repository, RepositoryFileInspectRequest, RepositoryIssue, RepositoryIssueLabelsRequest, RepositoryIssuesCountRequest, RepositoryIssuesRequest, - RepositorySummary, RepositoryTreeEntry, RepositoryFileFromIdRequest, RepositoryFile, RepositoryDiffRequest, RepositoryDiff, + RepositorySummary, RepositoryTreeEntry, RepositoryFileFromIdRequest, RepositoryFile, RepositoryDiffRequest, RepositoryDiff, Commit, RepositoryCommitBeforeRequest, }; use giterated_models::settings::AnySetting; use giterated_models::user::User; @@ -49,6 +49,12 @@ pub trait RepositoryBackend { repository: &Repository, request: &RepositoryDiffRequest, ) -> Result; + async fn repository_commit_before( + &mut self, + requester: Option<&User>, + repository: &Repository, + request: &RepositoryCommitBeforeRequest, + ) -> Result; async fn get_value( &mut self, user: &Repository, diff --git a/giterated-daemon/src/database_backend/handler.rs b/giterated-daemon/src/database_backend/handler.rs index 2b4196d..e56e6df 100644 --- a/giterated-daemon/src/database_backend/handler.rs +++ b/giterated-daemon/src/database_backend/handler.rs @@ -8,7 +8,7 @@ use giterated_models::{ operation::{AnyOperation, GiteratedOperation}, repository::{ DefaultBranch, Description, LatestCommit, Repository, RepositoryFileInspectRequest, - RepositoryInfoRequest, RepositorySummary, RepositoryView, Visibility, RepositoryFile, RepositoryFileFromIdRequest, RepositoryDiff, RepositoryDiffRequest, + RepositoryInfoRequest, RepositorySummary, RepositoryView, Visibility, RepositoryFile, RepositoryFileFromIdRequest, RepositoryDiff, RepositoryDiffRequest, RepositoryCommitBeforeRequest, Commit, }, settings::{AnySetting, GetSetting, GetSettingError, SetSetting, SetSettingError}, user::{User, UserRepositoriesRequest}, @@ -307,6 +307,34 @@ pub fn repository_diff( }.boxed() } +pub fn repository_commit_before( + object: &Repository, + operation: RepositoryCommitBeforeRequest, + state: DatabaseBackend, +) -> BoxFuture<'static, Result>> { + let object = object.clone(); + + async move { + let object = state + .get_object::(&object.to_string()) + .await + .unwrap(); + + let mut repository_backend = state.repository_backend.lock().await; + let file = repository_backend + .repository_commit_before( + None, + object.object(), + &operation, + ) + .await + .map_err(|err| OperationError::Internal(format!("{:?}", err)))?; + drop(repository_backend); + + Ok(file) + }.boxed() +} + pub fn repository_get_value( object: &Repository, operation: GetValue>, diff --git a/giterated-daemon/src/database_backend/mod.rs b/giterated-daemon/src/database_backend/mod.rs index 95147e1..1c2d29c 100644 --- a/giterated-daemon/src/database_backend/mod.rs +++ b/giterated-daemon/src/database_backend/mod.rs @@ -18,7 +18,7 @@ use crate::backend::{RepositoryBackend, UserBackend}; use self::handler::{ repository_get_setting, repository_get_value, repository_info, repository_set_setting, - user_get_repositories, user_get_setting, user_get_value, user_set_setting, OperationHandlers, repository_file_from_id, repository_diff, + user_get_repositories, user_get_setting, user_get_value, user_set_setting, OperationHandlers, repository_file_from_id, repository_diff, repository_commit_before, }; #[derive(Clone, Debug)] @@ -119,6 +119,7 @@ impl ObjectBackend for DatabaseBackend { .insert(repository_info) .insert(repository_file_from_id) .insert(repository_diff) + .insert(repository_commit_before) .insert(repository_get_value) .insert(repository_get_setting) .insert(repository_set_setting); @@ -257,7 +258,7 @@ mod test { use giterated_models::repository::{ Description, Repository, RepositoryFileInspectRequest, RepositorySummary, - RepositoryTreeEntry, RepositoryFileFromIdRequest, RepositoryFile, RepositoryDiff, RepositoryDiffRequest + RepositoryTreeEntry, RepositoryFileFromIdRequest, RepositoryFile, RepositoryDiff, RepositoryDiffRequest, RepositoryCommitBeforeRequest, Commit }; use giterated_models::settings::AnySetting; use giterated_models::user::{DisplayName, User}; @@ -359,6 +360,14 @@ mod test { ) -> Result { todo!() } + async fn repository_commit_before( + &mut self, + requester: Option<&User>, + repository: &Repository, + request: &RepositoryCommitBeforeRequest, + ) -> Result { + todo!() + } async fn get_value( &mut self, _repository: &Repository, diff --git a/giterated-models/src/repository/operations.rs b/giterated-models/src/repository/operations.rs index b947114..17f0907 100644 --- a/giterated-models/src/repository/operations.rs +++ b/giterated-models/src/repository/operations.rs @@ -7,7 +7,7 @@ use crate::{ operation::GiteratedOperation, }; -use super::{IssueLabel, Repository, RepositoryIssue, RepositoryTreeEntry, RepositoryView, RepositoryFile, RepositoryDiff}; +use super::{IssueLabel, Repository, RepositoryIssue, RepositoryTreeEntry, RepositoryView, RepositoryFile, RepositoryDiff, Commit}; /// A request to get a repository's information. /// @@ -67,6 +67,23 @@ impl GiteratedOperation for RepositoryDiffRequest { type Failure = RepositoryError; } +/// A request to get the commit before the one with the passed id +/// +/// # Authentication +/// - Instance Authentication +/// - Validate request against the `issued_for` public key +/// - Validate User token against the user's instance's public key +/// # Authorization +/// - User Authorization +/// - Potential User permissions checks +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RepositoryCommitBeforeRequest(pub String); + +impl GiteratedOperation for RepositoryCommitBeforeRequest { + type Success = Commit; + type Failure = RepositoryError; +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct RepositoryIssuesCountRequest; @@ -163,6 +180,13 @@ impl Object<'_, Repository, B> { self.request::(RepositoryDiffRequest { old_id, new_id }).await } + pub async fn commit_before( + &mut self, + id: String, + ) -> Result> { + self.request::(RepositoryCommitBeforeRequest(id)).await + } + // pub async fn issues_count(&mut self) -> Result> { // self.request::(RepositoryIssuesCountRequest) // .await