Skip to content

Commit 2f13063

Browse files
committed
Introduce SSH functionality
Added SshAgentCredentials for querying ssh-agent, SshUserKeyCredentials for authenticating with a given ssh key-pair. Introduced UsernameQueryCredentials which returns the supported credential types. Authentication exceptions are now translated from libgit2.
1 parent 7202df7 commit 2f13063

10 files changed

+235
-1
lines changed
+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System;
2+
using System.Runtime.Serialization;
3+
using LibGit2Sharp.Core;
4+
5+
namespace LibGit2Sharp
6+
{
7+
/// <summary>
8+
/// The exception that is thrown when an operation which requires an
9+
/// authentication fails.
10+
/// </summary>
11+
[Serializable]
12+
public class AuthenticationException : LibGit2SharpException
13+
{
14+
/// <summary>
15+
/// Initializes a new instance of the <see cref="LibGit2Sharp.AuthenticationException"/> class.
16+
/// </summary>
17+
public AuthenticationException()
18+
{
19+
}
20+
21+
/// <summary>
22+
/// Initializes a new instance of the <see cref="LibGit2Sharp.AuthenticationException"/> class with a specified error message.
23+
/// </summary>
24+
/// <param name="message">A message that describes the error.</param>
25+
public AuthenticationException(string message)
26+
: base(message)
27+
{
28+
}
29+
30+
/// <summary>
31+
/// Initializes a new instance of the <see cref="LibGit2Sharp.AuthenticationException"/> class with a specified error message and a reference to the inner exception that is the cause of this exception.
32+
/// </summary>
33+
/// <param name="message">The error message that explains the reason for the exception.</param>
34+
/// <param name="innerException">The exception that is the cause of the current exception. If the <paramref name="innerException"/> parameter is not a null reference, the current exception is raised in a catch block that handles the inner exception.</param>
35+
public AuthenticationException(string message, Exception innerException)
36+
: base(message, innerException)
37+
{
38+
}
39+
40+
/// <summary>
41+
/// Initializes a new instance of the <see cref="LibGit2Sharp.AuthenticationException"/> class with a serialized data.
42+
/// </summary>
43+
/// <param name="info">The <see cref="SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
44+
/// <param name="context">The <see cref="StreamingContext"/> that contains contextual information about the source or destination.</param>
45+
protected AuthenticationException(SerializationInfo info, StreamingContext context)
46+
: base(info, context)
47+
{
48+
}
49+
50+
internal AuthenticationException(string message, GitErrorCode code, GitErrorCategory category)
51+
: base(message, code, category)
52+
{
53+
}
54+
}
55+
}

LibGit2Sharp/Core/Ensure.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ private static readonly Dictionary<GitErrorCode, Func<string, GitErrorCode, GitE
100100
{ GitErrorCode.Conflict, (m, r, c) => new CheckoutConflictException(m, r, c) },
101101
{ GitErrorCode.LockedFile, (m, r, c) => new LockedFileException(m, r, c) },
102102
{ GitErrorCode.NotFound, (m, r, c) => new NotFoundException(m, r, c) },
103-
{ GitErrorCode.Peel, (m, r, c) => new PeelException(m, r, c) },
103+
{ GitErrorCode.Peel, (m, r, c) => new PeelException(m, r, c) },
104+
{ GitErrorCode.Auth, (m, r, c) => new AuthenticationException(m, r, c) },
104105
};
105106

106107
private static void HandleError(int result)

LibGit2Sharp/Core/GitCredentialType.cs

+5
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,10 @@ internal enum GitCredentialType
3232
/// TODO
3333
/// </summary>
3434
SshInteractive = (1 << 4),
35+
36+
/// <summary>
37+
/// Username only information.
38+
/// </summary>
39+
UsernameQuery = (1 << 5),
3540
}
3641
}

LibGit2Sharp/Core/NativeMethods.cs

+18
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,24 @@ internal static extern int git_cred_userpass_plaintext_new(
406406
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof (StrictUtf8Marshaler))] string username,
407407
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof (StrictUtf8Marshaler))] string password);
408408

