Skip to content

Commit d6faf5b

Browse files
committed
Support libssh2 based callbacks
1 parent cb58177 commit d6faf5b

File tree

9 files changed

+309
-19
lines changed

9 files changed

+309
-19
lines changed

LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks Condition="'$(TargetFrameworks)'==''">net472;net8.0;net9.0</TargetFrameworks>
4+
<TargetFrameworks Condition="'$(TargetFrameworks)'==''">net10.0</TargetFrameworks>
55
</PropertyGroup>
66

77
<ItemGroup>
88
<ProjectReference Include="..\LibGit2Sharp\LibGit2Sharp.csproj" />
9-
<ProjectReference Include="..\NativeLibraryLoadTestApp\x86\NativeLibraryLoadTestApp.x86.csproj" Condition="'$(TargetFramework)' == 'net472'" ReferenceOutputAssembly="false" OutputItemType="TestAppExe" />
10-
<ProjectReference Include="..\NativeLibraryLoadTestApp\x64\NativeLibraryLoadTestApp.x64.csproj" Condition="'$(TargetFramework)' == 'net472'" ReferenceOutputAssembly="false" OutputItemType="TestAppExe" />
9+
<!--<ProjectReference Include="..\NativeLibraryLoadTestApp\x86\NativeLibraryLoadTestApp.x86.csproj" Condition="'$(TargetFramework)' == 'net472'" ReferenceOutputAssembly="false" OutputItemType="TestAppExe" />
10+
<ProjectReference Include="..\NativeLibraryLoadTestApp\x64\NativeLibraryLoadTestApp.x64.csproj" Condition="'$(TargetFramework)' == 'net472'" ReferenceOutputAssembly="false" OutputItemType="TestAppExe" />-->
1111
</ItemGroup>
1212

1313
<ItemGroup>

