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); }
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); } }
/// <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(); }
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); }
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."); } }
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)); }
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); }
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); }
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 }; }
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); }