409+
[DllImport(libgit2)]
410+
internal static extern int git_cred_ssh_key_new(
411+
out IntPtr cred,
412+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string username,
413+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string publickey,
414+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string privatekey,
415+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string passphrase);
416+
417+
[DllImport(libgit2)]
418+
internal static extern int git_cred_ssh_key_from_agent(
419+
out IntPtr cred,
420+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string username);
421+
422+
[DllImport(libgit2)]
423+
internal static extern int git_cred_username_new(
424+
out IntPtr cred,
425+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string username);
426+
409427
[DllImport(libgit2)]
410428
internal static extern int git_describe_commit(
411429
out DescribeResultSafeHandle describe,

LibGit2Sharp/LibGit2Sharp.csproj

+4
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
<ItemGroup>
4747
<Compile Include="AmbiguousSpecificationException.cs" />
4848
<Compile Include="ArchiverBase.cs" />
49+
<Compile Include="AuthenticationException.cs" />
4950
<Compile Include="BareRepositoryException.cs" />
5051
<Compile Include="BlameHunkCollection.cs" />
5152
<Compile Include="BlameHunk.cs" />
@@ -150,6 +151,8 @@
150151
<Compile Include="RevertResult.cs" />
151152
<Compile Include="RevertOptions.cs" />
152153
<Compile Include="SecureUsernamePasswordCredentials.cs" />
154+
<Compile Include="SshAgentCredentials.cs" />
155+
<Compile Include="SshUserKeyCredentials.cs" />
153156
<Compile Include="StageOptions.cs" />
154157
<Compile Include="StatusOptions.cs" />
155158
<Compile Include="SimilarityOptions.cs" />
@@ -352,6 +355,7 @@
352355
<Compile Include="TreeEntryDefinition.cs" />
353356
<Compile Include="UserCanceledException.cs" />
354357
<Compile Include="UsernamePasswordCredentials.cs" />
358+
<Compile Include="UsernameQueryCredentials.cs" />
355359
<Compile Include="Version.cs" />
356360
<Compile Include="VoidReference.cs" />
357361
<Compile Include="Core\RawContentStream.cs" />

LibGit2Sharp/RemoteCallbacks.cs

+8
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,14 @@ private int GitCredentialHandler(out IntPtr ptr, IntPtr cUrl, IntPtr usernameFro
256256
{
257257
types |= SupportedCredentialTypes.Default;
258258
}
259+
if (credTypes.HasFlag(GitCredentialType.SshKey))
260+
{
261+
types |= SupportedCredentialTypes.Ssh;
262+
}
263+
if (credTypes.HasFlag(GitCredentialType.UsernameQuery))
264+
{
265+
types |= SupportedCredentialTypes.UsernameQuery;
266+
}
259267

260268
var cred = CredentialsProvider(url, username, types);
261269

LibGit2Sharp/SshAgentCredentials.cs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
using LibGit2Sharp.Core;
3+
4+
namespace LibGit2Sharp
5+
{
6+
/// <summary>
7+
/// Class that holds SSH agent credentials for remote repository access.
8+
/// </summary>
9+
public sealed class SshAgentCredentials : Credentials
10+
{
11+
/// <summary>
12+
/// Callback to acquire a credential object.
13+
/// </summary>
14+
/// <param name="cred">The newly created credential object.</param>
15+
/// <returns>0 for success, &lt; 0 to indicate an error, &gt; 0 to indicate no credential was acquired.</returns>
16+
protected internal override int GitCredentialHandler(out IntPtr cred)
17+
{
18+
if (!GlobalSettings.Version.Features.HasFlag(BuiltInFeatures.Ssh))
19+
{
20+
throw new InvalidOperationException("LibGit2 was not built with SSH support.");
21+
}
22+
23+
if (Username == null)
24+
{
25+
throw new InvalidOperationException("SshAgentCredentials contains a null Username.");
26+
}
27+
28+
return NativeMethods.git_cred_ssh_key_from_agent(out cred, Username);
29+
}
30+
31+
/// <summary>
32+
/// Username for SSH authentication.
33+
/// </summary>
34+
public string Username { get; set; }
35+
}
36+
}

LibGit2Sharp/SshUserKeyCredentials.cs

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using System;
2+
using LibGit2Sharp.Core;
3+
4+
namespace LibGit2Sharp
5+
{
6+
/// <summary>
7+
/// Class that holds SSH username with key credentials for remote repository access.
8+
/// </summary>
9+
public sealed class SshUserKeyCredentials : Credentials
10+
{
11+
/// <summary>
12+
/// Callback to acquire a credential object.
13+
/// </summary>
14+
/// <param name="cred">The newly created credential object.</param>
15+
/// <returns>0 for success, &lt; 0 to indicate an error, &gt; 0 to indicate no credential was acquired.</returns>
16+
protected internal override int GitCredentialHandler(out IntPtr cred)
17+
{
18+
if (!GlobalSettings.Version.Features.HasFlag(BuiltInFeatures.Ssh))
19+
{
20+
throw new InvalidOperationException("LibGit2 was not built with SSH support.");
21+
}
22+
23+
if (Username == null)
24+
{
25+
throw new InvalidOperationException("SshUserKeyCredentials contains a null Username.");
26+
}
27+
28+
if (Passphrase == null)
29+
{
30+
throw new InvalidOperationException("SshUserKeyCredentials contains a null Passphrase.");
31+
}
32+
33+
if (PublicKey == null)
34+
{
35+
throw new InvalidOperationException("SshUserKeyCredentials contains a null PublicKey.");
36+
}
37+
38+
if (PrivateKey == null)
39+
{
40+
throw new InvalidOperationException("SshUserKeyCredentials contains a null PrivateKey.");
41+
}
42+
43+
return NativeMethods.git_cred_ssh_key_new(out cred, Username, PublicKey, PrivateKey, Passphrase);
44+
}
45+
46+
/// <summary>
47+
/// Username for SSH authentication.
48+
/// </summary>
49+
public string Username { get; set; }
50+
51+
/// <summary>
52+
/// Public key file location for SSH authentication.
53+
/// </summary>
54+
public string PublicKey { get; set; }
55+
56+
/// <summary>
57+
/// Private key file location for SSH authentication.
58+
/// </summary>
59+
public string PrivateKey { get; set; }
60+
61+
/// <summary>
62+
/// Passphrase for SSH authentication.
63+
/// </summary>
64+
public string Passphrase { get; set; }
65+
}
66+
}

LibGit2Sharp/SupportedCredentialTypes.cs

+10
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,15 @@ public enum SupportedCredentialTypes
1818
/// Ask Windows to provide its default credentials for the current user (e.g. NTLM)
1919
/// </summary>
2020
Default = (1 << 1),
21+
22+
/// <summary>
23+
/// SSH with username and public/private keys. (SshUserKeyCredentials, SshAgentCredentials).
24+
/// </summary>
25+
Ssh = (1 << 2),
26+
27+
/// <summary>
28+
/// Queries the server with the given username, then later returns the supported credential types.
29+
/// </summary>
30+
UsernameQuery = (1 << 3),
2131
}
2232
}
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
using LibGit2Sharp.Core;
3+
4+
namespace LibGit2Sharp
5+
{
6+
/// <summary>
7+
/// Class that holds username query credentials for remote repository access.
8+
/// </summary>
9+
public sealed class UsernameQueryCredentials : Credentials
10+
{
11+
/// <summary>
12+
/// Callback to acquire a credential object.
13+
/// </summary>
14+
/// <param name="cred">The newly created credential object.</param>
15+
/// <returns>0 for success, &lt; 0 to indicate an error, &gt; 0 to indicate no credential was acquired.</returns>
16+
protected internal override int GitCredentialHandler(out IntPtr cred)
17+
{
18+
if (Username == null)
19+
{
20+
throw new InvalidOperationException("UsernameQueryCredentials contains a null Username.");
21+
}
22+
23+
return NativeMethods.git_cred_username_new(out cred, Username);
24+
}
25+
26+
/// <summary>
27+
/// Username for querying the server for supported authentication.
28+
/// </summary>
29+
public string Username { get; set; }
30+
}
31+
}

0 commit comments

Comments
 (0)