private bool TryFindHelperExecutablePath(out string path)
        {
            string helperName = GitHubConstants.AuthHelperName;

            if (PlatformUtils.IsWindows())
            {
                helperName += ".exe";
            }

            string executableDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

            path = Path.Combine(executableDirectory, helperName);
            if (!Context.FileSystem.FileExists(path))
            {
                Context.Trace.WriteLine($"Did not find helper '{helperName}' in '{executableDirectory}'");

                // We currently only have a helper on Windows. If we failed to find the helper we should warn the user.
                if (PlatformUtils.IsWindows())
                {
                    Context.Streams.Error.WriteLine($"warning: missing '{helperName}' from installation.");
                }

                return(false);
            }

            return(true);
        }
Пример #2
0
        private static void SetParentExternal(Window window, IntPtr parentHandle)
        {
            // We only support parenting on the Windows platform at the moment.
            if (!PlatformUtils.IsWindows())
            {
                return;
            }

            IntPtr ourHandle = window.PlatformImpl.Handle.Handle;

            // Get the desktop scaling factor from our window instance so we
            // can calculate rects correctly for both our window, and the parent window.
            double scaling = window.PlatformImpl.DesktopScaling;

            // Get our window rect
            var ourRect = new PixelRect(
                PixelPoint.Origin,
                PixelSize.FromSize(window.ClientSize, scaling));

            // Get the parent rect
            if (!(GetWindowRectWin32(parentHandle, scaling) is PixelRect parentRect))
            {
                return;
            }

            // Set the position of our window to the center of the parent.
            PixelRect centerRect = parentRect.CenterRect(ourRect);

            window.Position = centerRect.Position;

            // Tell the platform native windowing system that we wish to parent
            // our window to the external window handle.
            SetWindowParentWin32(ourHandle, parentHandle);
        }
        private async Task RegisterTokenCacheAsync(IPublicClientApplication app)
        {
            Context.Trace.WriteLine(
                "Configuring Microsoft Authentication token cache to instance shared with Microsoft developer tools...");

            if (!PlatformUtils.IsWindows() && !PlatformUtils.IsPosix())
            {
                string osType = PlatformUtils.GetPlatformInformation().OperatingSystemType;
                Context.Trace.WriteLine($"Token cache integration is not supported on {osType}.");
                return;
            }

            // We use the MSAL extension library to provide us consistent cache file access semantics (synchronisation, etc)
            // as other Microsoft developer tools such as the Azure PowerShell CLI.
            MsalCacheHelper helper = null;

            try
            {
                var storageProps = CreateTokenCacheProps(useLinuxFallback: false);
                helper = await MsalCacheHelper.CreateAsync(storageProps);

                // Test that cache access is working correctly
                helper.VerifyPersistence();
            }
            catch (MsalCachePersistenceException ex)
            {
                Context.Streams.Error.WriteLine("warning: cannot persist Microsoft authentication token cache securely!");
                Context.Trace.WriteLine("Cannot persist Microsoft Authentication data securely!");
                Context.Trace.WriteException(ex);

                if (PlatformUtils.IsMacOS())
                {
                    // On macOS sometimes the Keychain returns the "errSecAuthFailed" error - we don't know why
                    // but it appears to be something to do with not being able to access the keychain.
                    // Locking and unlocking (or restarting) often fixes this.
                    Context.Streams.Error.WriteLine(
                        "warning: there is a problem accessing the login Keychain - either manually lock and unlock the " +
                        "login Keychain, or restart the computer to remedy this");
                }
                else if (PlatformUtils.IsLinux())
                {
                    // On Linux the SecretService/keyring might not be available so we must fall-back to a plaintext file.
                    Context.Streams.Error.WriteLine("warning: using plain-text fallback token cache");
                    Context.Trace.WriteLine("Using fall-back plaintext token cache on Linux.");
                    var storageProps = CreateTokenCacheProps(useLinuxFallback: true);
                    helper = await MsalCacheHelper.CreateAsync(storageProps);
                }
            }

            if (helper is null)
            {
                Context.Streams.Error.WriteLine("error: failed to set up Microsoft Authentication token cache!");
                Context.Trace.WriteLine("Failed to integrate with shared token cache!");
            }
            else
            {
                helper.RegisterCache(app.UserTokenCache);
                Context.Trace.WriteLine("Microsoft developer tools token cache configured.");
            }
        }
        private async Task RegisterVisualStudioTokenCacheAsync(IPublicClientApplication app)
        {
            Context.Trace.WriteLine("Configuring Visual Studio token cache...");

            // We currently only support Visual Studio on Windows
            if (PlatformUtils.IsWindows())
            {
                // The Visual Studio MSAL cache is located at "%LocalAppData%\.IdentityService\msal.cache" on Windows.
                // We use the MSAL extension library to provide us consistent cache file access semantics (synchronisation, etc)
                // as Visual Studio itself follows, as well as other Microsoft developer tools such as the Azure PowerShell CLI.
                const string cacheFileName  = "msal.cache";
                string       appData        = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
                string       cacheDirectory = Path.Combine(appData, ".IdentityService");

                var storageProps = new StorageCreationPropertiesBuilder(cacheFileName, cacheDirectory, app.AppConfig.ClientId).Build();

                var helper = await MsalCacheHelper.CreateAsync(storageProps);

                helper.RegisterCache(app.UserTokenCache);

                Context.Trace.WriteLine("Visual Studio token cache configured.");
            }
            else
            {
                string osType = PlatformUtils.GetPlatformInformation().OperatingSystemType;
                Context.Trace.WriteLine($"Visual Studio token cache integration is not supported on {osType}.");
            }
        }
        public TesterWindow()
        {
            InitializeComponent();
#if DEBUG
            this.AttachDevTools();
#endif

            if (PlatformUtils.IsWindows())
            {
                _environment = new WindowsEnvironment(new WindowsFileSystem());
            }
            else
            {
                IFileSystem fs;
                if (PlatformUtils.IsMacOS())
                {
                    fs = new MacOSFileSystem();
                }
                else
                {
                    fs = new LinuxFileSystem();
                }

                _environment = new PosixEnvironment(fs);
            }
        }
