{
+ /// Obtain the value for the given trust `level`.
+ pub fn by_level(&self, level: Trust) -> &T {
+ match level {
+ Trust::Full => &self.full,
+ Trust::Reduced => &self.reduced,
+ }
+ }
+
+ /// Obtain the value for the given `level` once.
+ pub fn into_value_by_level(self, level: Trust) -> T {
+ match level {
+ Trust::Full => self.full,
+ Trust::Reduced => self.reduced,
+ }
+ }
+ }
+}
+
+///
+pub mod permission {
+ use crate::Access;
+
+ /// A marker trait to signal tags for permissions.
+ pub trait Tag {}
+
+ /// A tag indicating that a permission is applying to the contents of a configuration file.
+ pub struct Config;
+ impl Tag for Config {}
+
+ /// A tag indicating that a permission is applying to the resource itself.
+ pub struct Resource;
+ impl Tag for Resource {}
+
+ impl Access {
+ /// Create a permission for values contained in git configuration files.
+ ///
+ /// This applies permissions to values contained inside of these files.
+ pub fn config(permission: P) -> Self {
+ Access {
+ permission,
+ _data: Default::default(),
+ }
+ }
+ }
+
+ impl Access {
+ /// Create a permission a file or directory itself.
+ ///
+ /// This applies permissions to a configuration file itself and whether it can be used at all, or to a directory
+ /// to read from or write to.
+ pub fn resource(permission: P) -> Self {
+ Access {
+ permission,
+ _data: Default::default(),
+ }
+ }
+ }
+}
+
+/// Allow, deny or forbid using a resource or performing an action.
+#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash)]
+#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
+pub enum Permission {
+ /// Fail outright when trying to load a resource or performing an action.
+ Forbid,
+ /// Ignore resources or try to avoid performing an operation.
+ Deny,
+ /// Allow loading a reasource or performing an action.
+ Allow,
+}
+
+bitflags::bitflags! {
+ /// Whether something can be read or written.
+ #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
+ pub struct ReadWrite: u8 {
+ /// The item can be read.
+ const READ = 1 << 0;
+ /// The item can be written
+ const WRITE = 1 << 1;
+ }
+}
+
+/// A container to define tagged access permissions, rendering the permission read-only.
+pub struct Access {
+ /// The access permission itself.
+ permission: P,
+ _data: PhantomData,
+}
+
+impl Deref for Access {
+ type Target = P;
+
+ fn deref(&self) -> &Self::Target {
+ &self.permission
+ }
+}
+
+/// Various types to identify entities.
+pub mod identity;
diff --git a/git-sec/tests/identity/mod.rs b/git-sec/tests/identity/mod.rs
new file mode 100644
index 00000000000..99fc3f71427
--- /dev/null
+++ b/git-sec/tests/identity/mod.rs
@@ -0,0 +1,9 @@
+#[test]
+fn is_path_owned_by_current_user() -> crate::Result {
+ let dir = tempfile::tempdir()?;
+ let file = dir.path().join("file");
+ std::fs::write(&file, &[])?;
+ assert!(git_sec::identity::is_path_owned_by_current_user(file)?);
+ assert!(git_sec::identity::is_path_owned_by_current_user(dir.path())?);
+ Ok(())
+}
diff --git a/git-sec/tests/sec.rs b/git-sec/tests/sec.rs
new file mode 100644
index 00000000000..245e02b5e4a
--- /dev/null
+++ b/git-sec/tests/sec.rs
@@ -0,0 +1,12 @@
+pub type Result = std::result::Result>;
+
+mod trust {
+ use git_sec::Trust;
+
+ #[test]
+ fn ordering() {
+ assert!(Trust::Reduced < Trust::Full);
+ }
+}
+
+mod identity;
diff --git a/git-transport/Cargo.toml b/git-transport/Cargo.toml
index 401f92e06b5..be6ba876802 100644
--- a/git-transport/Cargo.toml
+++ b/git-transport/Cargo.toml
@@ -52,6 +52,7 @@ required-features = ["async-client"]
[dependencies]
git-features = { version = "^0.20.0", path = "../git-features" }
git-url = { version = "^0.4.0", path = "../git-url" }
+git-sec = { version = "^0.1.0", path = "../git-sec" }
git-packetline = { version = "^0.12.4", path = "../git-packetline" }
serde = { version = "1.0.114", optional = true, default-features = false, features = ["std", "derive"]}
diff --git a/git-transport/src/client/blocking_io/http/mod.rs b/git-transport/src/client/blocking_io/http/mod.rs
index 7f30032258a..1c8d7053bc9 100644
--- a/git-transport/src/client/blocking_io/http/mod.rs
+++ b/git-transport/src/client/blocking_io/http/mod.rs
@@ -32,7 +32,7 @@ pub struct Transport {
http: H,
service: Option,
line_provider: Option>,
- identity: Option,
+ identity: Option,
}
impl Transport {
@@ -71,21 +71,17 @@ impl Transport {
#[allow(clippy::unnecessary_wraps, unknown_lints)]
fn add_basic_auth_if_present(&self, headers: &mut Vec>) -> Result<(), client::Error> {
- if let Some(identity) = &self.identity {
- match identity {
- client::Identity::Account { username, password } => {
- #[cfg(not(debug_assertions))]
- if self.url.starts_with("http://") {
- return Err(client::Error::AuthenticationRefused(
- "Will not send credentials in clear text over http",
- ));
- }
- headers.push(Cow::Owned(format!(
- "Authorization: Basic {}",
- base64::encode(format!("{}:{}", username, password))
- )))
- }
+ if let Some(git_sec::identity::Account { username, password }) = &self.identity {
+ #[cfg(not(debug_assertions))]
+ if self.url.starts_with("http://") {
+ return Err(client::Error::AuthenticationRefused(
+ "Will not send credentials in clear text over http",
+ ));
}
+ headers.push(Cow::Owned(format!(
+ "Authorization: Basic {}",
+ base64::encode(format!("{}:{}", username, password))
+ )))
}
Ok(())
}
@@ -100,7 +96,7 @@ fn append_url(base: &str, suffix: &str) -> String {
}
impl client::TransportWithoutIO for Transport {
- fn set_identity(&mut self, identity: client::Identity) -> Result<(), client::Error> {
+ fn set_identity(&mut self, identity: git_sec::identity::Account) -> Result<(), client::Error> {
self.identity = Some(identity);
Ok(())
}
diff --git a/git-transport/src/client/mod.rs b/git-transport/src/client/mod.rs
index 7cf1ec4576a..1df469d8656 100644
--- a/git-transport/src/client/mod.rs
+++ b/git-transport/src/client/mod.rs
@@ -26,7 +26,9 @@ pub mod capabilities;
pub use capabilities::Capabilities;
mod non_io_types;
-pub use non_io_types::{Error, Identity, MessageKind, WriteMode};
+pub use non_io_types::{Error, MessageKind, WriteMode};
+
+pub use git_sec::identity::Account;
///
#[cfg(any(feature = "blocking-client", feature = "async-client"))]
diff --git a/git-transport/src/client/non_io_types.rs b/git-transport/src/client/non_io_types.rs
index 7fcbf4078de..6ffabe43500 100644
--- a/git-transport/src/client/non_io_types.rs
+++ b/git-transport/src/client/non_io_types.rs
@@ -29,19 +29,6 @@ pub enum MessageKind {
Text(&'static [u8]),
}
-#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
-#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
-/// An identity for use when authenticating the transport layer.
-pub enum Identity {
- /// An account based identity
- Account {
- /// The user's name
- username: String,
- /// The user's password
- password: String,
- },
-}
-
pub(crate) mod connect {
use quick_error::quick_error;
quick_error! {
diff --git a/git-transport/src/client/traits.rs b/git-transport/src/client/traits.rs
index e7a5ab40cc0..f57b2fe2d09 100644
--- a/git-transport/src/client/traits.rs
+++ b/git-transport/src/client/traits.rs
@@ -2,10 +2,7 @@ use std::ops::{Deref, DerefMut};
#[cfg(any(feature = "blocking-client", feature = "async-client"))]
use crate::client::{MessageKind, RequestWriter, WriteMode};
-use crate::{
- client::{Error, Identity},
- Protocol,
-};
+use crate::{client::Error, Protocol};
/// This trait represents all transport related functions that don't require any input/output to be done which helps
/// implementation to share more code across blocking and async programs.
@@ -16,7 +13,7 @@ pub trait TransportWithoutIO {
/// of the identity in order to mark it as invalid. Otherwise the user might have difficulty updating obsolete
/// credentials.
/// Please note that most transport layers are unauthenticated and thus return [an error][Error::AuthenticationUnsupported] here.
- fn set_identity(&mut self, _identity: Identity) -> Result<(), Error> {
+ fn set_identity(&mut self, _identity: git_sec::identity::Account) -> Result<(), Error> {
Err(Error::AuthenticationUnsupported)
}
/// Get a writer for sending data and obtaining the response. It can be configured in various ways
@@ -53,7 +50,7 @@ pub trait TransportWithoutIO {
// Would be nice if the box implementation could auto-forward to all implemented traits.
impl TransportWithoutIO for Box {
- fn set_identity(&mut self, identity: Identity) -> Result<(), Error> {
+ fn set_identity(&mut self, identity: git_sec::identity::Account) -> Result<(), Error> {
self.deref_mut().set_identity(identity)
}
@@ -76,7 +73,7 @@ impl TransportWithoutIO for Box {
}
impl TransportWithoutIO for &mut T {
- fn set_identity(&mut self, identity: Identity) -> Result<(), Error> {
+ fn set_identity(&mut self, identity: git_sec::identity::Account) -> Result<(), Error> {
self.deref_mut().set_identity(identity)
}
diff --git a/git-transport/tests/client/blocking_io/http/mod.rs b/git-transport/tests/client/blocking_io/http/mod.rs
index b6ad121cd01..1659ad51fa8 100644
--- a/git-transport/tests/client/blocking_io/http/mod.rs
+++ b/git-transport/tests/client/blocking_io/http/mod.rs
@@ -9,7 +9,7 @@ use std::{
use bstr::ByteSlice;
use git_transport::{
- client::{self, http, Identity, SetServiceResponse, Transport, TransportV2Ext, TransportWithoutIO},
+ client::{self, http, SetServiceResponse, Transport, TransportV2Ext, TransportWithoutIO},
Protocol, Service,
};
@@ -42,7 +42,7 @@ fn assert_error_status(
fn http_authentication_error_can_be_differentiated_and_identity_is_transmitted() -> crate::Result {
let (server, mut client) = assert_error_status(401, std::io::ErrorKind::PermissionDenied)?;
server.next_read_and_respond_with(fixture_bytes("v1/http-handshake.response"));
- client.set_identity(Identity::Account {
+ client.set_identity(git_sec::identity::Account {
username: "user".into(),
password: "password".into(),
})?;