mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-04-18 19:59:18 +00:00
Handle SSH keys better
OpenSSH will iterate through a series of keys. Additionally, there may be a key specified in the ssh/config file that we need to account for. Fixes https://gitlab.com/kicad/code/kicad/-/issues/18864
This commit is contained in:
parent
4fe05bfe47
commit
5a57e5cfd4
@ -63,6 +63,7 @@ bool GIT_CLONE_HANDLER::PerformClone()
|
||||
cloneOptions.fetch_opts.callbacks.payload = this;
|
||||
|
||||
m_testedTypes = 0;
|
||||
ResetNextKey();
|
||||
|
||||
if( git_clone( &m_repo, m_URL.ToStdString().c_str(), m_clonePath.ToStdString().c_str(),
|
||||
&cloneOptions ) != 0 )
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include <iostream>
|
||||
#include <time.h>
|
||||
|
||||
GIT_PULL_HANDLER::GIT_PULL_HANDLER( git_repository* aRepo ) : KIGIT_COMMON( aRepo )
|
||||
GIT_PULL_HANDLER::GIT_PULL_HANDLER( KIGIT_COMMON* aRepo ) : KIGIT_COMMON( *aRepo )
|
||||
{}
|
||||
|
||||
|
||||
@ -55,6 +55,9 @@ bool GIT_PULL_HANDLER::PerformFetch()
|
||||
remoteCallbacks.credentials = credentials_cb;
|
||||
remoteCallbacks.payload = this;
|
||||
|
||||
m_testedTypes = 0;
|
||||
ResetNextKey();
|
||||
|
||||
if( git_remote_connect( remote, GIT_DIRECTION_FETCH, &remoteCallbacks, nullptr, nullptr ) )
|
||||
{
|
||||
git_remote_free( remote );
|
||||
|
@ -42,14 +42,14 @@ struct CommitDetails
|
||||
std::string m_date;
|
||||
};
|
||||
|
||||
// Enum for result codes
|
||||
enum class PullResult
|
||||
// Enum for result codes, error codes are negative, success codes are positive
|
||||
enum class PullResult : int
|
||||
{
|
||||
Success,
|
||||
Error,
|
||||
MergeFailed = -2,
|
||||
Error = -1,
|
||||
Success = 0,
|
||||
UpToDate,
|
||||
FastForward,
|
||||
MergeFailed
|
||||
FastForward
|
||||
};
|
||||
|
||||
struct ConflictData
|
||||
@ -68,7 +68,7 @@ struct ConflictData
|
||||
class GIT_PULL_HANDLER : public KIGIT_COMMON, public GIT_PROGRESS
|
||||
{
|
||||
public:
|
||||
GIT_PULL_HANDLER( git_repository* aRepo );
|
||||
GIT_PULL_HANDLER( KIGIT_COMMON* aCommon );
|
||||
~GIT_PULL_HANDLER();
|
||||
|
||||
PullResult PerformPull();
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
GIT_PUSH_HANDLER::GIT_PUSH_HANDLER( git_repository* aRepo ) : KIGIT_COMMON( aRepo )
|
||||
GIT_PUSH_HANDLER::GIT_PUSH_HANDLER( KIGIT_COMMON* aRepo ) : KIGIT_COMMON( *aRepo )
|
||||
{}
|
||||
|
||||
|
||||
|
@ -43,7 +43,7 @@ enum class PushResult
|
||||
class GIT_PUSH_HANDLER : public KIGIT_COMMON, public GIT_PROGRESS
|
||||
{
|
||||
public:
|
||||
GIT_PUSH_HANDLER( git_repository* aRepo );
|
||||
GIT_PUSH_HANDLER( KIGIT_COMMON* aRepo );
|
||||
~GIT_PUSH_HANDLER();
|
||||
|
||||
PushResult PerformPush();
|
||||
|
@ -27,6 +27,8 @@
|
||||
|
||||
#include <wx/filename.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/textfile.h>
|
||||
#include <wx/utils.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
@ -416,6 +418,90 @@ wxString KIGIT_COMMON::GetRemotename() const
|
||||
return retval;
|
||||
}
|
||||
|
||||
void KIGIT_COMMON::SetSSHKey( const wxString& aKey )
|
||||
{
|
||||
auto it = std::find( m_publicKeys.begin(), m_publicKeys.end(), aKey );
|
||||
|
||||
if( it != m_publicKeys.end() )
|
||||
m_publicKeys.erase( it );
|
||||
|
||||
m_publicKeys.insert( m_publicKeys.begin(), aKey );
|
||||
}
|
||||
|
||||
|
||||
void KIGIT_COMMON::updatePublicKeys()
|
||||
{
|
||||
m_publicKeys.clear();
|
||||
|
||||
wxFileName keyFile( wxGetHomeDir(), wxEmptyString );
|
||||
keyFile.AppendDir( ".ssh" );
|
||||
keyFile.SetFullName( "id_rsa" );
|
||||
|
||||
if( keyFile.FileExists() )
|
||||
m_publicKeys.push_back( keyFile.GetFullPath() );
|
||||
|
||||
keyFile.SetFullName( "id_dsa" );
|
||||
|
||||
if( keyFile.FileExists() )
|
||||
m_publicKeys.push_back( keyFile.GetFullPath() );
|
||||
|
||||
keyFile.SetFullName( "id_ecdsa" );
|
||||
|
||||
if( keyFile.FileExists() )
|
||||
m_publicKeys.push_back( keyFile.GetFullPath() );
|
||||
|
||||
keyFile.SetFullName( "id_ed25519" );
|
||||
|
||||
if( keyFile.FileExists() )
|
||||
m_publicKeys.push_back( keyFile.GetFullPath() );
|
||||
|
||||
// Parse SSH config file for hostname information
|
||||
wxFileName sshConfig( wxGetHomeDir(), wxEmptyString );
|
||||
sshConfig.AppendDir( ".ssh" );
|
||||
sshConfig.SetFullName( "config" );
|
||||
|
||||
if( sshConfig.FileExists() )
|
||||
{
|
||||
wxTextFile configFile( sshConfig.GetFullPath() );
|
||||
configFile.Open();
|
||||
|
||||
wxString lastIdentityFile;
|
||||
bool match = false;
|
||||
|
||||
for( wxString line = configFile.GetFirstLine(); !configFile.Eof(); line = configFile.GetNextLine() )
|
||||
{
|
||||
line.Trim( false ).Trim( true );
|
||||
|
||||
if( line.StartsWith( "Host " ) )
|
||||
{
|
||||
lastIdentityFile.Clear();
|
||||
match = false;
|
||||
}
|
||||
|
||||
// The difference here is that we are matching either "Hostname" or "Host" to get the
|
||||
// match. This is because in the absence of a "Hostname" line, the "Host" line is used
|
||||
if( line.StartsWith( "Host" ) && line.Contains( m_hostname ) )
|
||||
match = true;
|
||||
|
||||
if( match && line.StartsWith( "IdentityFile" ) )
|
||||
{
|
||||
wxString keyPath = line.AfterFirst( ' ' ).Trim( false ).Trim( true );
|
||||
// Expand ~ to home directory if present
|
||||
if( keyPath.StartsWith( "~" ) )
|
||||
keyPath.Replace( "~", wxGetHomeDir(), false );
|
||||
|
||||
// Add the public key to the beginning of the list
|
||||
if( wxFileName::FileExists( keyPath ) )
|
||||
{
|
||||
SetSSHKey( keyPath );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configFile.Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void KIGIT_COMMON::UpdateCurrentBranchInfo()
|
||||
{
|
||||
@ -437,6 +523,77 @@ void KIGIT_COMMON::UpdateCurrentBranchInfo()
|
||||
|
||||
// Find the stored password if it exists
|
||||
KIPLATFORM::SECRETS::GetSecret( m_remote, m_username, m_password );
|
||||
|
||||
updateConnectionType();
|
||||
updatePublicKeys();
|
||||
}
|
||||
|
||||
void KIGIT_COMMON::updateConnectionType()
|
||||
{
|
||||
if( m_remote.StartsWith( "https://" ) || m_remote.StartsWith( "http://" ) )
|
||||
m_connType = GIT_CONN_TYPE::GIT_CONN_HTTPS;
|
||||
else if( m_remote.StartsWith( "ssh://" ) || m_remote.StartsWith( "git@" ) || m_remote.StartsWith( "git+ssh://" ) )
|
||||
m_connType = GIT_CONN_TYPE::GIT_CONN_SSH;
|
||||
else
|
||||
m_connType = GIT_CONN_TYPE::GIT_CONN_LOCAL;
|
||||
|
||||
if( m_connType != GIT_CONN_TYPE::GIT_CONN_LOCAL )
|
||||
{
|
||||
wxString uri = m_remote;
|
||||
size_t atPos = uri.find( '@' );
|
||||
|
||||
if( atPos != wxString::npos )
|
||||
{
|
||||
size_t protoEnd = uri.find( "//" );
|
||||
|
||||
if( protoEnd != wxString::npos )
|
||||
{
|
||||
wxString credentials = uri.Mid( protoEnd + 2, atPos - protoEnd - 2 );
|
||||
size_t colonPos = credentials.find( ':' );
|
||||
|
||||
if( colonPos != wxString::npos )
|
||||
{
|
||||
m_username = credentials.Left( colonPos );
|
||||
m_password = credentials.Mid( colonPos + 1, credentials.Length() - colonPos - 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_username = credentials;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_username = uri.Left( atPos );
|
||||
}
|
||||
}
|
||||
|
||||
if( m_remote.StartsWith( "git@" ) )
|
||||
{
|
||||
// SSH format: git@hostname:path
|
||||
size_t colonPos = m_remote.find( ':' );
|
||||
if( colonPos != wxString::npos )
|
||||
m_hostname = m_remote.Mid( 4, colonPos - 4 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// other URL format: proto://[user@]hostname/path
|
||||
size_t hostStart = m_remote.find( "://" ) + 2;
|
||||
size_t hostEnd = m_remote.find( '/', hostStart );
|
||||
wxString host;
|
||||
|
||||
if( hostEnd != wxString::npos )
|
||||
host = m_remote.Mid( hostStart, hostEnd - hostStart );
|
||||
else
|
||||
host = m_remote.Mid( hostStart );
|
||||
|
||||
atPos = host.find( '@' );
|
||||
|
||||
if( atPos != wxString::npos )
|
||||
m_hostname = host.Mid( atPos + 1 );
|
||||
else
|
||||
m_hostname = host;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -585,7 +742,14 @@ extern "C" int credentials_cb( git_cred** aOut, const char* aUrl, const char* aU
|
||||
&& !( parent->TestedTypes() & GIT_CREDTYPE_SSH_KEY ) )
|
||||
{
|
||||
// SSH key authentication
|
||||
wxString sshKey = parent->GetSSHKey();
|
||||
wxString sshKey = parent->GetNextPublicKey();
|
||||
|
||||
if( sshKey.IsEmpty() )
|
||||
{
|
||||
parent->TestedTypes() |= GIT_CREDTYPE_SSH_KEY;
|
||||
return GIT_PASSTHROUGH;
|
||||
}
|
||||
|
||||
wxString sshPubKey = sshKey + ".pub";
|
||||
wxString username = parent->GetUsername().Trim().Trim( false );
|
||||
wxString password = parent->GetPassword().Trim().Trim( false );
|
||||
@ -594,7 +758,6 @@ extern "C" int credentials_cb( git_cred** aOut, const char* aUrl, const char* aU
|
||||
sshPubKey.ToStdString().c_str(),
|
||||
sshKey.ToStdString().c_str(),
|
||||
password.ToStdString().c_str() );
|
||||
parent->TestedTypes() |= GIT_CREDTYPE_SSH_KEY;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -89,12 +89,11 @@ public:
|
||||
|
||||
wxString GetUsername() const { return m_username; }
|
||||
wxString GetPassword() const { return m_password; }
|
||||
wxString GetSSHKey() const { return m_sshKey; }
|
||||
GIT_CONN_TYPE GetConnType() const { return m_connType; }
|
||||
|
||||
void SetUsername( const wxString& aUsername ) { m_username = aUsername; }
|
||||
void SetPassword( const wxString& aPassword ) { m_password = aPassword; }
|
||||
void SetSSHKey( const wxString& aSSHKey ) { m_sshKey = aSSHKey; }
|
||||
void SetSSHKey( const wxString& aSSHKey );
|
||||
|
||||
void SetConnType( GIT_CONN_TYPE aConnType ) { m_connType = aConnType; }
|
||||
void SetConnType( unsigned aConnType )
|
||||
@ -119,17 +118,34 @@ public:
|
||||
|
||||
wxString GetRemotename() const;
|
||||
|
||||
void ResetNextKey() { m_nextPublicKey = 0; }
|
||||
|
||||
wxString GetNextPublicKey()
|
||||
{
|
||||
if( m_nextPublicKey >= static_cast<int>( m_publicKeys.size() ) )
|
||||
return wxEmptyString;
|
||||
|
||||
return m_publicKeys[m_nextPublicKey++];
|
||||
}
|
||||
|
||||
protected:
|
||||
git_repository* m_repo;
|
||||
|
||||
GIT_CONN_TYPE m_connType;
|
||||
wxString m_remote;
|
||||
wxString m_remote; // This is the full connection string
|
||||
wxString m_hostname; // This is just the hostname without the protocol, username, or password
|
||||
wxString m_username;
|
||||
wxString m_password;
|
||||
wxString m_sshKey;
|
||||
|
||||
unsigned m_testedTypes;
|
||||
|
||||
private:
|
||||
void updatePublicKeys();
|
||||
void updateConnectionType();
|
||||
|
||||
std::vector<wxString> m_publicKeys;
|
||||
int m_nextPublicKey;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -641,15 +641,6 @@ void PROJECT_TREE_PANE::ReCreateTreePrj()
|
||||
m_TreeProject->GitCommon()->SetUsername( Prj().GetLocalSettings().m_GitRepoUsername );
|
||||
m_TreeProject->GitCommon()->SetSSHKey( Prj().GetLocalSettings().m_GitSSHKey );
|
||||
m_TreeProject->GitCommon()->UpdateCurrentBranchInfo();
|
||||
|
||||
wxString conn_type = Prj().GetLocalSettings().m_GitRepoType;
|
||||
|
||||
if( conn_type == "https" )
|
||||
m_TreeProject->GitCommon()->SetConnType( KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_HTTPS );
|
||||
else if( conn_type == "ssh" )
|
||||
m_TreeProject->GitCommon()->SetConnType( KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_SSH );
|
||||
else
|
||||
m_TreeProject->GitCommon()->SetConnType( KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_LOCAL );
|
||||
}
|
||||
|
||||
// We may have opened a legacy project, in which case GetProjectFileName will return the
|
||||
@ -1694,12 +1685,7 @@ void PROJECT_TREE_PANE::onGitInitializeProject( wxCommandEvent& aEvent )
|
||||
|
||||
m_gitLastError = GIT_ERROR_NONE;
|
||||
|
||||
GIT_PULL_HANDLER handler( repo );
|
||||
|
||||
handler.SetConnType( m_TreeProject->GitCommon()->GetConnType() );
|
||||
handler.SetUsername( m_TreeProject->GitCommon()->GetUsername() );
|
||||
handler.SetPassword( m_TreeProject->GitCommon()->GetPassword() );
|
||||
handler.SetSSHKey( m_TreeProject->GitCommon()->GetSSHKey() );
|
||||
GIT_PULL_HANDLER handler( m_TreeProject->GitCommon() );
|
||||
|
||||
handler.SetProgressReporter( std::make_unique<WX_PROGRESS_REPORTER>( this,
|
||||
_( "Fetching Remote" ),
|
||||
@ -1733,18 +1719,13 @@ void PROJECT_TREE_PANE::onGitPullProject( wxCommandEvent& aEvent )
|
||||
if( !repo )
|
||||
return;
|
||||
|
||||
GIT_PULL_HANDLER handler( repo );
|
||||
|
||||
handler.SetConnType( m_TreeProject->GitCommon()->GetConnType() );
|
||||
handler.SetUsername( m_TreeProject->GitCommon()->GetUsername() );
|
||||
handler.SetPassword( m_TreeProject->GitCommon()->GetPassword() );
|
||||
handler.SetSSHKey( m_TreeProject->GitCommon()->GetSSHKey() );
|
||||
GIT_PULL_HANDLER handler( m_TreeProject->GitCommon() );
|
||||
|
||||
handler.SetProgressReporter( std::make_unique<WX_PROGRESS_REPORTER>( this,
|
||||
_( "Fetching Remote" ),
|
||||
1 ) );
|
||||
|
||||
if( handler.PerformPull() != PullResult::Success )
|
||||
if( handler.PerformPull() < PullResult::Success )
|
||||
{
|
||||
wxString errorMessage = handler.GetErrorString();
|
||||
|
||||
@ -1760,12 +1741,7 @@ void PROJECT_TREE_PANE::onGitPushProject( wxCommandEvent& aEvent )
|
||||
if( !repo )
|
||||
return;
|
||||
|
||||
GIT_PUSH_HANDLER handler( repo );
|
||||
|
||||
handler.SetConnType( m_TreeProject->GitCommon()->GetConnType() );
|
||||
handler.SetUsername( m_TreeProject->GitCommon()->GetUsername() );
|
||||
handler.SetPassword( m_TreeProject->GitCommon()->GetPassword() );
|
||||
handler.SetSSHKey( m_TreeProject->GitCommon()->GetSSHKey() );
|
||||
GIT_PUSH_HANDLER handler( m_TreeProject->GitCommon() );
|
||||
|
||||
handler.SetProgressReporter( std::make_unique<WX_PROGRESS_REPORTER>( this,
|
||||
_( "Fetching Remote" ),
|
||||
@ -2410,12 +2386,12 @@ void PROJECT_TREE_PANE::onGitSyncProject( wxCommandEvent& aEvent )
|
||||
|
||||
void PROJECT_TREE_PANE::onGitFetch( wxCommandEvent& aEvent )
|
||||
{
|
||||
git_repository* repo = m_TreeProject->GetGitRepo();
|
||||
KIGIT_COMMON* gitCommon = m_TreeProject->GitCommon();
|
||||
|
||||
if( !repo )
|
||||
if( !gitCommon )
|
||||
return;
|
||||
|
||||
GIT_PULL_HANDLER handler( repo );
|
||||
GIT_PULL_HANDLER handler( gitCommon );
|
||||
handler.PerformFetch();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user