Пример #6
0
        /// <summary>
        /// Resolve symlinks and canonicalize the path (including "/" -> "\" on Windows)
        /// </summary>
        private static string RealPath(string path)
        {
            if (PlatformUtils.IsPosix())
            {
                bool trailingSlash = path.EndsWith("/");

                string resolvedPath;
                byte[] pathBytes = Encoding.UTF8.GetBytes(path);
                unsafe
                {
                    byte *resolvedPtr;
                    fixed(byte *pathPtr = pathBytes)
                    {
                        if ((resolvedPtr = Stdlib.realpath(pathPtr, (byte *)IntPtr.Zero)) == (byte *)IntPtr.Zero)
                        {
                            return(null);
                        }
                    }

                    resolvedPath = U8StringConverter.ToManaged(resolvedPtr);
                }

                // Preserve the trailing slash if there was one present initially
                return(trailingSlash ? $"{resolvedPath}/" : resolvedPath);
            }

            if (PlatformUtils.IsWindows())
            {
                // GetFullPath on Windows already preserves trailing slashes
                return(Path.GetFullPath(path));
            }

            throw new PlatformNotSupportedException();
        }
Пример #7
0
        private async Task <IPublicClientApplication> CreatePublicClientApplicationAsync(string authority, string clientId, Uri redirectUri)
        {
            var httpFactoryAdaptor = new MsalHttpClientFactoryAdaptor(Context.HttpClientFactory);

            var appBuilder = PublicClientApplicationBuilder.Create(clientId)
                             .WithAuthority(authority)
                             .WithRedirectUri(redirectUri.ToString())
                             .WithHttpClientFactory(httpFactoryAdaptor);

            // Listen to MSAL logs if GCM_TRACE_MSAUTH is set
            if (Context.Settings.IsMsalTracingEnabled)
            {
                // If GCM secret tracing is enabled also enable "PII" logging in MSAL
                bool enablePiiLogging = Context.Trace.IsSecretTracingEnabled;

                appBuilder.WithLogging(OnMsalLogMessage, LogLevel.Verbose, enablePiiLogging, false);
            }

            // If we have a parent window ID we should tell MSAL about it so it can parent any authentication dialogs
            // correctly. We only support this on Windows right now as MSAL only supports embedded/dialogs on Windows.
            if (PlatformUtils.IsWindows() && !string.IsNullOrWhiteSpace(Context.Settings.ParentWindowId) &&
                int.TryParse(Context.Settings.ParentWindowId, out int hWndInt) && hWndInt > 0)
            {
                appBuilder.WithParentActivityOrWindow(() => new IntPtr(hWndInt));
            }

            IPublicClientApplication app = appBuilder.Build();

            // Register the application token cache
            await RegisterTokenCacheAsync(app);

            return(app);
        }
