internal void SetNewUsername(GitCredential entry, string username) { if (SeletedAdapter != null) { try { if (!SeletedAdapter.SaveUsername(entry.ManagerUrl, username)) { logger.LogFormat(LogType.Error, "Could not set new Username to {0} with URL: {1}", GetAdapterName(SeletedAdapter), entry.ManagerUrl); return; } } catch (Exception e) { logger.LogFormat(LogType.Error, "There was a problem while trying to set username to {0}", GetAdapterName(SeletedAdapter)); logger.LogException(e); } return; } if (entry != null) { entry.SetUsername(username); gitCredentials.MarkDirty(); } }
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)); } }
internal void SetNewPassword(GitCredential entry, SecureString password) { if (SeletedAdapter != null) { try { if (!SeletedAdapter.SavePassword(entry.ManagerUrl, null, password, false)) { logger.LogFormat(LogType.Error, "Could not save new Password to {0} with URL: {1}", GetAdapterName(SeletedAdapter), entry.ManagerUrl); return; } } catch (Exception e) { logger.LogFormat(LogType.Error, "There was a problem while trying to save credentials to {0}", GetAdapterName(SeletedAdapter)); logger.LogException(e); } return; } if (entry != null) { entry.EncryptPassword(password); gitCredentials.MarkDirty(); } }
private async Task <AuthenticationResult> ParseSuccessResponseAsync(Uri targetUri, HttpResponseMessage response) { GitCredential token = null; string responseText = await response.Content.ReadAsStringAsync(); Match tokenMatch; if ((tokenMatch = Regex.Match(responseText, @"\s*""token""\s*:\s*""([^""]+)""\s*", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)).Success && tokenMatch.Groups.Count > 1) { string tokenText = tokenMatch.Groups[1].Value; token = new GitCredential(Constants.PersonalAccessTokenUserName, tokenText); } if (token == null) { _context.Trace.WriteLine($"Authentication for '{targetUri}' failed."); return(new AuthenticationResult(GitHubAuthenticationResultType.Failure)); } else { _context.Trace.WriteLine($"Authentication success: new personal access token for '{targetUri}' created."); return(new AuthenticationResult(GitHubAuthenticationResultType.Success, token)); } }
public void SecretServiceCollection_ReadWriteDelete() { SecretServiceCollection collection = SecretServiceCollection.Open(); // Create a key that is guarenteed to be unique string key = $"secretkey-{Guid.NewGuid():N}"; const string userName = "******"; const string password = "******"; var credential = new GitCredential(userName, password); try { // Write collection.AddOrUpdate(key, credential); // Read ICredential outCredential = collection.Get(key); Assert.NotNull(outCredential); Assert.Equal(credential.UserName, outCredential.UserName); Assert.Equal(credential.Password, outCredential.Password); } finally { // Ensure we clean up after ourselves even in case of 'get' failures collection.Remove(key); } }
public async Task GetCommand_ExecuteAsync_CallsHostProviderAndWritesCredential() { const string testUserName = "******"; const string testPassword = "******"; ICredential testCredential = new GitCredential(testUserName, testPassword); var expectedStdOutDict = new Dictionary <string, string> { ["username"] = testUserName, ["password"] = testPassword }; var providerMock = new Mock <IHostProvider>(); providerMock.Setup(x => x.GetCredentialAsync(It.IsAny <InputArguments>())) .ReturnsAsync(testCredential); var providerRegistry = new TestHostProviderRegistry { Provider = providerMock.Object }; var context = new TestCommandContext(); string[] cmdArgs = { "get" }; var command = new GetCommand(providerRegistry); await command.ExecuteAsync(context, cmdArgs); IDictionary <string, string> actualStdOutDict = ParseDictionary(context.Streams.Out); providerMock.Verify(x => x.GetCredentialAsync(It.IsAny <InputArguments>()), Times.Once); Assert.Equal(expectedStdOutDict, actualStdOutDict); }
public async Task GenericHostProvider_CreateCredentialAsync_LegacyAuthorityBasic_ReturnsBasicCredentialNoWiaCheck() { var input = new InputArguments(new Dictionary <string, string> { ["protocol"] = "https", ["host"] = "example.com", }); const string testUserName = "******"; const string testPassword = "******"; var basicCredential = new GitCredential(testUserName, testPassword); var context = new TestCommandContext { Settings = { LegacyAuthorityOverride = "basic" } }; var basicAuthMock = new Mock <IBasicAuthentication>(); basicAuthMock.Setup(x => x.GetCredentials(It.IsAny <string>(), It.IsAny <string>())) .Returns(basicCredential) .Verifiable(); var wiaAuthMock = new Mock <IWindowsIntegratedAuthentication>(); var provider = new GenericHostProvider(context, basicAuthMock.Object, wiaAuthMock.Object); ICredential credential = await provider.GenerateCredentialAsync(input); Assert.NotNull(credential); Assert.Equal(testUserName, credential.Account); Assert.Equal(testPassword, credential.Password); wiaAuthMock.Verify(x => x.GetIsSupportedAsync(It.IsAny <Uri>()), Times.Never); basicAuthMock.Verify(x => x.GetCredentials(It.IsAny <string>(), It.IsAny <string>()), Times.Once); }
public Task StoreCredentialAsync(InputArguments input) { // It doesn't matter if this is an OAuth access token, or the literal username & password // because we store them the same way, against the same credential key in the store. // The OAuth refresh token is already stored on the 'get' request. string credentialKey = GetCredentialKey(input); ICredential credential = new GitCredential(input.UserName, input.Password); _context.Trace.WriteLine($"Storing credential with key '{credentialKey}'..."); _context.CredentialStore.AddOrUpdate(credentialKey, credential); _context.Trace.WriteLine("Credential was successfully stored."); Uri targetUri = GetTargetUri(input); if (IsBitbucketServer(targetUri)) { // BBS doesn't usually include the username in the urls which means they aren't included in the GET call, // which means if we store only with the username the credentials are never found again ... // This does have the potential to overwrite itself for different BbS accounts, // but typically BbS doesn't encourage multiple user accounts string bbsCredentialKey = GetBitbucketServerCredentialKey(input); _context.Trace.WriteLine($"Storing Bitbucket Server credential with key '{bbsCredentialKey}'..."); _context.CredentialStore.AddOrUpdate(bbsCredentialKey, credential); _context.Trace.WriteLine("Bitbucket Server Credential was successfully stored."); } return(Task.CompletedTask); }
public async Task GetCommand_ExecuteAsync_CallsHostProviderAndWritesCredential() { const string testUserName = "******"; const string testPassword = "******"; // [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Fake credential")] ICredential testCredential = new GitCredential(testUserName, testPassword); var stdin = $"protocol=http\nhost=example.com\n\n"; var expectedStdOutDict = new Dictionary <string, string> { ["protocol"] = "http", ["host"] = "example.com", ["username"] = testUserName, ["password"] = testPassword }; var providerMock = new Mock <IHostProvider>(); providerMock.Setup(x => x.GetCredentialAsync(It.IsAny <InputArguments>())) .ReturnsAsync(testCredential); var providerRegistry = new TestHostProviderRegistry { Provider = providerMock.Object }; var context = new TestCommandContext { Streams = { In = stdin } }; var command = new GetCommand(context, providerRegistry); await command.ExecuteAsync(); IDictionary <string, string> actualStdOutDict = ParseDictionary(context.Streams.Out); providerMock.Verify(x => x.GetCredentialAsync(It.IsAny <InputArguments>()), Times.Once); Assert.Equal(expectedStdOutDict, actualStdOutDict); }
public async Task GitHubHostProvider_GenerateCredentialAsync_Basic_2FARequired_ReturnsCredential() { var input = new InputArguments(new Dictionary <string, string> { ["protocol"] = "https", ["host"] = "github.com", }); var expectedTargetUri = new Uri("https://github.com/"); var expectedUserName = "******"; var expectedPassword = "******"; var expectedAuthCode = "123456"; IEnumerable <string> expectedPatScopes = new[] { GitHubConstants.TokenScopes.Gist, GitHubConstants.TokenScopes.Repo, }; var patValue = "PERSONAL-ACCESS-TOKEN"; var pat = new GitCredential(Constants.PersonalAccessTokenUserName, patValue); var response1 = new AuthenticationResult(GitHubAuthenticationResultType.TwoFactorApp); var response2 = new AuthenticationResult(GitHubAuthenticationResultType.Success, pat); var context = new TestCommandContext(); var ghAuthMock = new Mock <IGitHubAuthentication>(MockBehavior.Strict); ghAuthMock.Setup(x => x.GetAuthenticationAsync(expectedTargetUri, It.IsAny <AuthenticationModes>())) .ReturnsAsync(new AuthenticationPromptResult(new GitCredential(expectedUserName, expectedPassword))); ghAuthMock.Setup(x => x.GetTwoFactorCodeAsync(expectedTargetUri, false)) .ReturnsAsync(expectedAuthCode); var ghApiMock = new Mock <IGitHubRestApi>(MockBehavior.Strict); ghApiMock.Setup(x => x.CreatePersonalAccessTokenAsync(expectedTargetUri, expectedUserName, expectedPassword, null, It.IsAny <IEnumerable <string> >())) .ReturnsAsync(response1); ghApiMock.Setup(x => x.CreatePersonalAccessTokenAsync(expectedTargetUri, expectedUserName, expectedPassword, expectedAuthCode, It.IsAny <IEnumerable <string> >())) .ReturnsAsync(response2); var provider = new GitHubHostProvider(context, ghApiMock.Object, ghAuthMock.Object); ICredential credential = await provider.GenerateCredentialAsync(input); Assert.NotNull(credential); Assert.Equal(Constants.PersonalAccessTokenUserName, credential.UserName); Assert.Equal(patValue, credential.Password); ghApiMock.Verify( x => x.CreatePersonalAccessTokenAsync( expectedTargetUri, expectedUserName, expectedPassword, null, expectedPatScopes), Times.Once); ghApiMock.Verify( x => x.CreatePersonalAccessTokenAsync( expectedTargetUri, expectedUserName, expectedPassword, expectedAuthCode, expectedPatScopes), Times.Once); }
private static void AssertBasicAuth(HttpRequestMessage request, string userName, string password) { string expectedBasicValue = new GitCredential(userName, password).ToBase64String(); AuthenticationHeaderValue authHeader = request.Headers.Authorization; Assert.NotNull(authHeader); Assert.Equal("Basic", authHeader.Scheme); Assert.Equal(expectedBasicValue, authHeader.Parameter); }
public void GitCredential_ToBase64String_ComplexUserPass_ReturnsCorrectString() { const string expected = "aGVsbG8tbXlfbmFtZSBpczpqb2huLmRvZTp0aGlzIWlzQVA0U1NXMFJEOiB3aXRoPyBfbG90cyBvZi8gY2hhcnM="; const string testUserName = "******"; const string testPassword = "******"; var credential = new GitCredential(testUserName, testPassword); string actual = credential.ToBase64String(); Assert.Equal(expected, actual); }
public void GitCredential_ToBase64String_EmptyCredential_ReturnsCorrectString() { const string expected = "Og=="; const string testUserName = ""; const string testPassword = ""; var credential = new GitCredential(testUserName, testPassword); string actual = credential.ToBase64String(); Assert.Equal(expected, actual); }
internal bool HasPassword(GitCredential entry) { if (SeletedAdapter != null) { if (string.IsNullOrEmpty(entry.Username)) { return(SeletedAdapter.Exists(entry.ManagerUrl)); } return(SeletedAdapter.Exists(entry.ManagerUrl, entry.Username)); } return(entry != null && entry.HasStoredPassword); }
public Task StoreCredentialAsync(InputArguments input) { // It doesn't matter if this is an OAuth access token, or the literal username & password // because we store them the same way, against the same credential key in the store. // The OAuth refresh token is already stored on the 'get' request. string credentialKey = GetCredentialKey(input); ICredential credential = new GitCredential(input.UserName, input.Password); _context.Trace.WriteLine($"Storing credential with key '{credentialKey}'..."); _context.CredentialStore.AddOrUpdate(credentialKey, credential); _context.Trace.WriteLine("Credential was successfully stored."); return(Task.CompletedTask); }
internal GitCredential CreateEntry(string url, string username) { GitCredential entry = gitCredentials.GetEntry(url); if (entry != null) { return(null); } entry = new GitCredential() { URL = url }; entry.SetUsername(username); gitCredentials.AddEntry(entry); return(entry); }
public void BasicAuthentication_GetCredentials_ResourceAndUserName_PasswordPromptReturnsCredentials() { const string testResource = "https://example.com"; const string testUserName = "******"; const string testPassword = "******"; var context = new TestCommandContext(); context.Terminal.SecretPrompts["Password"] = testPassword; var basicAuth = new BasicAuthentication(context); GitCredential credential = basicAuth.GetCredentials(testResource, testUserName); Assert.Equal(testUserName, credential.UserName); Assert.Equal(testPassword, credential.Password); }
public async Task <AuthenticationResult> AcquireTokenAsync(Uri targetUri, string username, string password, string authenticationCode, IEnumerable <string> scopes) { EnsureArgument.AbsoluteUri(targetUri, nameof(targetUri)); EnsureArgument.NotNull(scopes, nameof(scopes)); string base64Cred = new GitCredential(username, password).ToBase64String(); Uri requestUri = GetAuthenticationRequestUri(targetUri); _context.Trace.WriteLine($"HTTP: POST {requestUri}"); using (HttpContent content = GetTokenJsonContent(targetUri, scopes)) using (var request = new HttpRequestMessage(HttpMethod.Post, requestUri)) { // Set the request content as well as auth and 2FA headers request.Content = content; request.Headers.Authorization = new AuthenticationHeaderValue(Constants.Http.WwwAuthenticateBasicScheme, base64Cred); if (!string.IsNullOrWhiteSpace(authenticationCode)) { request.Headers.Add(GitHubConstants.GitHubOptHeader, authenticationCode); } // Send the request! using (HttpResponseMessage response = await HttpClient.SendAsync(request)) { _context.Trace.WriteLine($"HTTP: Response {(int) response.StatusCode} [{response.StatusCode}]"); switch (response.StatusCode) { case HttpStatusCode.OK: case HttpStatusCode.Created: return(await ParseSuccessResponseAsync(targetUri, response)); case HttpStatusCode.Unauthorized: return(ParseUnauthorizedResponse(targetUri, authenticationCode, response)); case HttpStatusCode.Forbidden: return(await ParseForbiddenResponseAsync(targetUri, password, response)); default: _context.Trace.WriteLine($"Authentication failed for '{targetUri}'."); return(new AuthenticationResult(GitHubAuthenticationResultType.Failure)); } } } }
private CredentialsHandler GetCredentialsHandler(GitCredential gitCredential) { return((url, usernameFromUrl, types) => { switch (gitCredential.AuthorizationMode) { case AuthorizationMode.UserNameAndPassword: return new UsernamePasswordCredentials { Username = gitCredential.UserName, Password = gitCredential.Password }; default: throw new ArgumentException("Invalid AuthorizationMode"); } }); }
internal void RemoveCredentials(GitCredential credential, bool removeFromManager) { if (removeFromManager) { if (SeletedAdapter != null) { try { } catch (Exception e) { logger.LogFormat(LogType.Error, "There was an error while trying to remove credentials form {0}", GetAdapterName(SeletedAdapter)); logger.LogException(e); } } } gitCredentials.RemoveEntry(credential); gitCredentials.MarkDirty(); }
internal static GitCredential CreatEntry(string url, string username, string password) { GitCredential entry = gitCredentials.GetEntry(url); if (entry != null) { return(null); } entry = new GitCredential() { URL = url }; entry.SetUsername(username); gitCredentials.AddEntry(entry); if (!string.IsNullOrEmpty(password)) { SetNewPassword(url, username, password); } return(entry); }
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 bool CommitAndPush(string workdir, string message, GitCredential gitCredential) { using (var repo = new Repository(workdir)) { Commands.Stage(repo, "*"); // Create the committer's signature and commit Signature author = new Signature("GameArchiveSync", "*****@*****.**", DateTime.Now); Signature committer = author; // Commit to the repository Commit commit = repo.Commit(message, author, committer); // Push var pushOpt = new PushOptions() { CredentialsProvider = this.GetCredentialsHandler(gitCredential) }; repo.Network.Push(repo.Branches["master"], pushOpt); return(true); } }
internal static string LoadPassword(GitCredential entry) { string pass = null; if (SeletedAdapter != null) { try { if (!SeletedAdapter.LoadPassword(SeletedAdapter.FormatUrl(entry.URL), ref pass)) { Debug.LogFormat("Could not load password with URL: {0} from {1}", entry.URL, GetAdapterName(SeletedAdapter)); } } catch (Exception e) { Debug.LogError("There was an error while trying to load credentials from Windows Credentials Manager"); Debug.LogException(e); } } return(pass ?? entry.DecryptPassword()); }
private bool TryGet( string url, string username, string targetKey, string message, bool isNameReadonly, out IGitCredential gitCredential) { CredentialsDialog dialog = null; UiThread.Run(() => dialog = ShowDialog(targetKey, username, message, isNameReadonly)); if (dialog != null) { gitCredential = new GitCredential(dialog, url); return(true); } Log.Debug("Credentials dialog canceled"); gitCredential = null; return(false); }
internal SecureString LoadPassword(GitCredential entry) { if (SeletedAdapter != null) { try { SecureString pass; if (SeletedAdapter.LoadPassword(entry.ManagerUrl, out pass)) { return(pass); } } catch (Exception e) { logger.Log(LogType.Error, "There was an error while trying to load credentials from Windows Credentials Manager"); logger.LogException(e); } return(new SecureString()); } return(entry.DecryptPassword()); }
internal string LoadUsername(GitCredential entry) { if (SeletedAdapter != null) { try { string username; if (SeletedAdapter.LoadUsername(entry.ManagerUrl, out username)) { return(username); } } catch (Exception e) { logger.Log(LogType.Error, "There was an error while trying to load credentials from Windows Credentials Manager"); logger.LogException(e); } return(""); } return(entry.Username); }
public async Task <int> AddGitCredentialsToProject(GitCredentialsDTO gitCredentialsDTO, int authorId) { var project = await _context.Projects .SingleOrDefaultAsync(p => p.Id == Convert.ToInt32(gitCredentialsDTO.ProjectId)); var gitCredentials = new GitCredential { Login = gitCredentialsDTO.Login, Url = gitCredentialsDTO.Url, Provider = gitCredentialsDTO.Provider, PasswordHash = gitCredentialsDTO.Password }; project.GitCredential = gitCredentials; project.ProjectLink = gitCredentials.Url.Substring(0, gitCredentials.Url.LastIndexOf('.')); await _context.GitCredentials.AddAsync(gitCredentials); await _context.SaveChangesAsync(); await Clone(gitCredentialsDTO.ProjectId, project.ProjectLink, authorId); return(gitCredentials.Id); }
public AuthenticationResult(GitHubAuthenticationResultType type, GitCredential token) { Type = type; Token = token; }
internal void ClearCredentialPassword(GitCredential entry) { entry.ClearPassword(); gitCredentials.MarkDirty(); }