private void CreateTargetUriTestWithPath(InputArg input) { using (var memory = new MemoryStream()) using (var writer = new StreamWriter(memory)) { writer.Write(input.ToString()); writer.Flush(); memory.Seek(0, SeekOrigin.Begin); var oparg = new OperationArguments.Impl(memory); oparg.UseHttpPath = true; Assert.NotNull(oparg); Assert.Equal(input.Protocol ?? string.Empty, oparg.QueryProtocol); Assert.Equal(input.Host ?? string.Empty, oparg.QueryHost); Assert.Equal(input.Path, oparg.QueryPath); Assert.Equal(input.Username, oparg.CredUsername); Assert.Equal(input.Password, oparg.CredPassword); // file or unc paths are treated specially if (oparg.QueryUri.Scheme != System.Uri.UriSchemeFile) { Assert.Equal("/" + input.Path, oparg.QueryUri.AbsolutePath); } } }
public void SpecialCharacters() { const string input = "protocol=https\n" + "host=example.visualstudio.com\n" + "path=path\n" + "username=userNamể\n" + "password=ḭncorrect\n"; OperationArguments cut; using (var memory = new MemoryStream()) using (var writer = new StreamWriter(memory)) { writer.Write(input); writer.Flush(); memory.Seek(0, SeekOrigin.Begin); cut = new OperationArguments.Impl(memory); } Assert.Equal("https", cut.QueryProtocol); Assert.Equal("example.visualstudio.com", cut.QueryHost); Assert.Equal("https://example.visualstudio.com/", cut.TargetUri.ToString()); Assert.Equal("path", cut.QueryPath); Assert.Equal("userNamể", cut.CredUsername); Assert.Equal("ḭncorrect", cut.CredPassword); var expected = ReadLines(input); var actual = ReadLines(cut.ToString()); Assert.Equal(expected, actual); }
internal void Get() { // parse the operations arguments from stdin (this is how git sends commands) // see: https://www.kernel.org/pub/software/scm/git/docs/technical/api-credentials.html // see: https://www.kernel.org/pub/software/scm/git/docs/git-credential.html using (var stdin = Console.OpenStandardInput()) { OperationArguments operationArguments = new OperationArguments.Impl(stdin); LoadOperationArguments(operationArguments); EnableTraceLogging(operationArguments); Credential credentials; if ((credentials = QueryCredentials(operationArguments)) == null) { Exit(-1, "Logon failed, use ctrl+c to cancel basic credential prompt."); } else { using (var stdout = Console.OpenStandardOutput()) { operationArguments.WriteToStream(stdout); } } } }
internal void Store() { // parse the operations arguments from stdin (this is how git sends commands) // see: https://www.kernel.org/pub/software/scm/git/docs/technical/api-credentials.html // see: https://www.kernel.org/pub/software/scm/git/docs/git-credential.html using (var stdin = Console.OpenStandardInput()) { OperationArguments operationArguments = new OperationArguments.Impl(stdin); Debug.Assert(operationArguments != null, "The operationArguments is null"); Debug.Assert(operationArguments.CredUsername != null, "The operaionArgument.Username is null"); Debug.Assert(operationArguments.TargetUri != null, "The operationArgument.TargetUri is null"); LoadOperationArguments(operationArguments); EnableTraceLogging(operationArguments); Credential credentials = new Credential(operationArguments.CredUsername, operationArguments.CredPassword); var task = Task.Run(async() => { return(await CreateAuthentication(operationArguments)); }); BaseAuthentication authentication = task.Result; switch (operationArguments.Authority) { default: case AuthorityType.Basic: Git.Trace.WriteLine($"storing basic credentials for '{operationArguments.TargetUri}'."); break; case AuthorityType.AzureDirectory: case AuthorityType.MicrosoftAccount: Git.Trace.WriteLine($"storing VSTS credentials for '{operationArguments.TargetUri}'."); break; case AuthorityType.GitHub: Git.Trace.WriteLine($"storing GitHub credentials for '{operationArguments.TargetUri}'."); break; case AuthorityType.Ntlm: Git.Trace.WriteLine($"storing NTLM credentials for '{operationArguments.TargetUri}'."); break; } authentication.SetCredentials(operationArguments.TargetUri, credentials); } }
private static void Get() { // parse the operations arguments from stdin (this is how git sends commands) // see: https://www.kernel.org/pub/software/scm/git/docs/technical/api-credentials.html // see: https://www.kernel.org/pub/software/scm/git/docs/git-credential.html using (var stdin = Console.OpenStandardInput()) { OperationArguments operationArguments = new OperationArguments.Impl(stdin); LoadOperationArguments(operationArguments); EnableTraceLogging(operationArguments); QueryCredentials(operationArguments); using (var stdout = Console.OpenStandardOutput()) { operationArguments.WriteToStream(stdout); } } }
internal void Erase() { // parse the operations arguments from stdin (this is how git sends commands) // see: https://www.kernel.org/pub/software/scm/git/docs/technical/api-credentials.html // see: https://www.kernel.org/pub/software/scm/git/docs/git-credential.html using (var stdin = Console.OpenStandardInput()) { OperationArguments operationArguments = new OperationArguments.Impl(stdin); Debug.Assert(operationArguments != null, "The operationArguments is null"); Debug.Assert(operationArguments.TargetUri != null, "The operationArgument.TargetUri is null"); LoadOperationArguments(operationArguments); EnableTraceLogging(operationArguments); if (operationArguments.PreserveCredentials) { Git.Trace.WriteLine($"{ConfigPreserveCredentialsKey} = true, canceling erase request."); return; } DeleteCredentials(operationArguments); } }
private static void Askpass(string[] args) { if (args == null || args.Length == 0) { throw new ArgumentException("Arguments cannot be empty."); } Gui.UserPromptKind promptKind = Gui.UserPromptKind.SshPassphrase; Match match; if ((match = AskPasswordRegex.Match(args[0])).Success) { promptKind = Gui.UserPromptKind.CredentialsPassword; } else if ((match = AskPassphraseRegex.Match(args[0])).Success) { promptKind = Gui.UserPromptKind.SshPassphrase; } if (match.Success) { Git.Trace.WriteLine("querying for passphrase key."); if (match.Groups.Count < 2) { throw new ArgumentException("Unable to understand command."); } // string request = match.Groups[0].Value; string resource = match.Groups[1].Value; Git.Trace.WriteLine($"open dialog for '{resource}'."); System.Windows.Application application = new System.Windows.Application(); Gui.UserPromptDialog prompt = new Gui.UserPromptDialog(promptKind, resource); application.Run(prompt); if (!prompt.Failed && !string.IsNullOrEmpty(prompt.Response)) { string passphase = prompt.Response; Git.Trace.WriteLine("passphase acquired."); Console.Out.Write(passphase + "\n"); return; } Die("failed to interactively acquire credentials."); } if ((match = AskCredentialRegex.Match(args[0])).Success) { Git.Trace.WriteLine("querying for basic credentials."); if (match.Groups.Count < 3) { throw new ArgumentException("Unable to understand command."); } string seeking = match.Groups[1].Value; string targetUrl = match.Groups[2].Value; // Since we're looking for HTTP(s) credentials, we can use NetFx `Uri` class. if (Uri.TryCreate(targetUrl, UriKind.Absolute, out Uri targetUri)) { Git.Trace.WriteLine($"success parsing URL, targetUri = '{targetUri}'."); if (TryParseUrlCredentials(targetUrl, out string username, out string password)) { if (password != null && seeking.Equals("Password", StringComparison.OrdinalIgnoreCase)) { Console.Out.Write(password + '\n'); return; } // print the username if it sought if (seeking.Equals("Username", StringComparison.OrdinalIgnoreCase)) { Console.Out.Write(username + '\n'); return; } } // create a target Url with the credential portion stripped, because Git doesn't // report hosts with credentials targetUrl = targetUri.Scheme + "://"; // Add the username@ portion of the url if it exists if (username != null) { targetUrl += Uri.EscapeDataString(username); targetUrl += '@'; } targetUrl += targetUri.Host; // retain the port if specified if (!targetUri.IsDefaultPort) { targetUrl += $":{targetUri.Port}"; } // retain the path if specified if (!string.IsNullOrWhiteSpace(targetUri.LocalPath)) { targetUrl += targetUri.LocalPath; } if (Uri.TryCreate(targetUrl, UriKind.Absolute, out targetUri)) { Git.Trace.WriteLine($"success parsing URL, targetUri = '{targetUri}'."); OperationArguments operationArguments = new OperationArguments.Impl(targetUri); operationArguments.SetCredentials(username, password); // load up the operation arguments, enable tracing, and query for credentials LoadOperationArguments(operationArguments); EnableTraceLogging(operationArguments); Credential credentials; if ((credentials = QueryCredentials(operationArguments)) != null) { if (seeking.Equals("Username", StringComparison.OrdinalIgnoreCase)) { Git.Trace.WriteLine($"username for '{targetUrl}' asked for and found."); Console.Out.Write(credentials.Username + '\n'); return; } if (seeking.Equals("Password", StringComparison.OrdinalIgnoreCase)) { Git.Trace.WriteLine($"password for '{targetUrl}' asked for and found."); Console.Out.Write(credentials.Password + '\n'); return; } } else { Git.Trace.WriteLine($"user cancelled credential dialog."); return; } } else { Git.Trace.WriteLine("error: unable to parse target URL."); } } else { Git.Trace.WriteLine("error: unable to parse supplied URL."); } Die($"failed to detect {seeking} in target URL."); } if ((match = AskAuthenticityRegex.Match(args[0])).Success) { string host = match.Groups[1].Value; string fingerprint = match.Groups[2].Value; Git.Trace.WriteLine($"requesting authorization to add {host} ({fingerprint}) to known hosts."); System.Windows.Application application = new System.Windows.Application(); Gui.UserPromptDialog prompt = new Gui.UserPromptDialog(host, fingerprint); application.Run(prompt); if (prompt.Failed) { Git.Trace.WriteLine("denied authorization of host."); Console.Out.Write("no\n"); } else { Git.Trace.WriteLine("approved authorization of host."); Console.Out.Write("yes\n"); } return; } Die("failed to acquire credentials."); }
internal void Clear() { var args = Environment.GetCommandLineArgs(); string url = null; bool forced = false; if (args.Length <= 2) { if (!StandardInputIsTty) { Git.Trace.WriteLine("standard input is not TTY, abandoning prompt."); return; } Git.Trace.WriteLine("prompting user for url."); WriteLine(" Target Url:"); url = Console.In.ReadLine(); } else { url = args[2]; if (args.Length > 3) { bool.TryParse(args[3], out forced); } } Uri uri; if (Uri.TryCreate(url, UriKind.Absolute, out uri)) { Git.Trace.WriteLine($"converted '{url}' to '{uri.AbsoluteUri}'."); OperationArguments operationArguments = new OperationArguments.Impl(uri); LoadOperationArguments(operationArguments); EnableTraceLogging(operationArguments); if (operationArguments.PreserveCredentials && !forced) { Git.Trace.WriteLine("attempting to delete preserved credentials without force, prompting user for interactivity."); if (!StandardInputIsTty || !StandardErrorIsTty) { Git.Trace.WriteLine("standard input is not TTY, abandoning prompt."); return; } WriteLine(" credentials are protected by preserve flag, clear anyways? [Y]es, [N]o."); ConsoleKeyInfo key; while ((key = ReadKey(true)).Key != ConsoleKey.Escape) { if (key.KeyChar == 'N' || key.KeyChar == 'n') { return; } if (key.KeyChar == 'Y' || key.KeyChar == 'y') { break; } } } DeleteCredentials(operationArguments); } else { Git.Trace.WriteLine($"unable to parse input '{url}'."); } }
internal void Delete() { string[] args = Environment.GetCommandLineArgs(); if (args.Length < 3) { goto error_parse; } string url = args[2]; Uri uri = null; if (Uri.IsWellFormedUriString(url, UriKind.Absolute)) { if (!Uri.TryCreate(url, UriKind.Absolute, out uri)) { goto error_parse; } } else { url = string.Format("{0}://{1}", Uri.UriSchemeHttps, url); if (!Uri.TryCreate(url, UriKind.Absolute, out uri)) { goto error_parse; } } using (var stdin = Console.OpenStandardInput()) { OperationArguments operationArguments = new OperationArguments.Impl(stdin); operationArguments.QueryUri = uri; LoadOperationArguments(operationArguments); var task = Task.Run(async() => { return(await CreateAuthentication(operationArguments)); }); BaseAuthentication authentication = task.Result; switch (operationArguments.Authority) { default: case AuthorityType.Basic: Git.Trace.WriteLine($"deleting basic credentials for '{operationArguments.TargetUri}'."); break; case AuthorityType.AzureDirectory: case AuthorityType.MicrosoftAccount: Git.Trace.WriteLine($"deleting VSTS credentials for '{operationArguments.TargetUri}'."); break; case AuthorityType.GitHub: Git.Trace.WriteLine($"deleting GitHub credentials for '{operationArguments.TargetUri}'."); break; case AuthorityType.Ntlm: Git.Trace.WriteLine($"deleting NTLM credentials for '{operationArguments.TargetUri}'."); break; case AuthorityType.Bitbucket: Git.Trace.WriteLine($"deleting Bitbucket credentials for '{operationArguments.CredUsername}@{operationArguments.TargetUri}'."); break; } authentication.DeleteCredentials(operationArguments.TargetUri, operationArguments.CredUsername); } return; error_parse: Die("Unable to parse target URI."); }
internal void Config() { string[] args = Environment.GetCommandLineArgs(); // Attempt to parse a target URI from the command line arguments. if (args.Length < 3 || !Uri.TryCreate(args[2], UriKind.Absolute, out Uri targetUri)) { targetUri = new Uri("file://localhost"); } // Create operation arguments, and load configuration data. OperationArguments operationArguments = new OperationArguments.Impl(targetUri); LoadOperationArguments(operationArguments); EnableTraceLogging(operationArguments); // Create a set of irrelevant environment variable entries. var irrelevantEntries = new HashSet <string>(StringComparer.OrdinalIgnoreCase) { "ToastSettings" }; // Write out the environment variables. WriteLine("Environment Variables:"); foreach (var entry in operationArguments.EnvironmentVariables) { // Skip well-known, irrelevant entries. if (irrelevantEntries.Contains(entry.Key)) { continue; } WriteLine($" {entry.Key} = {entry.Value}"); } WriteLine(); // Write out the Git configuration. WriteLine("Git Configuration:"); foreach (var entry in operationArguments.GitConfiguration) { WriteLine($" [{entry.Level}] {entry.Key} = {entry.Value}"); } WriteLine(); // Write out the effective settings for GCM. WriteLine($"Effective Manager Configuration for {operationArguments.QueryUri.ToString()}:"); WriteLine($" Executable = {AssemblyTitle} v{Version.ToString(4)} ({ExecutablePath})"); WriteLine($" Authority = {operationArguments.Authority}"); WriteLine($" CustomNamespace = {operationArguments.CustomNamespace}"); WriteLine($" Interactivity = {operationArguments.Interactivity}"); WriteLine($" PreserveCredentials = {operationArguments.PreserveCredentials}"); WriteLine($" QueryUri = {operationArguments.QueryUri}"); WriteLine($" TargetUri = {operationArguments.TargetUri}"); WriteLine($" TokenDuration = {operationArguments.TokenDuration}"); WriteLine($" UseConfigLocal = {operationArguments.UseConfigLocal}"); WriteLine($" UseConfigSystem = {operationArguments.UseConfigSystem}"); WriteLine($" UseHttpPath = {operationArguments.UseHttpPath}"); WriteLine($" UseModalUi = {operationArguments.UseModalUi}"); WriteLine($" ValidateCredentials = {operationArguments.ValidateCredentials}"); WriteLine($" WriteLog = {operationArguments.WriteLog}"); }
private static void Askpass(string[] args) { if (args == null || args.Length == 0) { throw new ArgumentException("Arguments cannot be empty."); } Gui.UserPromptKind promptKind = Gui.UserPromptKind.SshPassphrase; Match match; if ((match = AskPasswordRegex.Match(args[0])).Success) { promptKind = Gui.UserPromptKind.CredentialsPassword; } else if ((match = AskPassphraseRegex.Match(args[0])).Success) { promptKind = Gui.UserPromptKind.SshPassphrase; } if (match.Success) { Git.Trace.WriteLine("querying for passphrase key."); if (match.Groups.Count < 2) { throw new ArgumentException("Unable to understand command."); } // string request = match.Groups[0].Value; string resource = match.Groups[1].Value; Git.Trace.WriteLine($"open dialog for '{resource}'."); System.Windows.Application application = new System.Windows.Application(); Gui.UserPromptDialog prompt = new Gui.UserPromptDialog(promptKind, resource); application.Run(prompt); if (!prompt.Failed && !string.IsNullOrEmpty(prompt.Response)) { string passphase = prompt.Response; Git.Trace.WriteLine("passphase acquired."); Console.Out.Write(passphase + "\n"); return; } Git.Trace.WriteLine("failed to interactively acquire credentials."); } if ((match = AskCredentialRegex.Match(args[0])).Success) { Git.Trace.WriteLine("querying for HTTPS credentials."); if (match.Groups.Count < 3) { throw new ArgumentException("Unable to understand command."); } string seeking = match.Groups[1].Value; string targetUrl = match.Groups[2].Value; Uri targetUri; if (Uri.TryCreate(targetUrl, UriKind.Absolute, out targetUri)) { Git.Trace.WriteLine($"success parsing URL, targetUri = '{targetUri}'."); // config stored credentials come in the format of <username>[:<password>]@<url> with password being optional // scheme terminator is actually "://" so we need adjust to get the correct index int schemeTerminator = targetUrl.IndexOf(':') + 2; int credentialTerminator = targetUrl.IndexOf('@', schemeTerminator + 1); if (credentialTerminator > 0) { Git.Trace.WriteLine("'@' symbol found in URL, assuming credential prefix."); string username = null; string password = null; // only check within the credential portion of the url, don't look past the '@' because the port token // is the same as the username / password seperator. int credentialLength = credentialTerminator - schemeTerminator; credentialLength = Math.Max(0, credentialLength); int passwordTerminator = targetUrl.IndexOf(':', schemeTerminator + 1, credentialLength); if (passwordTerminator > 0) { Git.Trace.WriteLine("':' symbol found in URL, assuming credential prefix contains password."); username = targetUrl.Substring(schemeTerminator + 1, passwordTerminator - schemeTerminator - 1); password = targetUrl.Substring(passwordTerminator + 1, credentialTerminator - passwordTerminator + 1); // print the password if it sought if (seeking.Equals("Password", StringComparison.OrdinalIgnoreCase)) { Console.Out.Write(password + '\n'); return; } } else { username = targetUrl.Substring(schemeTerminator + 1, credentialTerminator - schemeTerminator - 1); } // print the username if it sought if (seeking.Equals("Username", StringComparison.OrdinalIgnoreCase)) { Console.Out.Write(username + '\n'); return; } } // create a target Url with the credential portion stripped, because Git doesn't report hosts with credentials targetUrl = String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}://{1}", targetUri.Scheme, targetUri.Host); // retain the port if specified if (!targetUri.IsDefaultPort) { targetUrl += $":{targetUri.Port}"; } // retain the path if specified if (!String.IsNullOrWhiteSpace(targetUri.LocalPath)) { targetUrl += targetUri.LocalPath; } if (Uri.TryCreate(targetUrl, UriKind.Absolute, out targetUri)) { Git.Trace.WriteLine($"success parsing URL, targetUri = '{targetUri}'."); OperationArguments operationArguments = new OperationArguments.Impl(targetUri); // load up the operation arguments, enable tracing, and query for credentials LoadOperationArguments(operationArguments); EnableTraceLogging(operationArguments); if (QueryCredentials(operationArguments)) { if (seeking.Equals("Username", StringComparison.OrdinalIgnoreCase)) { Git.Trace.WriteLine($"username for '{targetUrl}' asked for and found."); Console.Out.Write(operationArguments.CredUsername + '\n'); return; } if (seeking.Equals("Password", StringComparison.OrdinalIgnoreCase)) { Git.Trace.WriteLine($"password for '{targetUrl}' asked for and found."); Console.Out.Write(operationArguments.CredPassword + '\n'); return; } } } else { Git.Trace.WriteLine("error: unable to parse target URL."); } } else { Git.Trace.WriteLine("error: unable to parse supplied URL."); } Git.Trace.WriteLine($"failed to detect {seeking} in target URL."); } if ((match = AskAuthenticityRegex.Match(args[0])).Success) { string host = match.Groups[1].Value; string fingerprint = match.Groups[2].Value; Git.Trace.WriteLine($"requesting authorization to add {host} ({fingerprint}) to known hosts."); System.Windows.Application application = new System.Windows.Application(); Gui.UserPromptDialog prompt = new Gui.UserPromptDialog(host, fingerprint); application.Run(prompt); if (prompt.Failed) { Git.Trace.WriteLine("denied authorization of host."); Console.Out.Write("no\n"); } else { Git.Trace.WriteLine("approved authorization of host."); Console.Out.Write("yes\n"); } return; } Git.Trace.WriteLine("failed to acquire credentials."); }