Пример #8
0
        private async Task RegisterTokenCacheAsync(IPublicClientApplication app)
        {
            Context.Trace.WriteLine(
                "Configuring Microsoft Authentication token cache to instance shared with Microsoft developer tools...");

            if (!PlatformUtils.IsWindows() && !PlatformUtils.IsPosix())
            {
                string osType = PlatformUtils.GetPlatformInformation().OperatingSystemType;
                Context.Trace.WriteLine($"Token cache integration is not supported on {osType}.");
                return;
            }

            string clientId = app.AppConfig.ClientId;

            // We use the MSAL extension library to provide us consistent cache file access semantics (synchronisation, etc)
            // as other Microsoft developer tools such as the Azure PowerShell CLI.
            MsalCacheHelper helper = null;

            try
            {
                var storageProps = CreateTokenCacheProps(clientId, useLinuxFallback: false);
                helper = await MsalCacheHelper.CreateAsync(storageProps);

                // Test that cache access is working correctly
                helper.VerifyPersistence();
            }
            catch (MsalCachePersistenceException ex)
            {
                Context.Streams.Error.WriteLine("warning: cannot persist Microsoft Authentication data securely!");
                Context.Trace.WriteLine("Cannot persist Microsoft Authentication data securely!");
                Context.Trace.WriteException(ex);

                // On Linux the SecretService/keyring might not be available so we must fall-back to a plaintext file.
                if (PlatformUtils.IsLinux())
                {
                    Context.Trace.WriteLine("Using fall-back plaintext token cache on Linux.");
                    var storageProps = CreateTokenCacheProps(clientId, useLinuxFallback: true);
                    helper = await MsalCacheHelper.CreateAsync(storageProps);
                }
            }

            if (helper is null)
            {
                Context.Streams.Error.WriteLine("error: failed to set up Microsoft Authentication token cache!");
                Context.Trace.WriteLine("Failed to integrate with shared token cache!");
            }
            else
            {
                helper.RegisterCache(app.UserTokenCache);
                Context.Trace.WriteLine("Microsoft developer tools token cache configured.");
            }
        }
