Implement SSH identity authentication
This commit is contained in:
1 parent
f636f3b551
commit
5dbda4bbd2
14 files changed
+1964
-60
No files matched your search
+171
-1
@@ -24,7 +24,7 @@ use sha1::{Digest as _, Sha1};
|
||||
use zeroize::Zeroize as _;
|
||||
|
||||
use crate::{
|
||||
config::{ApplicationId, GitRemote, RemoteEndpoint, RemoteTransport, ServerId},
|
||||
config::{ApplicationId, GitRemote, RemoteEndpoint, RemoteTransport, ServerId, SshFingerprint},
|
||||
crypto::{KeyHandle, KeyStore, SecretProvider},
|
||||
mutation::{TreeCommit, TreeCommitError, TreeCommitter},
|
||||
recipient::{PolicyCommit, PolicyCommitError, PolicyCommitter},
|
||||
@@ -364,6 +364,67 @@ impl GitDiffEntry {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct SshHostKey {
|
||||
host: String,
|
||||
port: u16,
|
||||
algorithm: String,
|
||||
fingerprint: SshFingerprint,
|
||||
encoded: String,
|
||||
}
|
||||
|
||||
impl SshHostKey {
|
||||
#[cfg(feature = "ssh")]
|
||||
pub(crate) fn new(
|
||||
host: String,
|
||||
port: u16,
|
||||
algorithm: String,
|
||||
fingerprint: SshFingerprint,
|
||||
encoded: String,
|
||||
) -> Self {
|
||||
Self {
|
||||
host,
|
||||
port,
|
||||
algorithm,
|
||||
fingerprint,
|
||||
encoded,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn host(&self) -> &str {
|
||||
&self.host
|
||||
}
|
||||
|
||||
pub const fn port(&self) -> u16 {
|
||||
self.port
|
||||
}
|
||||
|
||||
pub fn algorithm(&self) -> &str {
|
||||
&self.algorithm
|
||||
}
|
||||
|
||||
pub const fn fingerprint(&self) -> &SshFingerprint {
|
||||
&self.fingerprint
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssh")]
|
||||
pub(crate) fn encoded(&self) -> &str {
|
||||
&self.encoded
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SshHostKey {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter
|
||||
.debug_struct("SshHostKey")
|
||||
.field("host", &self.host)
|
||||
.field("port", &self.port)
|
||||
.field("algorithm", &self.algorithm)
|
||||
.field("fingerprint", &self.fingerprint)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum GitError {
|
||||
NotRepository,
|
||||
@@ -390,6 +451,44 @@ pub enum GitError {
|
||||
RemoteNotFound {
|
||||
name: String,
|
||||
},
|
||||
SshAuthenticationNotConfigured,
|
||||
SshIdentityMissing {
|
||||
path: PathBuf,
|
||||
},
|
||||
SshIdentityInvalid {
|
||||
path: PathBuf,
|
||||
},
|
||||
SshUnsupportedAlgorithm {
|
||||
algorithm: String,
|
||||
},
|
||||
SshKeyPassphraseUnavailable {
|
||||
fingerprint: SshFingerprint,
|
||||
},
|
||||
SshKeyPassphraseDenied {
|
||||
fingerprint: SshFingerprint,
|
||||
},
|
||||
SshKeyPassphraseCancelled {
|
||||
fingerprint: SshFingerprint,
|
||||
},
|
||||
SshKeyPassphraseRejected {
|
||||
fingerprint: SshFingerprint,
|
||||
},
|
||||
SshAgentUnavailable,
|
||||
SshAgentIdentityMissing {
|
||||
fingerprint: SshFingerprint,
|
||||
},
|
||||
SshAuthenticationRejected,
|
||||
UnknownSshHostKey {
|
||||
host_key: Box<SshHostKey>,
|
||||
},
|
||||
ChangedSshHostKey {
|
||||
host_key: Box<SshHostKey>,
|
||||
line: usize,
|
||||
},
|
||||
SshKnownHostsUnavailable {
|
||||
path: PathBuf,
|
||||
},
|
||||
SshProtocolFailed,
|
||||
CredentialsUnavailable,
|
||||
CredentialAccessDenied,
|
||||
CredentialCancelled,
|
||||
@@ -441,6 +540,73 @@ impl fmt::Display for GitError {
|
||||
)
|
||||
}
|
||||
Self::RemoteNotFound { name } => write!(formatter, "Git remote not found: {name}"),
|
||||
Self::SshAuthenticationNotConfigured => {
|
||||
formatter.write_str("SSH authentication is not configured")
|
||||
}
|
||||
Self::SshIdentityMissing { path } => {
|
||||
write!(
|
||||
formatter,
|
||||
"SSH identity file was not found: {}",
|
||||
path.display()
|
||||
)
|
||||
}
|
||||
Self::SshIdentityInvalid { path } => {
|
||||
write!(
|
||||
formatter,
|
||||
"SSH identity file is invalid: {}",
|
||||
path.display()
|
||||
)
|
||||
}
|
||||
Self::SshUnsupportedAlgorithm { algorithm } => {
|
||||
write!(formatter, "unsupported SSH key algorithm: {algorithm}")
|
||||
}
|
||||
Self::SshKeyPassphraseUnavailable { fingerprint } => {
|
||||
write!(
|
||||
formatter,
|
||||
"SSH key passphrase is unavailable for {fingerprint}"
|
||||
)
|
||||
}
|
||||
Self::SshKeyPassphraseDenied { fingerprint } => {
|
||||
write!(
|
||||
formatter,
|
||||
"access to the SSH key passphrase was denied for {fingerprint}"
|
||||
)
|
||||
}
|
||||
Self::SshKeyPassphraseCancelled { fingerprint } => {
|
||||
write!(
|
||||
formatter,
|
||||
"SSH key passphrase access was cancelled for {fingerprint}"
|
||||
)
|
||||
}
|
||||
Self::SshKeyPassphraseRejected { fingerprint } => {
|
||||
write!(
|
||||
formatter,
|
||||
"SSH key passphrase was rejected for {fingerprint}"
|
||||
)
|
||||
}
|
||||
Self::SshAgentUnavailable => formatter.write_str("the SSH agent is unavailable"),
|
||||
Self::SshAgentIdentityMissing { fingerprint } => {
|
||||
write!(formatter, "the SSH agent does not contain {fingerprint}")
|
||||
}
|
||||
Self::SshAuthenticationRejected => {
|
||||
formatter.write_str("SSH public-key authentication was rejected")
|
||||
}
|
||||
Self::UnknownSshHostKey { host_key } => write!(
|
||||
formatter,
|
||||
"unknown SSH host key for {}:{} ({})",
|
||||
host_key.host, host_key.port, host_key.fingerprint
|
||||
),
|
||||
Self::ChangedSshHostKey { host_key, line } => write!(
|
||||
formatter,
|
||||
"changed SSH host key for {}:{} ({}, known_hosts line {line})",
|
||||
host_key.host, host_key.port, host_key.fingerprint
|
||||
),
|
||||
Self::SshKnownHostsUnavailable { path } => write!(
|
||||
formatter,
|
||||
"SSH known-hosts file is unavailable: {}",
|
||||
path.display()
|
||||
),
|
||||
Self::SshProtocolFailed => formatter.write_str("the SSH protocol failed"),
|
||||
Self::CredentialsUnavailable => {
|
||||
formatter.write_str("HTTPS Git credentials are unavailable")
|
||||
}
|
||||
@@ -617,6 +783,10 @@ pub trait GitCredentialProvider {
|
||||
) -> Result<GitCredential, GitError>;
|
||||
}
|
||||
|
||||
pub trait SshPassphraseProvider {
|
||||
fn ssh_key_passphrase(&self, fingerprint: &SshFingerprint) -> Result<SecretBytes, GitError>;
|
||||
}
|
||||
|
||||
pub trait GitSmartHttpTransport {
|
||||
fn advertise_receive_pack(
|
||||
&self,
|
||||
|
||||
Reference in new issue
Block a user