LibGit2Sharp.Tests/NetworkFixture.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ namespace LibGit2Sharp.Tests
99
public class NetworkFixture : BaseFixture
1010
{
1111
[Theory]
12-
[InlineData("http://github.com/libgit2/TestGitRepository")]
13-
[InlineData("https://github.com/libgit2/TestGitRepository")]
12+
//[InlineData("http://github.com/libgit2/TestGitRepository")]
13+
[InlineData("git@github.com:libgit2/TestGitRepository.git")]
1414
public void CanListRemoteReferences(string url)
1515
{
1616
string remoteName = "testRemote";
@@ -20,7 +20,11 @@ public void CanListRemoteReferences(string url)
2020
using (var repo = new Repository(repoPath))
2121
{
2222
Remote remote = repo.Network.Remotes.Add(remoteName, url);
23-
IList<Reference> references = repo.Network.ListReferences(remote).ToList();
23+
IList<Reference> references = repo.Network.ListReferences(remote, (s, fromUrl, types) =>
24+
{
25+
26+
return null;
27+
}).ToList();
2428

2529

2630
foreach (var reference in references)

LibGit2Sharp/Core/SshExtensions.cs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
2+
using LibGit2Sharp.Core;
3+
using System;
4+
using System.Runtime.InteropServices;
5+
6+
namespace LibGit2Sharp.Ssh
7+
{
8+
9+
internal static class NativeMethods
10+
{
11+
private const string libgit2 = NativeDllName.Name;
12+
13+
[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
14+
internal static extern int git_cred_ssh_key_new(
15+
out IntPtr cred,
16+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string username,
17+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string publickey,
18+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string privatekey,
19+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string passphrase);
20+
21+
[DllImport(libgit2)]
22+
internal static extern int git_cred_ssh_key_memory_new(
23+
out IntPtr cred,
24+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string username,
25+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string publickey,
26+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string privatekey,
27+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string passphrase);
28+
}
29+
30+
/// <summary>
31+
/// Class that holds SSH username with key credentials for remote repository access.
32+
/// </summary>
33+
public sealed class SshUserKeyCredentials : Credentials
34+
{
35+
/// <summary>
36+
/// Callback to acquire a credential object.
37+
/// </summary>
38+
/// <param name="cred">The newly created credential object.</param>
39+
/// <returns>0 for success, &lt; 0 to indicate an error, &gt; 0 to indicate no credential was acquired.</returns>
40+
protected internal override int GitCredentialHandler(out IntPtr cred)
41+
{
42+
if (Username == null)
43+
{
44+
throw new InvalidOperationException("SshUserKeyCredentials contains a null Username.");
45+
}
46+
47+
if (Passphrase == null)
48+
{
49+
throw new InvalidOperationException("SshUserKeyCredentials contains a null Passphrase.");
50+
}
51+
52+
if (PublicKey == null)
53+
{
54+
throw new InvalidOperationException("SshUserKeyCredentials contains a null PublicKey.");
55+
}
56+
57+
if (PrivateKey == null)
58+
{
59+
throw new InvalidOperationException("SshUserKeyCredentials contains a null PrivateKey.");
60+
}
61+
62+
return NativeMethods.git_cred_ssh_key_new(out cred, Username, PublicKey, PrivateKey, Passphrase);
63+
}
64+
65+
/// <summary>
66+
/// Username for SSH authentication.
67+
/// </summary>
68+
public string Username { get; set; }
69+
70+
/// <summary>
71+
/// Public key file location for SSH authentication.
72+
/// </summary>
73+
public string PublicKey { get; set; }
74+
75+
/// <summary>
76+
/// Private key file location for SSH authentication.
77+
/// </summary>
78+
public string PrivateKey { get; set; }
79+
80+
/// <summary>
81+
/// Passphrase for SSH authentication.
82+
/// </summary>
83+
public string Passphrase { get; set; }
84+
}
85+
86+
/// <summary>
87+
/// Class that holds SSH username with in-memory key credentials for remote repository access.
88+
/// </summary>
89+
public sealed class SshUserKeyMemoryCredentials : Credentials
90+
{
91+
/// <summary>
92+
/// Callback to acquire a credential object.
93+
/// </summary>
94+
/// <param name="cred">The newly created credential object.</param>
95+
/// <returns>0 for success, &lt; 0 to indicate an error, &gt; 0 to indicate no credential was acquired.</returns>
96+
protected internal override int GitCredentialHandler(out IntPtr cred)
97+
{
98+
if (Username == null)
99+
{
100+
throw new InvalidOperationException("SshUserKeyMemoryCredentials contains a null Username.");
101+
}
102+
103+
if (Passphrase == null)
104+
{
105+
throw new InvalidOperationException("SshUserKeyMemoryCredentials contains a null Passphrase.");
106+
}
107+
108+
if (PublicKey == null)
109+
{
110+
//throw new InvalidOperationException("SshUserKeyMemoryCredentials contains a null PublicKey.");
111+
}
112+
113+
if (PrivateKey == null)
114+
{
115+
throw new InvalidOperationException("SshUserKeyMemoryCredentials contains a null PrivateKey.");
116+
}
117+
118+
return NativeMethods.git_cred_ssh_key_memory_new(out cred, Username, PublicKey, PrivateKey, Passphrase);
119+
}
120+
121+
/// <summary>
122+
/// Username for SSH authentication.
123+
/// </summary>
124+
public string Username { get; set; }
125+
126+
/// <summary>
127+
/// Public key for SSH authentication.
128+
/// </summary>
129+
public string PublicKey { get; set; }
130+
131+
/// <summary>
132+
/// Private key for SSH authentication.
133+
/// </summary>
134+
public string PrivateKey { get; set; }
135+
136+
/// <summary>
137+
/// Passphrase for SSH authentication.
138+
/// </summary>
139+
public string Passphrase { get; set; }
140+
}
141+
}

LibGit2Sharp/GlobalSettings.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public static class GlobalSettings
2020

2121
private static string nativeLibraryPath;
2222
private static bool nativeLibraryPathLocked;
23-
private static readonly string nativeLibraryDefaultPath = null;
23+
private static readonly string nativeLibraryDefaultPath = "/Users/robert/Development/Sandbox/libgit2/build";
2424

2525
static GlobalSettings()
2626
{

LibGit2Sharp/ListRemoteOptions.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using LibGit2Sharp.Handlers;
2+
3+
namespace LibGit2Sharp;
4+
5+
/// <summary>
6+
/// Options controlling ListRemote behavior.
7+
/// </summary>
8+
public sealed class ListRemoteOptions
9+
{
10+
/// <summary>
11+
/// Handler to generate <see cref="LibGit2Sharp.Credentials"/> for authentication.
12+
/// </summary>
13+
public CredentialsHandler CredentialsProvider { get; set; }
14+
15+
/// <summary>
16+
/// This handler will be called to let the user make a decision on whether to allow
17+
/// the connection to proceed based on the certificate presented by the server.
18+
/// </summary>
19+
public CertificateCheckHandler CertificateCheck { get; set; }
20+
21+
22+
/// <summary>
23+
/// Options for connecting through a proxy.
24+
/// </summary>
25+
public ProxyOptions ProxyOptions { get; set; } = new();
26+
}

LibGit2Sharp/Network.cs

Lines changed: 93 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,34 @@ public virtual IEnumerable<Reference> ListReferences(Remote remote)
5252
{
5353
Ensure.ArgumentNotNull(remote, "remote");
5454

55-
return ListReferencesInternal(remote.Url, null, new ProxyOptions());
55+
var options = new ListRemoteOptions()
56+
{
57+
ProxyOptions = new ProxyOptions()
58+
};
59+
60+
return ListReferencesInternal(remote.Url, options);
61+
}
62+
63+
/// <summary>
64+
/// List references in a <see cref="Remote"/> repository.
65+
/// <para>
66+
/// When the remote tips are ahead of the local ones, the retrieved
67+
/// <see cref="DirectReference"/>s may point to non existing
68+
/// <see cref="GitObject"/>s in the local repository. In that
69+
/// case, <see cref="DirectReference.Target"/> will return <c>null</c>.
70+
/// </para>
71+
/// </summary>
72+
/// <param name="remote">The <see cref="Remote"/> to list from.</param>
73+
/// <param name="options">The options for the remote request.</param>
74+
/// <returns>The references in the <see cref="Remote"/> repository.</returns>
75+
public virtual IEnumerable<Reference> ListReferences(Remote remote, ListRemoteOptions options)
76+
{
77+
Ensure.ArgumentNotNull(remote, "remote");
78+
79+
return ListReferencesInternal(remote.Url, options);
5680
}
5781

82+
5883
/// <summary>
5984
/// List references in a <see cref="Remote"/> repository.
6085
/// <para>
@@ -71,7 +96,12 @@ public virtual IEnumerable<Reference> ListReferences(Remote remote, ProxyOptions
7196
{
7297
Ensure.ArgumentNotNull(remote, "remote");
7398

74-
return ListReferencesInternal(remote.Url, null, proxyOptions);
99+
var options = new ListRemoteOptions()
100+
{
101+
ProxyOptions = proxyOptions
102+
};
103+
104+
return ListReferencesInternal(remote.Url, options);
75105
}
76106

77107
/// <summary>
@@ -91,7 +121,13 @@ public virtual IEnumerable<Reference> ListReferences(Remote remote, CredentialsH
91121
Ensure.ArgumentNotNull(remote, "remote");
92122
Ensure.ArgumentNotNull(credentialsProvider, "credentialsProvider");
93123

94-
return ListReferencesInternal(remote.Url, credentialsProvider, new ProxyOptions());
124+
var options = new ListRemoteOptions()
125+
{
126+
ProxyOptions = new ProxyOptions(),
127+
CredentialsProvider = credentialsProvider
128+
};
129+
130+
return ListReferencesInternal(remote.Url, options);
95131
}
96132

97133
/// <summary>
@@ -112,7 +148,32 @@ public virtual IEnumerable<Reference> ListReferences(Remote remote, CredentialsH
112148
Ensure.ArgumentNotNull(remote, "remote");
113149
Ensure.ArgumentNotNull(credentialsProvider, "credentialsProvider");
114150

115-
return ListReferencesInternal(remote.Url, credentialsProvider, proxyOptions);
151+
var options = new ListRemoteOptions()
152+
{
153+
ProxyOptions = proxyOptions,
154+
CredentialsProvider = credentialsProvider
155+
};
156+
157+
return ListReferencesInternal(remote.Url, options);
158+
}
159+
160+
/// <summary>
161+
/// List references in a remote repository.
162+
/// <para>
163+
/// When the remote tips are ahead of the local ones, the retrieved
164+
/// <see cref="DirectReference"/>s may point to non existing
165+
/// <see cref="GitObject"/>s in the local repository. In that
166+
/// case, <see cref="DirectReference.Target"/> will return <c>null</c>.
167+
/// </para>
168+
/// </summary>
169+
/// <param name="url">The url to list from.</param>
170+
/// <param name="options">The options for the remote request.</param>
171+
/// <returns>The references in the remote repository.</returns>
172+
public virtual IEnumerable<Reference> ListReferences(string url, ListRemoteOptions options)
173+
{
174+
Ensure.ArgumentNotNull(url, "url");
175+
176+
return ListReferencesInternal(url, options);
116177
}
117178

118179
/// <summary>
@@ -130,7 +191,12 @@ public virtual IEnumerable<Reference> ListReferences(string url)
130191
{
131192
Ensure.ArgumentNotNull(url, "url");
132193

133-
return ListReferencesInternal(url, null, new ProxyOptions());
194+
var options = new ListRemoteOptions()
195+
{
196+
ProxyOptions = new ProxyOptions()
197+
};
198+
199+
return ListReferencesInternal(url, options);
134200
}
135201

136202
/// <summary>
@@ -148,8 +214,12 @@ public virtual IEnumerable<Reference> ListReferences(string url)
148214
public virtual IEnumerable<Reference> ListReferences(string url, ProxyOptions proxyOptions)
149215
{
150216
Ensure.ArgumentNotNull(url, "url");
217+
var options = new ListRemoteOptions()
218+
{
219+
ProxyOptions = proxyOptions
220+
};
151221

152-
return ListReferencesInternal(url, null, proxyOptions);
222+
return ListReferencesInternal(url, options);
153223
}
154224

155225
/// <summary>
@@ -169,7 +239,13 @@ public virtual IEnumerable<Reference> ListReferences(string url, CredentialsHand
169239
Ensure.ArgumentNotNull(url, "url");
170240
Ensure.ArgumentNotNull(credentialsProvider, "credentialsProvider");
171241

172-
return ListReferencesInternal(url, credentialsProvider, new ProxyOptions());
242+
var options = new ListRemoteOptions()
243+
{
244+
CredentialsProvider = credentialsProvider,
245+
ProxyOptions = new ProxyOptions()
246+
};
247+
248+
return ListReferencesInternal(url, options);
173249
}
174250

175251
/// <summary>
@@ -190,21 +266,26 @@ public virtual IEnumerable<Reference> ListReferences(string url, CredentialsHand
190266
Ensure.ArgumentNotNull(url, "url");
191267
Ensure.ArgumentNotNull(credentialsProvider, "credentialsProvider");
192268

193-
return ListReferencesInternal(url, credentialsProvider, new ProxyOptions());
269+
var options = new ListRemoteOptions()
270+
{
271+
CredentialsProvider = credentialsProvider,
272+
ProxyOptions = new ProxyOptions()
273+
};
274+
return ListReferencesInternal(url, options);
194275
}
195276

196-
private IEnumerable<Reference> ListReferencesInternal(string url, CredentialsHandler credentialsProvider, ProxyOptions proxyOptions)
277+
private IEnumerable<Reference> ListReferencesInternal(string url, ListRemoteOptions options)
197278
{
198-
proxyOptions ??= new();
279+
var proxyOptions = options?.ProxyOptions ?? new();
199280

200281
using RemoteHandle remoteHandle = BuildRemoteHandle(repository.Handle, url);
201282
using var proxyOptionsWrapper = new GitProxyOptionsWrapper(proxyOptions.CreateGitProxyOptions());
202283

203284
GitRemoteCallbacks gitCallbacks = new GitRemoteCallbacks { version = 1 };
204285

205-
if (credentialsProvider != null)
286+
if (options != null)
206287
{
207-
var callbacks = new RemoteCallbacks(credentialsProvider);
288+
var callbacks = new RemoteCallbacks(options);
208289
gitCallbacks = callbacks.GenerateCallbacks();
209290
}
210291

0 commit comments

Comments
 (0)