diff --git a/giterated-daemon/src/backend/git.rs b/giterated-daemon/src/backend/git.rs index e7ba90b..6dc17cb 100644 --- a/giterated-daemon/src/backend/git.rs +++ b/giterated-daemon/src/backend/git.rs @@ -6,12 +6,12 @@ use giterated_models::instance::{Instance, RepositoryCreateRequest}; use giterated_models::repository::{ AccessList, Commit, DefaultBranch, Description, IssueLabel, LatestCommit, Repository, - RepositoryCommitBeforeRequest, RepositoryDiff, RepositoryDiffFile, RepositoryDiffFileChunk, - RepositoryDiffFileInfo, RepositoryDiffFileStatus, RepositoryDiffPatchRequest, - RepositoryDiffRequest, RepositoryFile, RepositoryFileFromIdRequest, - RepositoryFileInspectRequest, RepositoryIssue, RepositoryIssueLabelsRequest, - RepositoryIssuesCountRequest, RepositoryIssuesRequest, RepositoryObjectType, - RepositoryTreeEntry, RepositoryVisibility, Visibility, RepositoryChunkLine, RepositoryFileFromPathRequest, + RepositoryChunkLine, RepositoryCommitBeforeRequest, RepositoryDiff, RepositoryDiffFile, + RepositoryDiffFileChunk, RepositoryDiffFileInfo, RepositoryDiffFileStatus, + RepositoryDiffPatchRequest, RepositoryDiffRequest, RepositoryFile, RepositoryFileFromIdRequest, + RepositoryFileFromPathRequest, RepositoryFileInspectRequest, RepositoryIssue, + RepositoryIssueLabelsRequest, RepositoryIssuesCountRequest, RepositoryIssuesRequest, + RepositoryObjectType, RepositoryTreeEntry, RepositoryVisibility, Visibility, }; use giterated_models::settings::{AnySetting, Setting}; use giterated_models::user::{User, UserParseError}; @@ -50,7 +50,10 @@ impl GitRepository { user: Option<&User>, settings: &Arc>, ) -> bool { - info!("Can user {:?} view repository {}/{}?", user, self.owner_user, self.name); + info!( + "Can user {:?} view repository {}/{}?", + user, self.owner_user, self.name + ); if matches!(self.visibility, RepositoryVisibility::Public) { return true; } @@ -240,7 +243,10 @@ impl GitBackend { name: &str, requester: Option<&User>, ) -> Result { - info!("Checking permissions for user {:?} on {}/{}", requester, owner, name); + info!( + "Checking permissions for user {:?} on {}/{}", + requester, owner, name + ); let repository = match self .find_by_owner_user_name( @@ -661,7 +667,7 @@ impl RepositoryBackend for GitBackend { return Err(Box::new(GitBackendError::RefNotFound( request.rev.clone().unwrap(), )) - .into()) + .into()); } } Some(rev_name) => { @@ -717,7 +723,9 @@ impl RepositoryBackend for GitBackend { binary: blob.is_binary(), size: blob.size(), }, - Err(_) => return Err(Box::new(GitBackendError::BlobNotFound(entry.id().to_string())).into()), + Err(_) => { + return Err(Box::new(GitBackendError::BlobNotFound(entry.id().to_string())).into()) + } }; Ok(file) @@ -813,7 +821,7 @@ impl RepositoryBackend for GitBackend { change_type: line.origin_value().into(), content: line_utf8, old_line_num: line.old_lineno(), - new_line_num: line.new_lineno() + new_line_num: line.new_lineno(), }); } diff --git a/giterated-daemon/src/backend/mod.rs b/giterated-daemon/src/backend/mod.rs index 0d983d4..4236838 100644 --- a/giterated-daemon/src/backend/mod.rs +++ b/giterated-daemon/src/backend/mod.rs @@ -18,8 +18,9 @@ use giterated_models::instance::{ use giterated_models::repository::{ Commit, IssueLabel, Repository, RepositoryCommitBeforeRequest, RepositoryDiff, RepositoryDiffPatchRequest, RepositoryDiffRequest, RepositoryFile, RepositoryFileFromIdRequest, - RepositoryFileInspectRequest, RepositoryIssue, RepositoryIssueLabelsRequest, - RepositoryIssuesCountRequest, RepositoryIssuesRequest, RepositorySummary, RepositoryTreeEntry, RepositoryFileFromPathRequest, + RepositoryFileFromPathRequest, RepositoryFileInspectRequest, RepositoryIssue, + RepositoryIssueLabelsRequest, RepositoryIssuesCountRequest, RepositoryIssuesRequest, + RepositorySummary, RepositoryTreeEntry, }; use giterated_models::settings::AnySetting; use giterated_models::user::User; diff --git a/giterated-daemon/src/connection/wrapper.rs b/giterated-daemon/src/connection/wrapper.rs index f444374..40c34f7 100644 --- a/giterated-daemon/src/connection/wrapper.rs +++ b/giterated-daemon/src/connection/wrapper.rs @@ -1,12 +1,17 @@ use std::{ net::SocketAddr, + ops::Deref, sync::{atomic::AtomicBool, Arc}, }; use anyhow::Error; use futures_util::{SinkExt, StreamExt}; -use giterated_models::{error::OperationError, instance::Instance}; +use giterated_models::{ + authenticated::{AuthenticationSource, UserTokenMetadata}, + error::OperationError, + instance::Instance, +}; use giterated_models::object_backend::ObjectBackend; @@ -14,7 +19,17 @@ use giterated_models::{ authenticated::AuthenticatedPayload, message::GiteratedMessage, object::AnyObject, operation::AnyOperation, }; -use giterated_stack::{handler::GiteratedBackend, StackOperationState}; +use giterated_stack::{ + handler::GiteratedBackend, AuthenticatedInstance, AuthenticatedUser, StackOperationState, +}; +use jsonwebtoken::{DecodingKey, TokenData, Validation}; +use rsa::{ + pkcs1::DecodeRsaPublicKey, + pss::{Signature, VerifyingKey}, + sha2::Sha256, + signature::Verifier, + RsaPublicKey, +}; use serde::Serialize; use tokio::{net::TcpStream, sync::Mutex}; @@ -43,7 +58,7 @@ pub async fn connection_wrapper( instance_connections: Arc>, config: Table, backend: GiteratedBackend, - operation_state: StackOperationState, + mut operation_state: StackOperationState, ) { let connection_state = ConnectionState { socket: Arc::new(Mutex::new(socket)), @@ -61,6 +76,7 @@ pub async fn connection_wrapper( }; let _handshaked = false; + let mut key_cache = PublicKeyCache::default(); loop { let mut socket = connection_state.socket.lock().await; @@ -83,8 +99,76 @@ pub async fn connection_wrapper( let message: AuthenticatedPayload = bincode::deserialize(&payload).unwrap(); + // Get authentication + let instance = { + let mut verified_instance: Option = None; + for source in &message.source { + if let AuthenticationSource::Instance { + instance, + signature, + } = source + { + let public_key = key_cache.get(&instance).await.unwrap(); + let public_key = RsaPublicKey::from_pkcs1_pem(&public_key).unwrap(); + let verifying_key = VerifyingKey::::new(public_key); + + if verifying_key + .verify( + &message.payload, + &Signature::try_from(signature.as_ref()).unwrap(), + ) + .is_ok() + { + verified_instance = + Some(AuthenticatedInstance::new(instance.clone())); + + break; + } + } + } + + verified_instance + }; + + let user = { + let mut verified_user = None; + if let Some(verified_instance) = &instance { + for source in &message.source { + if let AuthenticationSource::User { user, token } = source { + // Get token + let public_key = key_cache.get(&verified_instance).await.unwrap(); + + let token: TokenData = jsonwebtoken::decode( + token.as_ref(), + &DecodingKey::from_rsa_pem(public_key.as_bytes()).unwrap(), + &Validation::new(jsonwebtoken::Algorithm::RS256), + ) + .unwrap(); + + if token.claims.generated_for != *verified_instance.deref() { + // Nope! + break; + } + + if token.claims.user != *user { + // Nope! + break; + } + + verified_user = Some(AuthenticatedUser::new(user.clone())); + break; + } + } + } + + verified_user + }; + let message: GiteratedMessage = message.into_message(); + operation_state.user = user; + operation_state.instance = instance; + let result = backend .object_operation( message.object, @@ -94,6 +178,10 @@ pub async fn connection_wrapper( ) .await; + // Asking for exploits here + operation_state.user = None; + operation_state.instance = None; + // Map result to Vec on both let result = match result { Ok(result) => Ok(serde_json::to_vec(&result).unwrap()), diff --git a/giterated-daemon/src/database_backend/handler.rs b/giterated-daemon/src/database_backend/handler.rs index 041131e..a337f32 100644 --- a/giterated-daemon/src/database_backend/handler.rs +++ b/giterated-daemon/src/database_backend/handler.rs @@ -12,8 +12,8 @@ use giterated_models::{ Commit, DefaultBranch, Description, LatestCommit, Repository, RepositoryCommitBeforeRequest, RepositoryDiff, RepositoryDiffPatchRequest, RepositoryDiffRequest, RepositoryFile, RepositoryFileFromIdRequest, - RepositoryFileInspectRequest, RepositoryInfoRequest, RepositorySummary, RepositoryView, - Visibility, RepositoryFileFromPathRequest, + RepositoryFileFromPathRequest, RepositoryFileInspectRequest, RepositoryInfoRequest, + RepositorySummary, RepositoryView, Visibility, }, settings::{AnySetting, GetSetting, GetSettingError, SetSetting, SetSettingError}, user::{User, UserRepositoriesRequest}, diff --git a/giterated-daemon/src/database_backend/mod.rs b/giterated-daemon/src/database_backend/mod.rs index a54d3fa..c2cea57 100644 --- a/giterated-daemon/src/database_backend/mod.rs +++ b/giterated-daemon/src/database_backend/mod.rs @@ -19,9 +19,9 @@ use crate::backend::{RepositoryBackend, UserBackend}; use self::handler::{ instance_authentication_request, instance_create_repository_request, instance_registration_request, repository_commit_before, repository_diff, - repository_diff_patch, repository_file_from_id, repository_get_setting, repository_get_value, - repository_info, repository_set_setting, user_get_repositories, user_get_setting, - user_get_value, user_set_setting, repository_file_from_path, + repository_diff_patch, repository_file_from_id, repository_file_from_path, + repository_get_setting, repository_get_value, repository_info, repository_set_setting, + user_get_repositories, user_get_setting, user_get_value, user_set_setting, }; #[derive(Clone, Debug)] @@ -124,8 +124,8 @@ mod test { use giterated_models::repository::{ Commit, Description, Repository, RepositoryCommitBeforeRequest, RepositoryDiff, RepositoryDiffPatchRequest, RepositoryDiffRequest, RepositoryFile, - RepositoryFileFromIdRequest, RepositoryFileInspectRequest, RepositorySummary, - RepositoryTreeEntry, RepositoryFileFromPathRequest, + RepositoryFileFromIdRequest, RepositoryFileFromPathRequest, RepositoryFileInspectRequest, + RepositorySummary, RepositoryTreeEntry, }; use giterated_models::settings::AnySetting; use giterated_models::user::{DisplayName, User}; diff --git a/giterated-daemon/src/main.rs b/giterated-daemon/src/main.rs index 4298a0b..98e705a 100644 --- a/giterated-daemon/src/main.rs +++ b/giterated-daemon/src/main.rs @@ -96,6 +96,8 @@ async fn main() -> Result<(), Error> { let operation_state = { StackOperationState { giterated_backend: backend_wrapper, + instance: None, + user: None, } }; diff --git a/giterated-models/src/authenticated.rs b/giterated-models/src/authenticated.rs index 3a63e17..94e8a2f 100644 --- a/giterated-models/src/authenticated.rs +++ b/giterated-models/src/authenticated.rs @@ -1,4 +1,4 @@ -use std::fmt::Debug; +use std::{fmt::Debug, sync::Arc}; use rsa::{ pkcs1::DecodeRsaPrivateKey, @@ -27,7 +27,7 @@ pub struct UserTokenMetadata { #[derive(Debug)] pub struct Authenticated> { - pub source: Vec>, + pub source: Vec>, pub message: GiteratedMessage, } @@ -87,12 +87,11 @@ impl> Authenticated { } } - pub fn append_authentication( + pub fn append_authentication( &mut self, - authentication: P, + authentication: Arc, ) { - self.source - .push(Box::new(authentication) as Box); + self.source.push(authentication); } pub fn into_payload(mut self) -> AuthenticatedPayload { @@ -104,7 +103,8 @@ impl> Authenticated { source: self .source .drain(..) - .map(|provider| provider.as_ref().authenticate(&payload)) + .map(|provider| provider.as_ref().authenticate_all(&payload)) + .flatten() .collect::>(), payload, } diff --git a/giterated-models/src/repository/operations.rs b/giterated-models/src/repository/operations.rs index 3860477..28bb0d5 100644 --- a/giterated-models/src/repository/operations.rs +++ b/giterated-models/src/repository/operations.rs @@ -231,10 +231,7 @@ impl + std::fmt::Debug> Object<'_, S operation_state: &S, ) -> Result> { self.request::( - RepositoryFileFromPathRequest { - rev, - path, - }, + RepositoryFileFromPathRequest { rev, path }, operation_state, ) .await diff --git a/giterated-stack/src/lib.rs b/giterated-stack/src/lib.rs index 7438caf..4cd833e 100644 --- a/giterated-stack/src/lib.rs +++ b/giterated-stack/src/lib.rs @@ -1,7 +1,7 @@ pub mod handler; pub mod state; -use std::{collections::HashMap, future::Future, pin::Pin, str::FromStr, sync::Arc}; +use std::{collections::HashMap, future::Future, ops::Deref, pin::Pin, str::FromStr, sync::Arc}; use futures_util::FutureExt; use giterated_models::{ @@ -394,6 +394,42 @@ impl FromOperationState for StackOperationState { #[derive(Clone)] pub struct StackOperationState { pub giterated_backend: BackendWrapper, + pub instance: Option, + pub user: Option, +} + +#[derive(Clone, Debug)] +pub struct AuthenticatedInstance(Instance); + +impl AuthenticatedInstance { + pub fn new(instance: Instance) -> Self { + AuthenticatedInstance(instance) + } +} + +impl Deref for AuthenticatedInstance { + type Target = Instance; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Clone, Debug)] +pub struct AuthenticatedUser(User); + +impl AuthenticatedUser { + pub fn new(user: User) -> Self { + AuthenticatedUser(user) + } +} + +impl Deref for AuthenticatedUser { + type Target = User; + + fn deref(&self) -> &Self::Target { + &self.0 + } } #[derive(Clone)]