Пример #9
0
        public Shell.ExecuteArgs ToExecuteArgs()
        {
            string str = this.CompilerExecutable.ToString();

            if (PlatformUtils.IsWindows())
            {
                str = this.CompilerExecutable.InQuotes();
            }
            return(new Shell.ExecuteArgs {
                Arguments = this.Arguments.SeparateWithSpaces(),
                EnvVars = this.EnvVars,
                Executable = str
            });
        }
        private bool TryFindHelperExecutablePath(out string path)
        {
            string helperName = Constants.MicrosoftAuthHelperName;

            if (PlatformUtils.IsWindows())
            {
                helperName += ".exe";
            }

            string executableDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

            path = Path.Combine(executableDirectory, helperName);
            return(Context.FileSystem.FileExists(path));
        }
        protected bool TryFindHelperExecutablePath(string envar, string configName, string defaultValue, out string path)
        {
            bool isOverride = false;

            if (Context.Settings.TryGetSetting(
                    envar, Constants.GitConfiguration.Credential.SectionName, configName, out string helperName))
            {
                Context.Trace.WriteLine($"UI helper override specified: '{helperName}'.");
                isOverride = true;
            }
            else
            {
                // Use the default helper if none was specified.
                // On Windows append ".exe" for the default helpers only. If a user has specified their own
                // helper they should append the correct extension.
                helperName = PlatformUtils.IsWindows() ? $"{defaultValue}.exe" : defaultValue;
            }

            // If the user set the helper override to the empty string then they are signalling not to use a helper
            if (string.IsNullOrEmpty(helperName))
            {
                path = null;
                return(false);
            }

            if (Path.IsPathRooted(helperName))
            {
                path = helperName;
            }
            else
            {
                string executableDirectory = Path.GetDirectoryName(Context.ApplicationPath);
                path = Path.Combine(executableDirectory !, helperName);
            }

            if (!Context.FileSystem.FileExists(path))
            {
                // Only warn for missing helpers specified by the user, not in-box ones
                if (isOverride)
                {
                    Context.Trace.WriteLine($"UI helper '{helperName}' was not found at '{path}'.");
                    Context.Streams.Error.WriteLine($"warning: could not find configured UI helper '{helperName}'");
                }

                return(false);
            }

            return(true);
        }
        public ICredential GetCredentials(string resource, string userName)
        {
            EnsureArgument.NotNullOrWhiteSpace(resource, nameof(resource));

            ThrowIfUserInteractionDisabled();

            // TODO: we only support system GUI prompts on Windows currently
            if (Context.SessionManager.IsDesktopSession && PlatformUtils.IsWindows())
            {
                return(GetCredentialsByUi(resource, userName));
            }

            ThrowIfTerminalPromptsDisabled();

            return(GetCredentialsByTty(resource, userName));
        }
Пример #13
0
        public static string GetGitPath()
        {
            ProcessStartInfo psi;

            if (PlatformUtils.IsWindows())
            {
                psi = new ProcessStartInfo(
                    Path.Combine(
                        Environment.GetFolderPath(Environment.SpecialFolder.System),
                        "where.exe"),
                    "git.exe"
                    );
            }
            else
            {
                psi = new ProcessStartInfo("/usr/bin/which", "git");
            }

            psi.RedirectStandardOutput = true;

            using (var which = new Process {
                StartInfo = psi
            })
            {
                which.Start();
                which.WaitForExit();

                if (which.ExitCode != 0)
                {
                    throw new Exception("Failed to locate Git");
                }

                string data = which.StandardOutput.ReadLine();

                if (string.IsNullOrWhiteSpace(data))
                {
                    throw new Exception("Failed to locate Git on the PATH");
                }

                return(data);
            }
        }
        public TestEnvironment(IFileSystem fileSystem = null, string envPathSeparator = null, IEqualityComparer <string> pathComparer = null, IEqualityComparer <string> envarComparer = null)
        {
            _fileSystem = fileSystem ?? new TestFileSystem();

            // Use the current platform separators and comparison types by default
            _envPathSeparator = envPathSeparator ?? (PlatformUtils.IsWindows() ? ";" : ":");

            _envarComparer = envarComparer ??
                             (PlatformUtils.IsWindows()
                                 ? StringComparer.OrdinalIgnoreCase
                                 : StringComparer.Ordinal);

            _pathComparer = pathComparer ??
                            (PlatformUtils.IsLinux()
                                ? StringComparer.Ordinal
                                : StringComparer.OrdinalIgnoreCase);

            Variables = new Dictionary <string, string>(_envarComparer);
            Symlinks  = new Dictionary <string, string>(_pathComparer);
        }
