diff --git a/LibGit2Sharp.Tests/TestHelpers/Constants.cs b/LibGit2Sharp.Tests/TestHelpers/Constants.cs index 450746ff6..5a389136c 100644 --- a/LibGit2Sharp.Tests/TestHelpers/Constants.cs +++ b/LibGit2Sharp.Tests/TestHelpers/Constants.cs @@ -22,6 +22,9 @@ public static class Constants // ... return new UsernamePasswordCredentials { Username = "username", Password = "swordfish" }; // // Or: + // ... return new SecureUsernamePasswordCredentials() { Username = "username", Password = StringToSecureString("swordfish") }; + // + // Or: // public const string PrivateRepoUrl = "https://tfs.contoso.com/tfs/DefaultCollection/project/_git/project"; // ... return new DefaultCredentials(); @@ -68,5 +71,21 @@ public static string BuildPath() Trace.TraceInformation("Test working directory set to '{0}'", testWorkingDirectory); return testWorkingDirectory; } + + // To help with creating secure strings to test with. + private static System.Security.SecureString StringToSecureString(string str) + { + var chars = str.ToCharArray(); + + var secure = new System.Security.SecureString(); + for (var i = 0; i < chars.Length; i++) + { + secure.AppendChar(chars[i]); + } + + secure.MakeReadOnly(); + + return secure; + } } } diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index 60b352522..856d2433b 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -143,6 +143,7 @@ + diff --git a/LibGit2Sharp/SecureUsernamePasswordCredentials.cs b/LibGit2Sharp/SecureUsernamePasswordCredentials.cs new file mode 100644 index 000000000..5e1d49314 --- /dev/null +++ b/LibGit2Sharp/SecureUsernamePasswordCredentials.cs @@ -0,0 +1,50 @@ +using System; +using LibGit2Sharp.Core; +using System.Security; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp +{ + /// + /// Class that uses to hold username and password credentials for remote repository access. + /// + public sealed class SecureUsernamePasswordCredentials : 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 || Password == null) + { + throw new InvalidOperationException("UsernamePasswordCredentials contains a null Username or Password."); + } + + IntPtr passwordPtr = IntPtr.Zero; + + try + { + passwordPtr = Marshal.SecureStringToGlobalAllocUnicode(Password); + + return NativeMethods.git_cred_userpass_plaintext_new(out cred, Username, Marshal.PtrToStringUni(passwordPtr)); + } + finally + { + Marshal.ZeroFreeGlobalAllocUnicode(passwordPtr); + } + + } + + /// + /// Username for username/password authentication (as in HTTP basic auth). + /// + public string Username { get; set; } + + /// + /// Password for username/password authentication (as in HTTP basic auth). + /// + public SecureString Password { get; set; } + } +}