|
1 | 1 | using System;
|
2 | 2 | using System.Collections.Generic;
|
| 3 | +using System.Net.Http; |
3 | 4 | using System.Threading.Tasks;
|
4 | 5 | using GitCredentialManager.Authentication;
|
| 6 | +using GitCredentialManager.Authentication.OAuth; |
5 | 7 | using GitCredentialManager.Tests.Objects;
|
6 | 8 | using Moq;
|
7 | 9 | using Xunit;
|
@@ -185,6 +187,89 @@ public async Task GenericHostProvider_CreateCredentialAsync_WiaNotSupported_Retu
|
185 | 187 | await TestCreateCredentialAsync_ReturnsBasicCredential(wiaSupported: false);
|
186 | 188 | }
|
187 | 189 |
|
| 190 | + [Fact] |
| 191 | + public async Task GenericHostProvider_GenerateCredentialAsync_OAuth_CompleteOAuthConfig_UsesOAuth() |
| 192 | + { |
| 193 | + var input = new InputArguments(new Dictionary<string, string> |
| 194 | + { |
| 195 | + ["protocol"] = "https", |
| 196 | + ["host"] = "example.com", |
| 197 | + }); |
| 198 | + |
| 199 | + const string testUserName = "TEST_OAUTH_USER"; |
| 200 | + const string testAcessToken = "OAUTH_TOKEN"; |
| 201 | + const string testRefreshToken = "OAUTH_REFRESH_TOKEN"; |
| 202 | + const string testResource = "https://example.com/"; |
| 203 | + const string expectedRefreshTokenService = "https://example.com/refresh_token"; |
| 204 | + |
| 205 | + var authMode = OAuthAuthenticationModes.Browser; |
| 206 | + string[] scopes = { "code:write", "code:read" }; |
| 207 | + string clientId = "3eadfc62-9e91-45d3-8c60-20ccd6d0c7cf"; |
| 208 | + string clientSecret = "C1DA8B93CCB5F5B93DA"; |
| 209 | + string redirectUri = "http://localhost"; |
| 210 | + string authzEndpoint = "/oauth/authorize"; |
| 211 | + string tokenEndpoint = "/oauth/token"; |
| 212 | + string deviceEndpoint = "/oauth/device"; |
| 213 | + |
| 214 | + string GetKey(string name) => $"{Constants.GitConfiguration.Credential.SectionName}.https://example.com.{name}"; |
| 215 | + |
| 216 | + var context = new TestCommandContext |
| 217 | + { |
| 218 | + Git = |
| 219 | + { |
| 220 | + Configuration = |
| 221 | + { |
| 222 | + Global = |
| 223 | + { |
| 224 | + [GetKey(Constants.GitConfiguration.Credential.OAuthClientId)] = new[] { clientId }, |
| 225 | + [GetKey(Constants.GitConfiguration.Credential.OAuthClientSecret)] = new[] { clientSecret }, |
| 226 | + [GetKey(Constants.GitConfiguration.Credential.OAuthRedirectUri)] = new[] { redirectUri }, |
| 227 | + [GetKey(Constants.GitConfiguration.Credential.OAuthScopes)] = new[] { string.Join(' ', scopes) }, |
| 228 | + [GetKey(Constants.GitConfiguration.Credential.OAuthAuthzEndpoint)] = new[] { authzEndpoint }, |
| 229 | + [GetKey(Constants.GitConfiguration.Credential.OAuthTokenEndpoint)] = new[] { tokenEndpoint }, |
| 230 | + [GetKey(Constants.GitConfiguration.Credential.OAuthDeviceEndpoint)] = new[] { deviceEndpoint }, |
| 231 | + [GetKey(Constants.GitConfiguration.Credential.OAuthDefaultUserName)] = new[] { testUserName }, |
| 232 | + } |
| 233 | + } |
| 234 | + }, |
| 235 | + Settings = |
| 236 | + { |
| 237 | + RemoteUri = new Uri(testResource) |
| 238 | + } |
| 239 | + }; |
| 240 | + |
| 241 | + var basicAuthMock = new Mock<IBasicAuthentication>(); |
| 242 | + var wiaAuthMock = new Mock<IWindowsIntegratedAuthentication>(); |
| 243 | + var oauthMock = new Mock<IOAuthAuthentication>(); |
| 244 | + oauthMock.Setup(x => |
| 245 | + x.GetAuthenticationModeAsync(It.IsAny<string>(), It.IsAny<OAuthAuthenticationModes>())) |
| 246 | + .ReturnsAsync(authMode); |
| 247 | + oauthMock.Setup(x => x.GetTokenByBrowserAsync(It.IsAny<OAuth2Client>(), It.IsAny<string[]>())) |
| 248 | + .ReturnsAsync(new OAuth2TokenResult(testAcessToken, "access_token") |
| 249 | + { |
| 250 | + Scopes = scopes, |
| 251 | + RefreshToken = testRefreshToken |
| 252 | + }); |
| 253 | + |
| 254 | + var provider = new GenericHostProvider(context, basicAuthMock.Object, wiaAuthMock.Object, oauthMock.Object); |
| 255 | + |
| 256 | + ICredential credential = await provider.GenerateCredentialAsync(input); |
| 257 | + |
| 258 | + Assert.NotNull(credential); |
| 259 | + Assert.Equal(testUserName, credential.Account); |
| 260 | + Assert.Equal(testAcessToken, credential.Password); |
| 261 | + |
| 262 | + Assert.True(context.CredentialStore.TryGet(expectedRefreshTokenService, null, out TestCredential refreshToken)); |
| 263 | + Assert.Equal(testUserName, refreshToken.Account); |
| 264 | + Assert.Equal(testRefreshToken, refreshToken.Password); |
| 265 | + |
| 266 | + oauthMock.Verify(x => x.GetAuthenticationModeAsync(testResource, OAuthAuthenticationModes.All), Times.Once); |
| 267 | + oauthMock.Verify(x => x.GetTokenByBrowserAsync(It.IsAny<OAuth2Client>(), scopes), Times.Once); |
| 268 | + oauthMock.Verify(x => x.GetTokenByDeviceCodeAsync(It.IsAny<OAuth2Client>(), scopes), Times.Never); |
| 269 | + wiaAuthMock.Verify(x => x.GetIsSupportedAsync(It.IsAny<Uri>()), Times.Never); |
| 270 | + basicAuthMock.Verify(x => x.GetCredentialsAsync(It.IsAny<string>(), It.IsAny<string>()), Times.Never); |
| 271 | + } |
| 272 | + |
188 | 273 | #region Helpers
|
189 | 274 |
|
190 | 275 | private static async Task TestCreateCredentialAsync_ReturnsEmptyCredential(bool wiaSupported)
|
|
0 commit comments