Пример #15
0
        private string FindHelperExecutablePath()
        {
            string helperName = Constants.MicrosoftAuthHelperName;

            if (PlatformUtils.IsWindows())
            {
                helperName += ".exe";
            }

            string executableDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            string path = Path.Combine(executableDirectory, helperName);

            if (!Context.FileSystem.FileExists(path))
            {
                // We expect to have a helper on Windows and Mac
                throw new Exception($"Cannot find required helper '{helperName}' in '{executableDirectory}'");
            }

            return(path);
        }
Пример #16
0
        public TestCommandContext()
        {
            AppPath = PlatformUtils.IsWindows()
                ? @"C:\Program Files\Git Credential Manager Core\git-credential-manager-core.exe"
                : "/usr/local/bin/git-credential-manager-core";

            Streams           = new TestStandardStreams();
            Terminal          = new TestTerminal();
            SessionManager    = new TestSessionManager();
            Trace             = new NullTrace();
            FileSystem        = new TestFileSystem();
            CredentialStore   = new TestCredentialStore();
            HttpClientFactory = new TestHttpClientFactory();
            Git           = new TestGit();
            Environment   = new TestEnvironment(FileSystem);
            SystemPrompts = new TestSystemPrompts();

            Settings = new TestSettings {
                Environment = Environment, GitConfiguration = Git.Configuration
            };
        }
Пример #17
0
        private StorageCreationProperties CreateTokenCacheProps(string clientId, bool useLinuxFallback)
        {
            const string cacheFileName = "msal.cache";
            string       cacheDirectory;

            if (PlatformUtils.IsWindows())
            {
                // The shared MSAL cache is located at "%LocalAppData%\.IdentityService\msal.cache" on Windows.
                cacheDirectory = Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                    ".IdentityService"
                    );
            }
            else
            {
                // The shared MSAL cache metadata is located at "~/.local/.IdentityService/msal.cache" on UNIX.
                cacheDirectory = Path.Combine(Context.FileSystem.UserHomePath, ".local", ".IdentityService");
            }

            // The keychain is used on macOS with the following service & account names
            var builder = new StorageCreationPropertiesBuilder(cacheFileName, cacheDirectory, clientId)
                          .WithMacKeyChain("Microsoft.Developer.IdentityService", "MSALCache");

            if (useLinuxFallback)
            {
                builder.WithLinuxUnprotectedFile();
            }
            else
            {
                // The SecretService/keyring is used on Linux with the following collection name and attributes
                builder.WithLinuxKeyring(cacheFileName,
                                         "default", "MSALCache",
                                         new KeyValuePair <string, string>("MsalClientID", "Microsoft.Developer.IdentityService"),
                                         new KeyValuePair <string, string>("Microsoft.Developer.IdentityService", "1.0.0.0"));
            }

            return(builder.Build());
        }
        public Task UnconfigureAsync(ConfigurationTarget target)
        {
            string helperKey      = $"{Constants.GitConfiguration.Credential.SectionName}.{Constants.GitConfiguration.Credential.Helper}";
            string useHttpPathKey = $"{KnownGitCfg.Credential.SectionName}.https://dev.azure.com.{KnownGitCfg.Credential.UseHttpPath}";

            _context.Trace.WriteLine("Clearing Git configuration 'credential.useHttpPath' for https://dev.azure.com...");

            GitConfigurationLevel configurationLevel = target == ConfigurationTarget.System
                ? GitConfigurationLevel.System
                : GitConfigurationLevel.Global;

            IGitConfiguration targetConfig = _context.Git.GetConfiguration();

            // On Windows, if there is a "manager-core" entry remaining in the system config then we must not clear
            // the useHttpPath option otherwise this would break the bundled version of GCM in Git for Windows.
            if (!PlatformUtils.IsWindows() || target != ConfigurationTarget.System ||
                targetConfig.GetAll(helperKey).All(x => !string.Equals(x, "manager-core")))
            {
                targetConfig.Unset(configurationLevel, useHttpPathKey);
            }

            return(Task.CompletedTask);
        }