diff --git a/LibGit2Sharp.Tests/GlobalSettingsFixture.cs b/LibGit2Sharp.Tests/GlobalSettingsFixture.cs index b4d1f24ab..41ace8e58 100644 --- a/LibGit2Sharp.Tests/GlobalSettingsFixture.cs +++ b/LibGit2Sharp.Tests/GlobalSettingsFixture.cs @@ -61,7 +61,6 @@ public void LoadFromSpecifiedPath(string architecture) { Skip.IfNot(Platform.IsRunningOnNetFramework(), ".NET Framework only test."); - var nativeDllFileName = NativeDllName.Name + ".dll"; var testDir = Path.GetDirectoryName(typeof(GlobalSettingsFixture).Assembly.Location); var testAppExe = Path.Combine(testDir, $"NativeLibraryLoadTestApp.{architecture}.exe"); var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); @@ -71,7 +70,10 @@ public void LoadFromSpecifiedPath(string architecture) try { Directory.CreateDirectory(platformDir); - File.Copy(Path.Combine(libraryPath, nativeDllFileName), Path.Combine(platformDir, nativeDllFileName)); + foreach (var file in Directory.GetFiles(libraryPath, "*.dll")) + { + File.Copy(file, Path.Combine(platformDir, Path.GetFileName(file))); + } var (output, exitCode) = ProcessHelper.RunProcess(testAppExe, arguments: $@"{NativeDllName.Name} ""{platformDir}""", workingDirectory: tempDir); diff --git a/LibGit2Sharp.Tests/NetworkFixture.cs b/LibGit2Sharp.Tests/NetworkFixture.cs index f4ad922f6..9f21fa90e 100644 --- a/LibGit2Sharp.Tests/NetworkFixture.cs +++ b/LibGit2Sharp.Tests/NetworkFixture.cs @@ -22,7 +22,6 @@ public void CanListRemoteReferences(string url) Remote remote = repo.Network.Remotes.Add(remoteName, url); IList references = repo.Network.ListReferences(remote).ToList(); - foreach (var reference in references) { // None of those references point to an existing @@ -136,6 +135,133 @@ public void CanListRemoteReferencesWithCredentials() } } + [Theory] + [InlineData("http://github.com/libgit2/TestGitRepository")] + [InlineData("https://github.com/libgit2/TestGitRepository")] + public void CanListRemoteReferencesWithListRemoteOptions(string url) + { + string remoteName = "testRemote"; + + string repoPath = InitNewRepository(); + + using (var repo = new Repository(repoPath)) + { + Remote remote = repo.Network.Remotes.Add(remoteName, url); + var options = new ListRemoteOptions + { + ProxyOptions = new ProxyOptions() + }; + + IList references = repo.Network.ListReferences(remote, options).ToList(); + + foreach (var reference in references) + { + Assert.Null(reference.ResolveToDirectReference().Target); + } + + List> actualRefs = references. + Select(directRef => new Tuple(directRef.CanonicalName, directRef.ResolveToDirectReference() + .TargetIdentifier)).ToList(); + + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs.Count, actualRefs.Count); + Assert.True(references.Single(reference => reference.CanonicalName == "HEAD") is SymbolicReference); + for (int i = 0; i < TestRemoteRefs.ExpectedRemoteRefs.Count; i++) + { + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2); + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1); + } + } + } + + [Theory] + [InlineData("http://github.com/libgit2/TestGitRepository")] + [InlineData("https://github.com/libgit2/TestGitRepository")] + public void CanListRemoteReferencesFromUrlWithListRemoteOptions(string url) + { + string repoPath = InitNewRepository(); + + using (var repo = new Repository(repoPath)) + { + var options = new ListRemoteOptions + { + ProxyOptions = new ProxyOptions() + }; + + IList references = repo.Network.ListReferences(url, options).ToList(); + + foreach (var reference in references) + { + Assert.Null(reference.ResolveToDirectReference().Target); + } + + List> actualRefs = references. + Select(directRef => new Tuple(directRef.CanonicalName, directRef.ResolveToDirectReference() + .TargetIdentifier)).ToList(); + + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs.Count, actualRefs.Count); + Assert.True(references.Single(reference => reference.CanonicalName == "HEAD") is SymbolicReference); + for (int i = 0; i < TestRemoteRefs.ExpectedRemoteRefs.Count; i++) + { + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2); + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1); + } + } + } + + [Theory] + [InlineData("https://github.com/libgit2/TestGitRepository")] + public void CanListRemoteReferencesWithCertificateCheckCallback(string url) + { + string repoPath = InitNewRepository(); + + bool certificateCheckCalled = false; + + using (var repo = new Repository(repoPath)) + { + var options = new ListRemoteOptions + { + CertificateCheck = (cert, valid, host) => + { + certificateCheckCalled = true; + return true; + } + }; + + IList references = repo.Network.ListReferences(url, options).ToList(); + + Assert.True(certificateCheckCalled); + Assert.NotEmpty(references); + } + } + + [SkippableFact] + public void CanListRemoteReferencesWithCredentialsInListRemoteOptions() + { + InconclusiveIf(() => string.IsNullOrEmpty(Constants.PrivateRepoUrl), + "Populate Constants.PrivateRepo* to run this test"); + + string remoteName = "origin"; + + string repoPath = InitNewRepository(); + + using (var repo = new Repository(repoPath)) + { + Remote remote = repo.Network.Remotes.Add(remoteName, Constants.PrivateRepoUrl); + + var options = new ListRemoteOptions + { + CredentialsProvider = Constants.PrivateRepoCredentials + }; + + var references = repo.Network.ListReferences(remote, options); + + foreach (var reference in references) + { + Assert.NotNull(reference); + } + } + } + [Theory] [InlineData(FastForwardStrategy.Default)] [InlineData(FastForwardStrategy.NoFastForward)] diff --git a/LibGit2Sharp.Tests/PushFixture.cs b/LibGit2Sharp.Tests/PushFixture.cs index 824c1d8c0..29969ca59 100644 --- a/LibGit2Sharp.Tests/PushFixture.cs +++ b/LibGit2Sharp.Tests/PushFixture.cs @@ -135,6 +135,18 @@ public void CanInvokePrePushCallbackAndFail() Assert.True(prePushHandlerCalled); } + [Fact] + public void CanPushWithRemoteProgressCallback() + { + PushOptions options = new PushOptions() + { + OnPushStatusError = OnPushStatusError, + OnPushRemoteProgress = (progress) => { return true; }, + }; + + AssertPush(repo => repo.Network.Push(repo.Network.Remotes["origin"], "HEAD", @"refs/heads/master", options)); + } + [Fact] public void PushingABranchThatDoesNotTrackAnUpstreamBranchThrows() { diff --git a/LibGit2Sharp.Tests/RepositoryFixture.cs b/LibGit2Sharp.Tests/RepositoryFixture.cs index ef3e72f07..891737ee5 100644 --- a/LibGit2Sharp.Tests/RepositoryFixture.cs +++ b/LibGit2Sharp.Tests/RepositoryFixture.cs @@ -724,6 +724,30 @@ public void CanListRemoteReferences(string url) } } + [Theory] + [InlineData("http://github.com/libgit2/TestGitRepository")] + [InlineData("https://github.com/libgit2/TestGitRepository")] + public void CanListRemoteReferencesWithListRemoteOptions(string url) + { + var options = new ListRemoteOptions + { + ProxyOptions = new ProxyOptions() + }; + + IEnumerable references = Repository.ListRemoteReferences(url, options).ToList(); + + List> actualRefs = references. + Select(reference => new Tuple(reference.CanonicalName, reference.ResolveToDirectReference().TargetIdentifier)).ToList(); + + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs.Count, actualRefs.Count); + Assert.True(references.Single(reference => reference.CanonicalName == "HEAD") is SymbolicReference); + for (int i = 0; i < TestRemoteRefs.ExpectedRemoteRefs.Count; i++) + { + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2); + Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1); + } + } + [Fact] public void CanListRemoteReferencesWithDetachedRemoteHead() { diff --git a/LibGit2Sharp/CertificateSsh.cs b/LibGit2Sharp/CertificateSsh.cs index 683c04402..37c3e3052 100644 --- a/LibGit2Sharp/CertificateSsh.cs +++ b/LibGit2Sharp/CertificateSsh.cs @@ -25,6 +25,11 @@ protected CertificateSsh() /// public readonly byte[] HashSHA1; + /// + /// The SHA256 hash of the host. Meaningful if is true + /// + public readonly byte[] HashSHA256; + /// /// True if we have the MD5 hostkey hash from the server /// @@ -35,11 +40,17 @@ protected CertificateSsh() /// public readonly bool HasSHA1; + /// + /// True if we have the SHA256 hostkey hash from the server + /// + public readonly bool HasSHA256; + internal unsafe CertificateSsh(git_certificate_ssh* cert) { HasMD5 = cert->type.HasFlag(GitCertificateSshType.MD5); HasSHA1 = cert->type.HasFlag(GitCertificateSshType.SHA1); + HasSHA256 = cert->type.HasFlag(GitCertificateSshType.SHA256); HashMD5 = new byte[16]; for (var i = 0; i < HashMD5.Length; i++) @@ -52,6 +63,12 @@ internal unsafe CertificateSsh(git_certificate_ssh* cert) { HashSHA1[i] = cert->HashSHA1[i]; } + + HashSHA256 = new byte[32]; + for (var i = 0; i < HashSHA256.Length; i++) + { + HashSHA256[i] = cert->HashSHA256[i]; + } } internal unsafe IntPtr ToPointer() @@ -65,6 +82,10 @@ internal unsafe IntPtr ToPointer() { sshCertType |= GitCertificateSshType.SHA1; } + if (HasSHA256) + { + sshCertType |= GitCertificateSshType.SHA256; + } var gitCert = new git_certificate_ssh() { @@ -88,6 +109,14 @@ internal unsafe IntPtr ToPointer() } } + fixed (byte* p = &HashSHA256[0]) + { + for (var i = 0; i < HashSHA256.Length; i++) + { + gitCert.HashSHA256[i] = p[i]; + } + } + var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(gitCert)); Marshal.StructureToPtr(gitCert, ptr, false); diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index cbb850b16..cf717138d 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -132,8 +132,26 @@ private static IntPtr ResolveDll(string libraryName, Assembly assembly, DllImpor [DllImport("libdl", EntryPoint = "dlopen")] private static extern IntPtr LoadUnixLibrary(string path, int flags); - [DllImport("kernel32", EntryPoint = "LoadLibrary")] - private static extern IntPtr LoadWindowsLibrary(string path); + [DllImport("kernel32", EntryPoint = "AddDllDirectory", CharSet = CharSet.Unicode)] + private static extern IntPtr AddDllDirectory(string path); + + [DllImport("kernel32", EntryPoint = "LoadLibraryExW", CharSet = CharSet.Unicode)] + private static extern IntPtr LoadWindowsLibraryEx(string path, IntPtr hFile, uint flags); + + private const uint LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000; + + // Use AddDllDirectory + LoadLibraryEx so that transitive native dependencies + // (e.g. libssh2 -> libcrypto) in the same directory are resolved at load time. + private static IntPtr LoadWindowsLibrary(string path) + { + var directory = Path.GetDirectoryName(path); + if (directory != null) + { + AddDllDirectory(directory); + } + + return LoadWindowsLibraryEx(path, IntPtr.Zero, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); + } // Avoid inlining this method because otherwise mono's JITter may try // to load the library _before_ we've configured the path. diff --git a/LibGit2Sharp/Core/SshExtensions.cs b/LibGit2Sharp/Core/SshExtensions.cs new file mode 100644 index 000000000..5c571707d --- /dev/null +++ b/LibGit2Sharp/Core/SshExtensions.cs @@ -0,0 +1,27 @@ +using LibGit2Sharp.Core; +using System; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Ssh +{ + internal static class NativeMethods + { + private const string libgit2 = NativeDllName.Name; + + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + 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, CallingConvention = CallingConvention.Cdecl)] + internal static extern int git_cred_ssh_key_memory_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); + } +} diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index 90d3eb193..befb596eb 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -30,7 +30,7 @@ - + diff --git a/LibGit2Sharp/ListRemoteOptions.cs b/LibGit2Sharp/ListRemoteOptions.cs new file mode 100644 index 000000000..d7ec62835 --- /dev/null +++ b/LibGit2Sharp/ListRemoteOptions.cs @@ -0,0 +1,26 @@ +using LibGit2Sharp.Handlers; + +namespace LibGit2Sharp; + +/// +/// Options controlling ListRemote behavior. +/// +public sealed class ListRemoteOptions +{ + /// + /// Handler to generate for authentication. + /// + public CredentialsHandler CredentialsProvider { get; set; } + + /// + /// This handler will be called to let the user make a decision on whether to allow + /// the connection to proceed based on the certificate presented by the server. + /// + public CertificateCheckHandler CertificateCheck { get; set; } + + + /// + /// Options for connecting through a proxy. + /// + public ProxyOptions ProxyOptions { get; set; } = new(); +} diff --git a/LibGit2Sharp/Network.cs b/LibGit2Sharp/Network.cs index ba0a33144..cddc770b0 100644 --- a/LibGit2Sharp/Network.cs +++ b/LibGit2Sharp/Network.cs @@ -52,7 +52,31 @@ public virtual IEnumerable ListReferences(Remote remote) { Ensure.ArgumentNotNull(remote, "remote"); - return ListReferencesInternal(remote.Url, null, new ProxyOptions()); + var options = new ListRemoteOptions() + { + ProxyOptions = new ProxyOptions() + }; + + return ListReferencesInternal(remote.Url, options); + } + + /// + /// List references in a repository. + /// + /// When the remote tips are ahead of the local ones, the retrieved + /// s may point to non existing + /// s in the local repository. In that + /// case, will return null. + /// + /// + /// The to list from. + /// The options for the remote request. + /// The references in the repository. + public virtual IEnumerable ListReferences(Remote remote, ListRemoteOptions options) + { + Ensure.ArgumentNotNull(remote, "remote"); + + return ListReferencesInternal(remote.Url, options); } /// @@ -71,7 +95,12 @@ public virtual IEnumerable ListReferences(Remote remote, ProxyOptions { Ensure.ArgumentNotNull(remote, "remote"); - return ListReferencesInternal(remote.Url, null, proxyOptions); + var options = new ListRemoteOptions() + { + ProxyOptions = proxyOptions + }; + + return ListReferencesInternal(remote.Url, options); } /// @@ -91,7 +120,13 @@ public virtual IEnumerable ListReferences(Remote remote, CredentialsH Ensure.ArgumentNotNull(remote, "remote"); Ensure.ArgumentNotNull(credentialsProvider, "credentialsProvider"); - return ListReferencesInternal(remote.Url, credentialsProvider, new ProxyOptions()); + var options = new ListRemoteOptions() + { + ProxyOptions = new ProxyOptions(), + CredentialsProvider = credentialsProvider + }; + + return ListReferencesInternal(remote.Url, options); } /// @@ -112,7 +147,32 @@ public virtual IEnumerable ListReferences(Remote remote, CredentialsH Ensure.ArgumentNotNull(remote, "remote"); Ensure.ArgumentNotNull(credentialsProvider, "credentialsProvider"); - return ListReferencesInternal(remote.Url, credentialsProvider, proxyOptions); + var options = new ListRemoteOptions() + { + ProxyOptions = proxyOptions, + CredentialsProvider = credentialsProvider + }; + + return ListReferencesInternal(remote.Url, options); + } + + /// + /// List references in a remote repository. + /// + /// When the remote tips are ahead of the local ones, the retrieved + /// s may point to non existing + /// s in the local repository. In that + /// case, will return null. + /// + /// + /// The url to list from. + /// The options for the remote request. + /// The references in the remote repository. + public virtual IEnumerable ListReferences(string url, ListRemoteOptions options) + { + Ensure.ArgumentNotNull(url, "url"); + + return ListReferencesInternal(url, options); } /// @@ -130,7 +190,12 @@ public virtual IEnumerable ListReferences(string url) { Ensure.ArgumentNotNull(url, "url"); - return ListReferencesInternal(url, null, new ProxyOptions()); + var options = new ListRemoteOptions + { + ProxyOptions = new ProxyOptions() + }; + + return ListReferencesInternal(url, options); } /// @@ -148,8 +213,12 @@ public virtual IEnumerable ListReferences(string url) public virtual IEnumerable ListReferences(string url, ProxyOptions proxyOptions) { Ensure.ArgumentNotNull(url, "url"); + var options = new ListRemoteOptions() + { + ProxyOptions = proxyOptions + }; - return ListReferencesInternal(url, null, proxyOptions); + return ListReferencesInternal(url, options); } /// @@ -169,7 +238,13 @@ public virtual IEnumerable ListReferences(string url, CredentialsHand Ensure.ArgumentNotNull(url, "url"); Ensure.ArgumentNotNull(credentialsProvider, "credentialsProvider"); - return ListReferencesInternal(url, credentialsProvider, new ProxyOptions()); + var options = new ListRemoteOptions() + { + CredentialsProvider = credentialsProvider, + ProxyOptions = new ProxyOptions() + }; + + return ListReferencesInternal(url, options); } /// @@ -190,21 +265,26 @@ public virtual IEnumerable ListReferences(string url, CredentialsHand Ensure.ArgumentNotNull(url, "url"); Ensure.ArgumentNotNull(credentialsProvider, "credentialsProvider"); - return ListReferencesInternal(url, credentialsProvider, new ProxyOptions()); + var options = new ListRemoteOptions() + { + CredentialsProvider = credentialsProvider, + ProxyOptions = new ProxyOptions() + }; + return ListReferencesInternal(url, options); } - private IEnumerable ListReferencesInternal(string url, CredentialsHandler credentialsProvider, ProxyOptions proxyOptions) + private IEnumerable ListReferencesInternal(string url, ListRemoteOptions options) { - proxyOptions ??= new(); + var proxyOptions = options?.ProxyOptions ?? new(); using RemoteHandle remoteHandle = BuildRemoteHandle(repository.Handle, url); using var proxyOptionsWrapper = new GitProxyOptionsWrapper(proxyOptions.CreateGitProxyOptions()); GitRemoteCallbacks gitCallbacks = new GitRemoteCallbacks { version = 1 }; - if (credentialsProvider != null) + if (options != null) { - var callbacks = new RemoteCallbacks(credentialsProvider); + var callbacks = new RemoteCallbacks(options); gitCallbacks = callbacks.GenerateCallbacks(); } diff --git a/LibGit2Sharp/RemoteCallbacks.cs b/LibGit2Sharp/RemoteCallbacks.cs index 178a4077b..45e85eed1 100644 --- a/LibGit2Sharp/RemoteCallbacks.cs +++ b/LibGit2Sharp/RemoteCallbacks.cs @@ -17,6 +17,17 @@ internal RemoteCallbacks(CredentialsHandler credentialsProvider) CredentialsProvider = credentialsProvider; } + internal RemoteCallbacks(ListRemoteOptions listRemoteOptions) + { + if (listRemoteOptions == null) + { + return; + } + + CertificateCheck = listRemoteOptions.CertificateCheck; + CredentialsProvider = listRemoteOptions.CredentialsProvider; + } + internal RemoteCallbacks(PushOptions pushOptions) { if (pushOptions == null) @@ -286,6 +297,16 @@ private int GitCredentialHandler( types |= SupportedCredentialTypes.Default; } + if (credTypes.HasFlag(GitCredentialType.SshKey)) + { + types |= SupportedCredentialTypes.SshKey; + } + + if (credTypes.HasFlag(GitCredentialType.SshMemory)) + { + types |= SupportedCredentialTypes.SshMemory; + } + ptr = IntPtr.Zero; try { diff --git a/LibGit2Sharp/Repository.cs b/LibGit2Sharp/Repository.cs index 9ac5e2424..67bbde997 100644 --- a/LibGit2Sharp/Repository.cs +++ b/LibGit2Sharp/Repository.cs @@ -656,7 +656,7 @@ internal Commit LookupCommit(string committish) /// The references in the remote repository. public static IEnumerable ListRemoteReferences(string url) { - return ListRemoteReferences(url, null, new ProxyOptions()); + return ListRemoteReferences(url, (ListRemoteOptions)null); } /// @@ -667,7 +667,7 @@ public static IEnumerable ListRemoteReferences(string url) /// The references in the remote repository. public static IEnumerable ListRemoteReferences(string url, ProxyOptions proxyOptions) { - return ListRemoteReferences(url, null, proxyOptions); + return ListRemoteReferences(url, new ListRemoteOptions { ProxyOptions = proxyOptions }); } /// @@ -683,7 +683,7 @@ public static IEnumerable ListRemoteReferences(string url, ProxyOptio /// The references in the remote repository. public static IEnumerable ListRemoteReferences(string url, CredentialsHandler credentialsProvider) { - return ListRemoteReferences(url, credentialsProvider, new ProxyOptions()); + return ListRemoteReferences(url, new ListRemoteOptions { CredentialsProvider = credentialsProvider }); } /// @@ -699,23 +699,33 @@ public static IEnumerable ListRemoteReferences(string url, Credential /// Options for connecting through a proxy. /// The references in the remote repository. public static IEnumerable ListRemoteReferences(string url, CredentialsHandler credentialsProvider, ProxyOptions proxyOptions) + { + return ListRemoteReferences(url, new ListRemoteOptions + { + CredentialsProvider = credentialsProvider, + ProxyOptions = proxyOptions, + }); + } + + /// + /// Lists the Remote Repository References. + /// + /// The url to list from. + /// Options for connecting to the remote repository. + /// The references in the remote repository. + public static IEnumerable ListRemoteReferences(string url, ListRemoteOptions listRemoteOptions) { Ensure.ArgumentNotNull(url, "url"); - proxyOptions ??= new(); + listRemoteOptions ??= new ListRemoteOptions(); + var proxyOptions = listRemoteOptions.ProxyOptions ?? new ProxyOptions(); using RepositoryHandle repositoryHandle = Proxy.git_repository_new(); using RemoteHandle remoteHandle = Proxy.git_remote_create_anonymous(repositoryHandle, url); using var proxyOptionsWrapper = new GitProxyOptionsWrapper(proxyOptions.CreateGitProxyOptions()); - var gitCallbacks = new GitRemoteCallbacks { version = 1 }; - - if (credentialsProvider != null) - { - var callbacks = new RemoteCallbacks(credentialsProvider); - gitCallbacks = callbacks.GenerateCallbacks(); - } - + var callbacks = new RemoteCallbacks(listRemoteOptions); + var gitCallbacks = callbacks.GenerateCallbacks(); var gitProxyOptions = proxyOptionsWrapper.Options; Proxy.git_remote_connect(remoteHandle, GitDirection.Fetch, ref gitCallbacks, ref gitProxyOptions); diff --git a/LibGit2Sharp/SshUserKeyCredentials.cs b/LibGit2Sharp/SshUserKeyCredentials.cs new file mode 100644 index 000000000..d7629961e --- /dev/null +++ b/LibGit2Sharp/SshUserKeyCredentials.cs @@ -0,0 +1,56 @@ +using System; +using LibGit2Sharp.Ssh; + +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 (Username == null) + { + throw new InvalidOperationException("SshUserKeyCredentials contains a null Username."); + } + + if (Passphrase == null) + { + throw new InvalidOperationException("SshUserKeyCredentials contains a null Passphrase."); + } + + 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/SshUserKeyMemoryCredentials.cs b/LibGit2Sharp/SshUserKeyMemoryCredentials.cs new file mode 100644 index 000000000..0ca851f9d --- /dev/null +++ b/LibGit2Sharp/SshUserKeyMemoryCredentials.cs @@ -0,0 +1,56 @@ +using System; +using LibGit2Sharp.Ssh; + +namespace LibGit2Sharp +{ + /// + /// Class that holds SSH username with in-memory key credentials for remote repository access. + /// + public sealed class SshUserKeyMemoryCredentials : 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("SshUserKeyMemoryCredentials contains a null Username."); + } + + if (Passphrase == null) + { + throw new InvalidOperationException("SshUserKeyMemoryCredentials contains a null Passphrase."); + } + + if (PrivateKey == null) + { + throw new InvalidOperationException("SshUserKeyMemoryCredentials contains a null PrivateKey."); + } + + return NativeMethods.git_cred_ssh_key_memory_new(out cred, Username, PublicKey, PrivateKey, Passphrase); + } + + /// + /// Username for SSH authentication. + /// + public string Username { get; set; } + + /// + /// Public key for SSH authentication. + /// + public string PublicKey { get; set; } + + /// + /// Private key 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..429684ba2 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 key credentials sourced from files + /// + SshKey = (1 << 2), + + /// + /// SSH key credentials sourced from in-memory keys + /// + SshMemory = (1 << 3), } }