From cde8ac647abb0ed30e347e9fd9f3dd7030d72bc8 Mon Sep 17 00:00:00 2001 From: Rick Martin Date: Fri, 19 May 2023 02:07:23 -0700 Subject: [PATCH] Add SSH support from github.com/leobuskin/libgit2sharp-ssh. Add local nuget package source to use locally built libgit2sharp.nativebinaries. Add readme explaining how to build locally. --- LibGit2Sharp/AuthenticationException.cs | 58 +++++++++++++++++++++ LibGit2Sharp/Core/Ensure.cs | 1 + LibGit2Sharp/Core/NativeMethods.cs | 18 +++++++ LibGit2Sharp/LibGit2Sharp.csproj | 2 +- LibGit2Sharp/RemoteCallbacks.cs | 8 +++ LibGit2Sharp/SshAgentCredentials.cs | 36 +++++++++++++ LibGit2Sharp/SshUserKeyCredentials.cs | 66 ++++++++++++++++++++++++ LibGit2Sharp/SupportedCredentialTypes.cs | 10 ++++ LibGit2Sharp/UsernameQueryCredentials.cs | 31 +++++++++++ README.md | 35 +++++++++++++ nuget.config | 1 + 11 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 LibGit2Sharp/AuthenticationException.cs create mode 100644 LibGit2Sharp/SshAgentCredentials.cs create mode 100644 LibGit2Sharp/SshUserKeyCredentials.cs create mode 100644 LibGit2Sharp/UsernameQueryCredentials.cs diff --git a/LibGit2Sharp/AuthenticationException.cs b/LibGit2Sharp/AuthenticationException.cs new file mode 100644 index 000000000..416d01719 --- /dev/null +++ b/LibGit2Sharp/AuthenticationException.cs @@ -0,0 +1,58 @@ +using System; +using System.Runtime.Serialization; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// The exception that is thrown when an operation which requires an + /// authentication fails. + /// + [Serializable] + public class AuthenticationException : LibGit2SharpException + { + /// + /// Initializes a new instance of the class. + /// + public AuthenticationException() + { + } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// A message that describes the error. + public AuthenticationException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception. If the parameter is not a null reference, the current exception is raised in a catch block that handles the inner exception. + public AuthenticationException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the class with a serialized data. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected AuthenticationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + internal AuthenticationException(string message, GitErrorCategory category) + : base(message, category) + { } + + internal AuthenticationException(string message, GitErrorCode code, GitErrorCategory category) + : base(message, code, category) + { + } + } +} diff --git a/LibGit2Sharp/Core/Ensure.cs b/LibGit2Sharp/Core/Ensure.cs index 3cf03d24b..d32efd1e5 100644 --- a/LibGit2Sharp/Core/Ensure.cs +++ b/LibGit2Sharp/Core/Ensure.cs @@ -128,6 +128,7 @@ private static readonly Dictionary new LockedFileException(m, c) }, { GitErrorCode.NotFound, (m, c) => new NotFoundException(m, c) }, { GitErrorCode.Peel, (m, c) => new PeelException(m, c) }, + { GitErrorCode.Auth, (m, c) => new AuthenticationException(m, c) }, }; private static unsafe void HandleError(int result) diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index e20d755ba..f45b211c2 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -599,6 +599,24 @@ internal static extern int git_cred_userpass_plaintext_new( [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern void git_cred_free(IntPtr cred); + [DllImport(libgit2)] + internal static extern int git_cred_ssh_key_new( + out IntPtr cred, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string username, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string publickey, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string privatekey, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string passphrase); + + [DllImport(libgit2)] + internal static extern int git_cred_ssh_key_from_agent( + out IntPtr cred, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string username); + + [DllImport(libgit2)] + internal static extern int git_cred_username_new( + out IntPtr cred, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string username); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_describe_commit( out git_describe_result* describe, diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index f690c9107..4a52626d0 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -33,7 +33,7 @@ - + diff --git a/LibGit2Sharp/RemoteCallbacks.cs b/LibGit2Sharp/RemoteCallbacks.cs index ce5dccf81..2cd8569d3 100644 --- a/LibGit2Sharp/RemoteCallbacks.cs +++ b/LibGit2Sharp/RemoteCallbacks.cs @@ -284,6 +284,14 @@ private int GitCredentialHandler( { types |= SupportedCredentialTypes.Default; } + if (credTypes.HasFlag(GitCredentialType.SshKey)) + { + types |= SupportedCredentialTypes.Ssh; + } + if (credTypes.HasFlag(GitCredentialType.Username)) + { + types |= SupportedCredentialTypes.UsernameQuery; + } ptr = IntPtr.Zero; try diff --git a/LibGit2Sharp/SshAgentCredentials.cs b/LibGit2Sharp/SshAgentCredentials.cs new file mode 100644 index 000000000..67267b656 --- /dev/null +++ b/LibGit2Sharp/SshAgentCredentials.cs @@ -0,0 +1,36 @@ +using System; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// Class that holds SSH agent credentials for remote repository access. + /// + public sealed class SshAgentCredentials : Credentials + { + /// + /// Callback to acquire a credential object. + /// + /// The newly created credential object. + /// 0 for success, < 0 to indicate an error, > 0 to indicate no credential was acquired. + protected internal override int GitCredentialHandler(out IntPtr cred) + { + if (!GlobalSettings.Version.Features.HasFlag(BuiltInFeatures.Ssh)) + { + throw new InvalidOperationException("LibGit2 was not built with SSH support."); + } + + if (Username == null) + { + throw new InvalidOperationException("SshAgentCredentials contains a null Username."); + } + + return NativeMethods.git_cred_ssh_key_from_agent(out cred, Username); + } + + /// + /// Username for SSH authentication. + /// + public string Username { get; set; } + } +} diff --git a/LibGit2Sharp/SshUserKeyCredentials.cs b/LibGit2Sharp/SshUserKeyCredentials.cs new file mode 100644 index 000000000..f32916fcc --- /dev/null +++ b/LibGit2Sharp/SshUserKeyCredentials.cs @@ -0,0 +1,66 @@ +using System; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// Class that holds SSH username with key credentials for remote repository access. + /// + public sealed class SshUserKeyCredentials : Credentials + { + /// + /// Callback to acquire a credential object. + /// + /// The newly created credential object. + /// 0 for success, < 0 to indicate an error, > 0 to indicate no credential was acquired. + protected internal override int GitCredentialHandler(out IntPtr cred) + { + if (!GlobalSettings.Version.Features.HasFlag(BuiltInFeatures.Ssh)) + { + throw new InvalidOperationException("LibGit2 was not built with SSH support."); + } + + if (Username == null) + { + throw new InvalidOperationException("SshUserKeyCredentials contains a null Username."); + } + + if (Passphrase == null) + { + throw new InvalidOperationException("SshUserKeyCredentials contains a null Passphrase."); + } + + if (PublicKey == null) + { + throw new InvalidOperationException("SshUserKeyCredentials contains a null PublicKey."); + } + + if (PrivateKey == null) + { + throw new InvalidOperationException("SshUserKeyCredentials contains a null PrivateKey."); + } + + return NativeMethods.git_cred_ssh_key_new(out cred, Username, PublicKey, PrivateKey, Passphrase); + } + + /// + /// Username for SSH authentication. + /// + public string Username { get; set; } + + /// + /// Public key file location for SSH authentication. + /// + public string PublicKey { get; set; } + + /// + /// Private key file location for SSH authentication. + /// + public string PrivateKey { get; set; } + + /// + /// Passphrase for SSH authentication. + /// + public string Passphrase { get; set; } + } +} diff --git a/LibGit2Sharp/SupportedCredentialTypes.cs b/LibGit2Sharp/SupportedCredentialTypes.cs index bc38a259e..077597011 100644 --- a/LibGit2Sharp/SupportedCredentialTypes.cs +++ b/LibGit2Sharp/SupportedCredentialTypes.cs @@ -18,5 +18,15 @@ public enum SupportedCredentialTypes /// Ask Windows to provide its default credentials for the current user (e.g. NTLM) /// Default = (1 << 1), + + /// + /// SSH with username and public/private keys. (SshUserKeyCredentials, SshAgentCredentials). + /// + Ssh = (1 << 2), + + /// + /// Queries the server with the given username, then later returns the supported credential types. + /// + UsernameQuery = (1 << 3), } } diff --git a/LibGit2Sharp/UsernameQueryCredentials.cs b/LibGit2Sharp/UsernameQueryCredentials.cs new file mode 100644 index 000000000..14981d74e --- /dev/null +++ b/LibGit2Sharp/UsernameQueryCredentials.cs @@ -0,0 +1,31 @@ +using System; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// Class that holds username query credentials for remote repository access. + /// + public sealed class UsernameQueryCredentials : Credentials + { + /// + /// Callback to acquire a credential object. + /// + /// The newly created credential object. + /// 0 for success, < 0 to indicate an error, > 0 to indicate no credential was acquired. + protected internal override int GitCredentialHandler(out IntPtr cred) + { + if (Username == null) + { + throw new InvalidOperationException("UsernameQueryCredentials contains a null Username."); + } + + return NativeMethods.git_cred_username_new(out cred, Username); + } + + /// + /// Username for querying the server for supported authentication. + /// + public string Username { get; set; } + } +} diff --git a/README.md b/README.md index 3aafdceb1..52adc70e6 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,41 @@ - [NuGet package](http://nuget.org/List/Packages/LibGit2Sharp) - [Source code](https://github.com/libgit2/libgit2sharp/) +## MadCap steps for building locally + +1. Clone this repository as well as libgit2sharp.nativebinaries so they are in folders next to each other: + + ``` + git clone https://github.com/madcapsoftware/libgit2sharp.git + git clone https://github.com/madcapsoftware/libgit2sharp.nativebinaries.git + ``` + + Example folder structure: + + ``` + C:\source\madcapsoftware\libgit2sharp + C:\source\madcapsoftware\libgit2sharp.nativebinaries + ``` + +2. Open a powershell terminal to the root of the libgit2sharp.nativebinaries repository +3. Build `libgit2sharp.nativebinaries` and create a local nuget package, specifying the version to use. The version at time of writing matches the main `libgit2sharp.nativebinaries` repository with a `-ssh` suffix. + + ``` + .\UpdateAllLibsToSha.ps1 -libgit2sha main -libssh2sha master -zlibsha master + .\build.libgit2.ps1 -x86 -x64 -libssh2 + .\nuget.exe Pack nuget.package/NativeBinaries.nuspec -Version 2.0.320-ssh -NoPackageAnalysis + ``` + +4. Open the LibGit2Sharp solution +5. Right click the LibGit2Sharp project and click "Manage NuGet packages..." +6. Make sure "Package source" is set to "libgit2sharp.nativebinaries" which is a local relative path to the package generated in step 3 +7. Check "Include prerelease" as the package version is a prerelease version due to the `-ssh` suffix +8. Click Install to update to the latest version +9. Build the `Release` solution configuration. + +NOTE: The version of the resulting dll is based on the most recent git tag, which in this case is `0.27.2-ssh.0`. +For more details, see: https://github.com/adamralph/minver + ## Troubleshooting and support - Usage or programming related question? Post it on [StackOverflow](http://stackoverflow.com/questions/tagged/libgit2sharp) using the tag *libgit2sharp* diff --git a/nuget.config b/nuget.config index 35696f810..678a24680 100644 --- a/nuget.config +++ b/nuget.config @@ -2,5 +2,6 @@ +