public override async Task <ICredential> GenerateCredentialAsync(InputArguments input) { ThrowIfDisposed(); // We should not allow unencrypted communication and should inform the user if (StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http")) { throw new Exception("Unencrypted HTTP is not supported for GitLab. Ensure the repository remote URL is using HTTPS."); } Uri remoteUri = input.GetRemoteUri(); AuthenticationModes authModes = GetSupportedAuthenticationModes(remoteUri); AuthenticationPromptResult promptResult = _gitLabAuth.GetAuthentication(remoteUri, input.UserName, authModes); switch (promptResult.AuthenticationMode) { case AuthenticationModes.Basic: case AuthenticationModes.Pat: return(promptResult.Credential); case AuthenticationModes.Browser: return(await GenerateOAuthCredentialAsync(input)); default: throw new ArgumentOutOfRangeException(nameof(promptResult)); } }
public BuildProviderExportAttribute(Type contractType, String guid, String name, AuthenticationModes supportedAuthenticationModes) : base(contractType) { Id = guid; Name = name; SupportedAuthenticationModes = supportedAuthenticationModes; }
/// <summary> /// Authentication /// </summary> /// <typeparam name="REQ"></typeparam> /// <typeparam name="RES"></typeparam> /// <param name="authentications"></param> /// <param name="credentials"></param> /// <returns></returns> internal MeisterStatus Authenticate <REQ, RES>(AuthenticationModes authentications, AuthenticationHeaderValue credentials) { string authHeader = credentials.Parameter; if (authHeader == null) { throw new MeisterException(resourceManager.GetString("BadAuthetication", CultureInfo.InvariantCulture)); } switch (authentications) { case AuthenticationModes.Basic: return(RunAsBasicAuthentication(authHeader)); case AuthenticationModes.OAuth: return(RunAsOAuth2Authentication(authHeader)); case AuthenticationModes.JWT: break; case AuthenticationModes.SAML2: break; default: break; } return(new MeisterStatus()); }
public override async Task <ICredential> GenerateCredentialAsync(InputArguments input) { ThrowIfDisposed(); // We should not allow unencrypted communication and should inform the user if (StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http")) { throw new Exception("Unencrypted HTTP is not supported for GitHub. Ensure the repository remote URL is using HTTPS."); } Uri remoteUri = input.GetRemoteUri(); string service = GetServiceName(input); AuthenticationModes authModes = await GetSupportedAuthenticationModesAsync(remoteUri); AuthenticationPromptResult promptResult = await _gitHubAuth.GetAuthenticationAsync(remoteUri, input.UserName, authModes); switch (promptResult.AuthenticationMode) { case AuthenticationModes.Basic: GitCredential patCredential = await GeneratePersonalAccessTokenAsync(remoteUri, promptResult.Credential); // HACK: Store the PAT immediately in case this PAT is not valid for SSO. // We don't know if this PAT is valid for SAML SSO and if it's not Git will fail // with a 403 and call neither 'store' or 'erase'. The user is expected to fiddle with // the PAT permissions manually on the web and then retry the Git operation. // We must store the PAT now so they can resume/repeat the operation with the same, // now SSO authorized, PAT. // See: https://github.com/GitCredentialManager/git-credential-manager/issues/133 Context.CredentialStore.AddOrUpdate(service, patCredential.Account, patCredential.Password); return(patCredential); case AuthenticationModes.Browser: return(await GenerateOAuthCredentialAsync(remoteUri, useBrowser : true)); case AuthenticationModes.Device: return(await GenerateOAuthCredentialAsync(remoteUri, useBrowser : false)); case AuthenticationModes.Pat: // The token returned by the user should be good to use directly as the password for Git string token = promptResult.Credential.Password; // Resolve the GitHub user handle if we don't have a specific username already from the // initial request. The reason for this is GitHub requires a (any?) value for the username // when Git makes calls to GitHub. string userName = promptResult.Credential.Account; if (userName is null) { GitHubUserInfo userInfo = await _gitHubApi.GetUserInfoAsync(remoteUri, token); userName = userInfo.Login; } return(new GitCredential(userName, token)); default: throw new ArgumentOutOfRangeException(nameof(promptResult)); } }
public override async Task <ICredential> GenerateCredentialAsync(InputArguments input) { ThrowIfDisposed(); // We should not allow unencrypted communication and should inform the user if (StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http")) { throw new Exception("Unencrypted HTTP is not supported for GitHub. Ensure the repository remote URL is using HTTPS."); } Uri targetUri = GetTargetUri(input); AuthenticationModes authModes = await GetSupportedAuthenticationModesAsync(targetUri); AuthenticationPromptResult promptResult = await _gitHubAuth.GetAuthenticationAsync(targetUri, authModes); switch (promptResult.AuthenticationMode) { case AuthenticationModes.Basic: return(await GeneratePersonalAccessTokenAsync(targetUri, promptResult.BasicCredential)); case AuthenticationModes.OAuth: return(await GenerateOAuthCredentialAsync(targetUri)); default: throw new ArgumentOutOfRangeException(nameof(promptResult)); } }
public async Task GitHubHostProvider_GetSupportedAuthenticationModes_OverrideNone_ReturnsDetectedValue() { var targetUri = new Uri("https://github.com"); var expectedModes = GitHubConstants.DotComAuthenticationModes; var context = new TestCommandContext { Settings = { Environment = { Variables = { [GitHubConstants.EnvironmentVariables.AuthenticationModes] = AuthenticationModes.None.ToString() } } } }; var ghApiMock = new Mock <IGitHubRestApi>(MockBehavior.Strict); var ghAuthMock = new Mock <IGitHubAuthentication>(MockBehavior.Strict); var provider = new GitHubHostProvider(context, ghApiMock.Object, ghAuthMock.Object); AuthenticationModes actualModes = await provider.GetSupportedAuthenticationModesAsync(targetUri); Assert.Equal(expectedModes, actualModes); }
public async Task GitHubHostProvider_GetSupportedAuthenticationModes_WithMetadata(string uriString, string gitHubAuthModes, string installedVersion, bool verifiablePasswordAuthentication, AuthenticationModes expectedModes) { var targetUri = new Uri(uriString); var context = new TestCommandContext { }; if (gitHubAuthModes != null) { context.Environment.Variables.Add(GitHubConstants.EnvironmentVariables.AuthenticationModes, gitHubAuthModes); } var ghApiMock = new Mock <IGitHubRestApi>(MockBehavior.Strict); var ghAuthMock = new Mock <IGitHubAuthentication>(MockBehavior.Strict); var metaInfo = new GitHubMetaInfo { InstalledVersion = installedVersion, VerifiablePasswordAuthentication = verifiablePasswordAuthentication }; ghApiMock.Setup(x => x.GetMetaInfoAsync(targetUri)).ReturnsAsync(metaInfo); var provider = new GitHubHostProvider(context, ghApiMock.Object, ghAuthMock.Object); AuthenticationModes actualModes = await provider.GetSupportedAuthenticationModesAsync(targetUri); Assert.Equal(expectedModes, actualModes); }
public async Task GitHubHostProvider_GetSupportedAuthenticationModes_GitHubDotCom_ReturnsDotComModes() { var targetUri = new Uri("https://github.com"); var expectedModes = GitHubConstants.DotComAuthenticationModes; var provider = new GitHubHostProvider(new TestCommandContext()); AuthenticationModes actualModes = await provider.GetSupportedAuthenticationModesAsync(targetUri); Assert.Equal(expectedModes, actualModes); }
public void GitLabHostProvider_GetSupportedAuthenticationModes_DotCom_ReturnsDotComModes() { Uri targetUri = GitLabConstants.GitLabDotCom; AuthenticationModes expected = GitLabConstants.DotComAuthenticationModes; var context = new TestCommandContext(); var provider = new GitLabHostProvider(context); AuthenticationModes actual = provider.GetSupportedAuthenticationModes(targetUri); Assert.Equal(expected, actual); }
public void GitLabHostProvider_GetSupportedAuthenticationModes_Custom_NoOAuthConfig_ReturnsBasicPat() { var targetUri = new Uri("https://gitlab.example.com"); var expected = AuthenticationModes.Basic | AuthenticationModes.Pat; var context = new TestCommandContext(); var provider = new GitLabHostProvider(context); AuthenticationModes actual = provider.GetSupportedAuthenticationModes(targetUri); Assert.Equal(expected, actual); }
public AuthenticationViewModel(AuthenticationModes authMode, PersonViewModel person) { _personToAuthenticate = person; KeypadButtonPressedCommand = new RelayCommand <string>((p) => { lock (this) { pinEntered += p; PinCheck(_personToAuthenticate.Pin); } }); }
public async Task <ICredential> GetCredentialAsync(InputArguments input) { // We should not allow unencrypted communication and should inform the user if (StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http") && input.TryGetHostAndPort(out string host, out _) && IsBitbucketOrg(host)) { throw new Exception("Unencrypted HTTP is not supported for Bitbucket.org. Ensure the repository remote URL is using HTTPS."); } Uri remoteUri = input.GetRemoteUri(); AuthenticationModes authModes = GetSupportedAuthenticationModes(remoteUri); return(await GetStoredCredentials(remoteUri, input.UserName, authModes) ?? await GetRefreshedCredentials(remoteUri, input.UserName, authModes)); }
public void GitLabHostProvider_GetSupportedAuthenticationModes_Custom_WithOAuthConfig_ReturnsBasicPatBrowser() { var targetUri = new Uri("https://gitlab.example.com"); var expected = AuthenticationModes.Basic | AuthenticationModes.Pat | AuthenticationModes.Browser; var context = new TestCommandContext(); context.Environment.Variables[GitLabConstants.EnvironmentVariables.DevOAuthClientId] = "abcdefg1234567"; var provider = new GitLabHostProvider(context); AuthenticationModes actual = provider.GetSupportedAuthenticationModes(targetUri); Assert.Equal(expected, actual); }
public bool Authenticate(string userid, string psw, Uri gatewayUri, string sapclient, bool od4) { string bae = userid + ":" + psw; var byteArray = Encoding.ASCII.GetBytes(bae); AuthenticationHeaderValue headerValue = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray)); MeisterExtensions me = MeisterExtensions.RemoveNullsAndEmptyArrays; MeisterOptions mo = MeisterOptions.None; if (od4) { mo = MeisterOptions.UseODataV4; } RuntimeOptions ro = RuntimeOptions.ExecuteSync; AuthenticationModes am = AuthenticationModes.Basic; resource = new Resource <dynamic, dynamic>(gatewayUri, headerValue, sapclient, me, mo, am, ro); return(resource.Authenticate()); }
public void BitbucketHostProvider_GetSupportedAuthenticationModes(string uriString, string bitbucketAuthModes, AuthenticationModes expectedModes) { var targetUri = new Uri(uriString); var context = new TestCommandContext { }; if (bitbucketAuthModes != null) { context.Environment.Variables.Add(BitbucketConstants.EnvironmentVariables.AuthenticationModes, bitbucketAuthModes); } var provider = new BitbucketHostProvider(context, bitbucketAuthentication.Object, bitbucketApi.Object); AuthenticationModes actualModes = provider.GetSupportedAuthenticationModes(targetUri); Assert.Equal(expectedModes, actualModes); }
public async Task GitHubHostProvider_GetSupportedAuthenticationModes(string uriString, string gitHubAuthModes, AuthenticationModes expectedModes) { var targetUri = new Uri(uriString); var context = new TestCommandContext { }; if (gitHubAuthModes != null) { context.Environment.Variables.Add(GitHubConstants.EnvironmentVariables.AuthenticationModes, gitHubAuthModes); } var ghApiMock = new Mock <IGitHubRestApi>(MockBehavior.Strict); var ghAuthMock = new Mock <IGitHubAuthentication>(MockBehavior.Strict); var provider = new GitHubHostProvider(context, ghApiMock.Object, ghAuthMock.Object); AuthenticationModes actualModes = await provider.GetSupportedAuthenticationModesAsync(targetUri); Assert.Equal(expectedModes, actualModes); }
public override async Task <ICredential> GenerateCredentialAsync(InputArguments input) { ThrowIfDisposed(); // We should not allow unencrypted communication and should inform the user if (StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http")) { throw new Exception("Unencrypted HTTP is not supported for GitHub. Ensure the repository remote URL is using HTTPS."); } Uri remoteUri = input.GetRemoteUri(); string service = GetServiceName(input); AuthenticationModes authModes = await GetSupportedAuthenticationModesAsync(remoteUri); AuthenticationPromptResult promptResult = await _gitHubAuth.GetAuthenticationAsync(remoteUri, input.UserName, authModes); switch (promptResult.AuthenticationMode) { case AuthenticationModes.Basic: GitCredential patCredential = await GeneratePersonalAccessTokenAsync(remoteUri, promptResult.BasicCredential); // HACK: Store the PAT immediately in case this PAT is not valid for SSO. // We don't know if this PAT is valid for SAML SSO and if it's not Git will fail // with a 403 and call neither 'store' or 'erase'. The user is expected to fiddle with // the PAT permissions manually on the web and then retry the Git operation. // We must store the PAT now so they can resume/repeat the operation with the same, // now SSO authorized, PAT. // See: https://github.com/microsoft/Git-Credential-Manager-Core/issues/133 Context.CredentialStore.AddOrUpdate(service, patCredential.Account, patCredential.Password); return(patCredential); case AuthenticationModes.OAuth: return(await GenerateOAuthCredentialAsync(remoteUri)); default: throw new ArgumentOutOfRangeException(nameof(promptResult)); } }
public async Task GitHubHostProvider_GetSupportedAuthenticationModes_NotDotCom_NewInstanceWithPassword_ReturnsBasicAndOAuth() { var targetUri = new Uri("https://ghe.io"); var metaInfo = new GitHubMetaInfo { InstalledVersion = "100.0", VerifiablePasswordAuthentication = true }; var expectedModes = AuthenticationModes.Basic | AuthenticationModes.OAuth; var context = new TestCommandContext(); var ghAuthMock = new Mock <IGitHubAuthentication>(MockBehavior.Strict); var ghApiMock = new Mock <IGitHubRestApi>(MockBehavior.Strict); ghApiMock.Setup(x => x.GetMetaInfoAsync(targetUri)).ReturnsAsync(metaInfo); var provider = new GitHubHostProvider(context, ghApiMock.Object, ghAuthMock.Object); AuthenticationModes actualModes = await provider.GetSupportedAuthenticationModesAsync(targetUri); Assert.Equal(expectedModes, actualModes); }
private async Task <bool> ValidateCredentialsWork(Uri remoteUri, ICredential credentials, AuthenticationModes authModes) { if (credentials is null) { return(false); } // TODO: ideally we'd also check if the credentials have expired based on some local metadata // (once/if we get such metadata storage), and return false if they have. // This would be more efficient than having to make REST API calls to check. _context.Trace.WriteLineSecrets($"Validate credentials ({credentials.Account}/{{0}}) are fresh for {remoteUri} ...", new object[] { credentials.Password }); if (!IsBitbucketOrg(remoteUri)) { // TODO: Validate DC/Server credentials before returning them to Git // Currently credentials for DC/Server are not checked by GCM. // Instead the validation relies on Git to try and fail with the credentials and then request GCM to erase them _context.Trace.WriteLine("For DC/Server skip validating existing credentials"); return(await Task.FromResult(true)); } // Bitbucket supports both OAuth + Basic Auth unless there is explicit GCM configuration. // The credentials could be for either scheme therefore need to potentially test both possibilities. if (SupportsOAuth(authModes)) { try { await ResolveOAuthUserNameAsync(credentials.Password); _context.Trace.WriteLine("Validated existing credentials using OAuth"); return(true); } catch (Exception) { _context.Trace.WriteLine("Failed to validate existing credentials using OAuth"); } } if (SupportsBasicAuth(authModes)) { try { await ResolveBasicAuthUserNameAsync(credentials.Account, credentials.Password); _context.Trace.WriteLine("Validated existing credentials using BasicAuth"); return(true); } catch (Exception) { _context.Trace.WriteLine("Failed to validate existing credentials using Basic Auth"); return(false); } } return(true); }
public async Task <AuthenticationPromptResult> GetAuthenticationAsync(Uri targetUri, AuthenticationModes modes) { ThrowIfUserInteractionDisabled(); // If the GitHub auth stack doesn't support flows such as RFC 8628 and we do not have // an interactive desktop session, we cannot offer OAuth authentication. if ((modes & AuthenticationModes.OAuth) != 0 && !Context.SessionManager.IsDesktopSession && !GitHubConstants.IsOAuthDeviceAuthSupported) { Context.Trace.WriteLine("Ignoring OAuth authentication mode because we are not in an interactive desktop session. GitHub does not support RFC 8628."); modes &= ~AuthenticationModes.OAuth; } if (modes == AuthenticationModes.None) { throw new ArgumentException($"Must specify at least one {nameof(AuthenticationModes)}", nameof(modes)); } if (TryFindHelperExecutablePath(out string helperPath)) { var promptArgs = new StringBuilder("prompt"); if ((modes & AuthenticationModes.Basic) != 0) { promptArgs.Append(" --basic"); } if ((modes & AuthenticationModes.OAuth) != 0) { promptArgs.Append(" --oauth"); } if (!GitHubHostProvider.IsGitHubDotCom(targetUri)) { promptArgs.AppendFormat(" --enterprise-url {0}", targetUri.ToString()); } IDictionary <string, string> resultDict = await InvokeHelperAsync(helperPath, promptArgs.ToString(), null); if (!resultDict.TryGetValue("mode", out string responseMode)) { throw new Exception("Missing 'mode' in response"); } switch (responseMode.ToLowerInvariant()) { case "oauth": return(new AuthenticationPromptResult(AuthenticationModes.OAuth)); case "basic": if (!resultDict.TryGetValue("username", out string userName)) { throw new Exception("Missing 'username' in response"); } if (!resultDict.TryGetValue("password", out string password)) { throw new Exception("Missing 'password' in response"); } return(new AuthenticationPromptResult(new GitCredential(userName, password))); default: throw new Exception($"Unknown mode value in response '{responseMode}'"); } } else { ThrowIfTerminalPromptsDisabled(); switch (modes) { case AuthenticationModes.Basic | AuthenticationModes.OAuth: var menuTitle = $"Select an authentication method for '{targetUri}'"; var menu = new TerminalMenu(Context.Terminal, menuTitle) { new TerminalMenuItem(1, "Web browser"), new TerminalMenuItem(2, "Username/password", true) }; int option = menu.Show(); if (option == 1) { goto case AuthenticationModes.OAuth; } if (option == 2) { goto case AuthenticationModes.Basic; } throw new Exception(); case AuthenticationModes.Basic: Context.Terminal.WriteLine("Enter GitHub credentials for '{0}'...", targetUri); string userName = Context.Terminal.Prompt("Username"); string password = Context.Terminal.PromptSecret("Password"); return(new AuthenticationPromptResult(new GitCredential(userName, password))); case AuthenticationModes.OAuth: return(new AuthenticationPromptResult(AuthenticationModes.OAuth)); default: throw new ArgumentOutOfRangeException(nameof(modes), $"Unknown {nameof(AuthenticationModes)} value"); } } }
private async Task <ICredential> GetStoredCredentials(Uri remoteUri, string userName, AuthenticationModes authModes) { if (_context.Settings.TryGetSetting(BitbucketConstants.EnvironmentVariables.AlwaysRefreshCredentials, Constants.GitConfiguration.Credential.SectionName, BitbucketConstants.GitConfiguration.Credential.AlwaysRefreshCredentials, out string alwaysRefreshCredentials) && alwaysRefreshCredentials.ToBooleanyOrDefault(false)) { _context.Trace.WriteLine("Ignore stored credentials"); return(null); } string credentialService = GetServiceName(remoteUri); _context.Trace.WriteLine($"Look for existing credentials under {credentialService} ..."); ICredential credentials = _context.CredentialStore.Get(credentialService, userName); if (credentials == null) { _context.Trace.WriteLine("No stored credentials found"); return(null); } _context.Trace.WriteLineSecrets($"Found stored credentials: {credentials.Account}/{{0}}", new object[] { credentials.Password }); // Check credentials are still valid if (!await ValidateCredentialsWork(remoteUri, credentials, authModes)) { return(null); } return(credentials); }
private async Task <ICredential> GetRefreshedCredentials(Uri remoteUri, string userName, AuthenticationModes authModes) { _context.Trace.WriteLine("Refresh credentials..."); // Check for presence of refresh_token entry in credential store var refreshTokenService = GetRefreshTokenServiceName(remoteUri); _context.Trace.WriteLine("Checking for refresh token..."); ICredential refreshToken = SupportsOAuth(authModes) ? _context.CredentialStore.Get(refreshTokenService, userName) : null; if (refreshToken is null) { _context.Trace.WriteLine($"No stored refresh token found"); // There is no refresh token either because this is a non-2FA enabled account (where OAuth is not // required), or because we previously erased the RT. bool skipOAuthPrompt = false; if (SupportsBasicAuth(authModes)) { _context.Trace.WriteLine("Prompt for credentials..."); // We don't have any credentials to use at all! Start with the assumption of no 2FA requirement // and capture username and password via an interactive prompt. var result = await _bitbucketAuth.GetCredentialsAsync(remoteUri, userName, authModes); if (result is null || result.AuthenticationMode == AuthenticationModes.None) { _context.Trace.WriteLine("User cancelled credential prompt"); throw new Exception("User cancelled credential prompt."); } switch (result.AuthenticationMode) { case AuthenticationModes.OAuth: // If the user wants to use OAuth fall through to interactive auth // and avoid the OAuth "continue" confirmation prompt skipOAuthPrompt = true; break; case AuthenticationModes.Basic: // We need to check if these user/pass credentials require 2FA _context.Trace.WriteLine("Checking if two-factor requirements for credentials..."); bool requires2Fa = await RequiresTwoFactorAuthenticationAsync(result.Credential); if (!requires2Fa) { _context.Trace.WriteLine("Two-factor authentication not required"); // Return the valid credential return(result.Credential); } // 2FA is required; fall through to interactive OAuth flow break; default: throw new ArgumentOutOfRangeException( $"Unexpected {nameof(AuthenticationModes)} returned from prompt"); } // Fall through to the start of the interactive OAuth authentication flow } if (SupportsOAuth(authModes) && !skipOAuthPrompt) { _context.Trace.WriteLine("Two-factor authentication is required - prompting for auth via OAuth..."); _context.Trace.WriteLine("Prompt for OAuth..."); // Show the 2FA/OAuth authentication required prompt bool @continue = await _bitbucketAuth.ShowOAuthRequiredPromptAsync(); if (!@continue) { _context.Trace.WriteLine("User cancelled OAuth prompt"); throw new Exception("User cancelled OAuth authentication."); } // Fall through to the start of the interactive OAuth authentication flow } } else { _context.Trace.WriteLineSecrets($"Found stored refresh token: {{0}}", new object[] { refreshToken }); try { return(await GetOAuthCredentialsViaRefreshFlow(remoteUri, refreshToken)); } catch (OAuth2Exception ex) { _context.Trace.WriteLine("Failed to refresh existing OAuth credential using refresh token"); _context.Trace.WriteException(ex); // We failed to refresh the AT using the RT; log the refresh failure and fall through to restart // the OAuth authentication flow } } return(await GetOAuthCredentialsViaInteractiveBrowserFlow(remoteUri)); }
private static bool SupportsBasicAuth(AuthenticationModes authModes) { return((authModes & AuthenticationModes.Basic) != 0); }
public AuthenticationPromptResult(AuthenticationModes mode, ICredential credential) : this(mode) { Credential = credential; }
public async Task <AuthenticationPromptResult> GetAuthenticationAsync(Uri targetUri, string userName, AuthenticationModes modes) { // If we don't have a desktop session/GUI then we cannot offer browser if (!Context.SessionManager.IsDesktopSession) { modes = modes & ~AuthenticationModes.Browser; } // We need at least one mode! if (modes == AuthenticationModes.None) { throw new ArgumentException(@$ "Must specify at least one {nameof(AuthenticationModes)}", nameof(modes)); } if (Context.Settings.IsGuiPromptsEnabled && Context.SessionManager.IsDesktopSession && TryFindHelperExecutablePath(out string helperPath)) { var cmdArgs = new StringBuilder("prompt"); if (!string.IsNullOrWhiteSpace(userName)) { cmdArgs.AppendFormat(" --username {0}", QuoteCmdArg(userName)); } cmdArgs.AppendFormat(" --url {0}", QuoteCmdArg(targetUri.ToString())); if ((modes & AuthenticationModes.Basic) != 0) { cmdArgs.Append(" --basic"); } if ((modes & AuthenticationModes.Browser) != 0) { cmdArgs.Append(" --browser"); } if ((modes & AuthenticationModes.Pat) != 0) { cmdArgs.Append(" --pat"); } IDictionary <string, string> resultDict = await InvokeHelperAsync(helperPath, cmdArgs.ToString()); if (!resultDict.TryGetValue("mode", out string responseMode)) { throw new Exception("Missing 'mode' in response"); } switch (responseMode.ToLowerInvariant()) { case "pat": if (!resultDict.TryGetValue("pat", out string pat)) { throw new Exception("Missing 'pat' in response"); } if (!resultDict.TryGetValue("username", out string patUserName)) { // Username is optional for PATs } return(new AuthenticationPromptResult( AuthenticationModes.Pat, new GitCredential(patUserName, pat))); case "browser": return(new AuthenticationPromptResult(AuthenticationModes.Browser)); case "basic": if (!resultDict.TryGetValue("username", out userName)) { throw new Exception("Missing 'username' in response"); } if (!resultDict.TryGetValue("password", out string password)) { throw new Exception("Missing 'password' in response"); } return(new AuthenticationPromptResult( AuthenticationModes.Basic, new GitCredential(userName, password))); default: throw new Exception($"Unknown mode value in response '{responseMode}'"); } } else { switch (modes) { case AuthenticationModes.Basic: ThrowIfUserInteractionDisabled(); ThrowIfTerminalPromptsDisabled(); Context.Terminal.WriteLine("Enter GitLab credentials for '{0}'...", targetUri); if (string.IsNullOrWhiteSpace(userName)) { userName = Context.Terminal.Prompt("Username"); } else { Context.Terminal.WriteLine("Username: {0}", userName); } string password = Context.Terminal.PromptSecret("Password"); return(new AuthenticationPromptResult(AuthenticationModes.Basic, new GitCredential(userName, password))); case AuthenticationModes.Pat: ThrowIfUserInteractionDisabled(); ThrowIfTerminalPromptsDisabled(); Context.Terminal.WriteLine("Enter GitLab credentials for '{0}'...", targetUri); if (string.IsNullOrWhiteSpace(userName)) { userName = Context.Terminal.Prompt("Username"); } else { Context.Terminal.WriteLine("Username: {0}", userName); } string token = Context.Terminal.PromptSecret("Personal access token"); return(new AuthenticationPromptResult(AuthenticationModes.Pat, new GitCredential(userName, token))); case AuthenticationModes.Browser: return(new AuthenticationPromptResult(AuthenticationModes.Browser)); case AuthenticationModes.None: throw new ArgumentOutOfRangeException(nameof(modes), @$ "At least one {nameof(AuthenticationModes)} must be supplied"); default: ThrowIfUserInteractionDisabled(); ThrowIfTerminalPromptsDisabled(); var menuTitle = $"Select an authentication method for '{targetUri}'"; var menu = new TerminalMenu(Context.Terminal, menuTitle); TerminalMenuItem browserItem = null; TerminalMenuItem basicItem = null; TerminalMenuItem patItem = null; if ((modes & AuthenticationModes.Browser) != 0) { browserItem = menu.Add("Web browser"); } if ((modes & AuthenticationModes.Pat) != 0) { patItem = menu.Add("Personal access token"); } if ((modes & AuthenticationModes.Basic) != 0) { basicItem = menu.Add("Username/password"); } // Default to the 'first' choice in the menu TerminalMenuItem choice = menu.Show(0); if (choice == browserItem) { goto case AuthenticationModes.Browser; } if (choice == basicItem) { goto case AuthenticationModes.Basic; } if (choice == patItem) { goto case AuthenticationModes.Pat; } throw new Exception(); } } }
public AuthenticationPromptResult(AuthenticationModes mode) { AuthenticationMode = mode; }
public async Task <CredentialsPromptResult> GetCredentialsAsync(Uri targetUri, string userName, AuthenticationModes modes) { ThrowIfUserInteractionDisabled(); string password; // If we don't have a desktop session/GUI then we cannot offer OAuth since the only // supported grant is authcode (i.e, using a web browser; device code is not supported). if (!Context.SessionManager.IsDesktopSession) { modes = modes & ~AuthenticationModes.OAuth; } // If the only supported mode is OAuth then just return immediately if (modes == AuthenticationModes.OAuth) { return(new CredentialsPromptResult(AuthenticationModes.OAuth)); } // We need at least one mode! if (modes == AuthenticationModes.None) { throw new ArgumentException(@$ "Must specify at least one {nameof(AuthenticationModes)}", nameof(modes)); } // Shell out to the UI helper and show the Bitbucket u/p prompt if (Context.Settings.IsGuiPromptsEnabled && Context.SessionManager.IsDesktopSession && TryFindHelperExecutablePath(out string helperPath)) { var cmdArgs = new StringBuilder("userpass"); if (!string.IsNullOrWhiteSpace(userName)) { cmdArgs.AppendFormat(" --username {0}", QuoteCmdArg(userName)); } if ((modes & AuthenticationModes.OAuth) != 0) { cmdArgs.Append(" --show-oauth"); } IDictionary <string, string> output = await InvokeHelperAsync(helperPath, cmdArgs.ToString()); if (output.TryGetValue("mode", out string mode) && StringComparer.OrdinalIgnoreCase.Equals(mode, "oauth")) { return(new CredentialsPromptResult(AuthenticationModes.OAuth)); } else { if (!output.TryGetValue("username", out userName)) { throw new Exception("Missing username in response"); } if (!output.TryGetValue("password", out password)) { throw new Exception("Missing password in response"); } return(new CredentialsPromptResult( AuthenticationModes.Basic, new GitCredential(userName, password))); } } else { ThrowIfTerminalPromptsDisabled(); switch (modes) { case AuthenticationModes.Basic: Context.Terminal.WriteLine("Enter Bitbucket credentials for '{0}'...", targetUri); if (!string.IsNullOrWhiteSpace(userName)) { // Don't need to prompt for the username if it has been specified already Context.Terminal.WriteLine("Username: {0}", userName); } else { // Prompt for username userName = Context.Terminal.Prompt("Username"); } // Prompt for password password = Context.Terminal.PromptSecret("Password"); return(new CredentialsPromptResult( AuthenticationModes.Basic, new GitCredential(userName, password))); case AuthenticationModes.OAuth: return(new CredentialsPromptResult(AuthenticationModes.OAuth)); case AuthenticationModes.None: throw new ArgumentOutOfRangeException(nameof(modes), @$ "At least one {nameof(AuthenticationModes)} must be supplied"); default: var menuTitle = $"Select an authentication method for '{targetUri}'"; var menu = new TerminalMenu(Context.Terminal, menuTitle); TerminalMenuItem oauthItem = null; TerminalMenuItem basicItem = null; if ((modes & AuthenticationModes.OAuth) != 0) { oauthItem = menu.Add("OAuth"); } if ((modes & AuthenticationModes.Basic) != 0) { basicItem = menu.Add("Username/password"); } // Default to the 'first' choice in the menu TerminalMenuItem choice = menu.Show(0); if (choice == oauthItem) { goto case AuthenticationModes.OAuth; } if (choice == basicItem) { goto case AuthenticationModes.Basic; } throw new Exception(); } } }
public CredentialsPromptResult(AuthenticationModes mode) { AuthenticationMode = mode; }
private static bool SupportsOAuth(AuthenticationModes authModes) { return((authModes & AuthenticationModes.OAuth) != 0); }
public async Task <AuthenticationPromptResult> GetAuthenticationAsync(Uri targetUri, string userName, AuthenticationModes modes) { ThrowIfUserInteractionDisabled(); if (modes == AuthenticationModes.None) { throw new ArgumentException($"Must specify at least one {nameof(AuthenticationModes)}", nameof(modes)); } if (TryFindHelperExecutablePath(out string helperPath)) { var promptArgs = new StringBuilder("prompt"); if ((modes & AuthenticationModes.Basic) != 0) { promptArgs.Append(" --basic"); } if ((modes & AuthenticationModes.OAuth) != 0) { promptArgs.Append(" --oauth"); } if (!GitHubHostProvider.IsGitHubDotCom(targetUri)) { promptArgs.AppendFormat(" --enterprise-url {0}", targetUri); } if (!string.IsNullOrWhiteSpace(userName)) { promptArgs.AppendFormat("--username {0}", userName); } IDictionary <string, string> resultDict = await InvokeHelperAsync(helperPath, promptArgs.ToString(), null); if (!resultDict.TryGetValue("mode", out string responseMode)) { throw new Exception("Missing 'mode' in response"); } switch (responseMode.ToLowerInvariant()) { case "oauth": return(new AuthenticationPromptResult(AuthenticationModes.OAuth)); case "basic": if (!resultDict.TryGetValue("username", out userName)) { throw new Exception("Missing 'username' in response"); } if (!resultDict.TryGetValue("password", out string password)) { throw new Exception("Missing 'password' in response"); } return(new AuthenticationPromptResult(new GitCredential(userName, password))); default: throw new Exception($"Unknown mode value in response '{responseMode}'"); } } else { ThrowIfTerminalPromptsDisabled(); switch (modes) { case AuthenticationModes.Basic | AuthenticationModes.OAuth: var menuTitle = $"Select an authentication method for '{targetUri}'"; var menu = new TerminalMenu(Context.Terminal, menuTitle) { new TerminalMenuItem(1, "Web browser", isDefault: true), new TerminalMenuItem(2, "Username/password") }; int option = menu.Show(); if (option == 1) { goto case AuthenticationModes.OAuth; } if (option == 2) { goto case AuthenticationModes.Basic; } throw new Exception(); case AuthenticationModes.Basic: Context.Terminal.WriteLine("Enter GitHub credentials for '{0}'...", targetUri); if (string.IsNullOrWhiteSpace(userName)) { userName = Context.Terminal.Prompt("Username"); } else { Context.Terminal.WriteLine("Username: {0}", userName); } string password = Context.Terminal.PromptSecret("Password"); return(new AuthenticationPromptResult(new GitCredential(userName, password))); case AuthenticationModes.OAuth: return(new AuthenticationPromptResult(AuthenticationModes.OAuth)); default: throw new ArgumentOutOfRangeException(nameof(modes), $"Unknown {nameof(AuthenticationModes)} value"); } } }