Beispiel #1
0
        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);
                    }
                }
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        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);
                    }
                }
            }
        }
Beispiel #4
0
        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);
                }
            }
        }
Beispiel #6
0
        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);
            }
        }
Beispiel #7
0
        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.");
        }
Beispiel #8
0
        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}'.");
            }
        }
Beispiel #9
0
        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.");
        }