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 ;
@@ -87,8 +89,9 @@ public async Task GenericHostProvider_CreateCredentialAsync_WiaNotAllowed_Return
87
89
. ReturnsAsync ( basicCredential )
88
90
. Verifiable ( ) ;
89
91
var wiaAuthMock = new Mock < IWindowsIntegratedAuthentication > ( ) ;
92
+ var oauthMock = new Mock < IOAuthAuthentication > ( ) ;
90
93
91
- var provider = new GenericHostProvider ( context , basicAuthMock . Object , wiaAuthMock . Object ) ;
94
+ var provider = new GenericHostProvider ( context , basicAuthMock . Object , wiaAuthMock . Object , oauthMock . Object ) ;
92
95
93
96
ICredential credential = await provider . GenerateCredentialAsync ( input ) ;
94
97
@@ -121,8 +124,9 @@ public async Task GenericHostProvider_CreateCredentialAsync_LegacyAuthorityBasic
121
124
. ReturnsAsync ( basicCredential )
122
125
. Verifiable ( ) ;
123
126
var wiaAuthMock = new Mock < IWindowsIntegratedAuthentication > ( ) ;
127
+ var oauthMock = new Mock < IOAuthAuthentication > ( ) ;
124
128
125
- var provider = new GenericHostProvider ( context , basicAuthMock . Object , wiaAuthMock . Object ) ;
129
+ var provider = new GenericHostProvider ( context , basicAuthMock . Object , wiaAuthMock . Object , oauthMock . Object ) ;
126
130
127
131
ICredential credential = await provider . GenerateCredentialAsync ( input ) ;
128
132
@@ -152,8 +156,9 @@ public async Task GenericHostProvider_CreateCredentialAsync_NonHttpProtocol_Retu
152
156
. ReturnsAsync ( basicCredential )
153
157
. Verifiable ( ) ;
154
158
var wiaAuthMock = new Mock < IWindowsIntegratedAuthentication > ( ) ;
159
+ var oauthMock = new Mock < IOAuthAuthentication > ( ) ;
155
160
156
- var provider = new GenericHostProvider ( context , basicAuthMock . Object , wiaAuthMock . Object ) ;
161
+ var provider = new GenericHostProvider ( context , basicAuthMock . Object , wiaAuthMock . Object , oauthMock . Object ) ;
157
162
158
163
ICredential credential = await provider . GenerateCredentialAsync ( input ) ;
159
164
@@ -182,6 +187,90 @@ public async Task GenericHostProvider_CreateCredentialAsync_WiaNotSupported_Retu
182
187
await TestCreateCredentialAsync_ReturnsBasicCredential ( wiaSupported : false ) ;
183
188
}
184
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" ] = "git.example.com" ,
197
+ [ "path" ] = "foo"
198
+ } ) ;
199
+
200
+ const string testUserName = "TEST_OAUTH_USER" ;
201
+ const string testAcessToken = "OAUTH_TOKEN" ;
202
+ const string testRefreshToken = "OAUTH_REFRESH_TOKEN" ;
203
+ const string testResource = "https://git.example.com/foo" ;
204
+ const string expectedRefreshTokenService = "https://refresh_token.git.example.com/foo" ;
205
+
206
+ var authMode = OAuthAuthenticationModes . Browser ;
207
+ string [ ] scopes = { "code:write" , "code:read" } ;
208
+ string clientId = "3eadfc62-9e91-45d3-8c60-20ccd6d0c7cf" ;
209
+ string clientSecret = "C1DA8B93CCB5F5B93DA" ;
210
+ string redirectUri = "http://localhost" ;
211
+ string authzEndpoint = "/oauth/authorize" ;
212
+ string tokenEndpoint = "/oauth/token" ;
213
+ string deviceEndpoint = "/oauth/device" ;
214
+
215
+ string GetKey ( string name ) => $ "{ Constants . GitConfiguration . Credential . SectionName } .https://example.com.{ name } ";
216
+
217
+ var context = new TestCommandContext
218
+ {
219
+ Git =
220
+ {
221
+ Configuration =
222
+ {
223
+ Global =
224
+ {
225
+ [ GetKey ( Constants . GitConfiguration . Credential . OAuthClientId ) ] = new [ ] { clientId } ,
226
+ [ GetKey ( Constants . GitConfiguration . Credential . OAuthClientSecret ) ] = new [ ] { clientSecret } ,
227
+ [ GetKey ( Constants . GitConfiguration . Credential . OAuthRedirectUri ) ] = new [ ] { redirectUri } ,
228
+ [ GetKey ( Constants . GitConfiguration . Credential . OAuthScopes ) ] = new [ ] { string . Join ( ' ' , scopes ) } ,
229
+ [ GetKey ( Constants . GitConfiguration . Credential . OAuthAuthzEndpoint ) ] = new [ ] { authzEndpoint } ,
230
+ [ GetKey ( Constants . GitConfiguration . Credential . OAuthTokenEndpoint ) ] = new [ ] { tokenEndpoint } ,
231
+ [ GetKey ( Constants . GitConfiguration . Credential . OAuthDeviceEndpoint ) ] = new [ ] { deviceEndpoint } ,
232
+ [ GetKey ( Constants . GitConfiguration . Credential . OAuthDefaultUserName ) ] = new [ ] { testUserName } ,
233
+ }
234
+ }
235
+ } ,
236
+ Settings =
237
+ {
238
+ RemoteUri = new Uri ( testResource )
239
+ }
240
+ } ;
241
+
242
+ var basicAuthMock = new Mock < IBasicAuthentication > ( ) ;
243
+ var wiaAuthMock = new Mock < IWindowsIntegratedAuthentication > ( ) ;
244
+ var oauthMock = new Mock < IOAuthAuthentication > ( ) ;
245
+ oauthMock . Setup ( x =>
246
+ x . GetAuthenticationModeAsync ( It . IsAny < string > ( ) , It . IsAny < OAuthAuthenticationModes > ( ) ) )
247
+ . ReturnsAsync ( authMode ) ;
248
+ oauthMock . Setup ( x => x . GetTokenByBrowserAsync ( It . IsAny < OAuth2Client > ( ) , It . IsAny < string [ ] > ( ) ) )
249
+ . ReturnsAsync ( new OAuth2TokenResult ( testAcessToken , "access_token" )
250
+ {
251
+ Scopes = scopes ,
252
+ RefreshToken = testRefreshToken
253
+ } ) ;
254
+
255
+ var provider = new GenericHostProvider ( context , basicAuthMock . Object , wiaAuthMock . Object , oauthMock . Object ) ;
256
+
257
+ ICredential credential = await provider . GenerateCredentialAsync ( input ) ;
258
+
259
+ Assert . NotNull ( credential ) ;
260
+ Assert . Equal ( testUserName , credential . Account ) ;
261
+ Assert . Equal ( testAcessToken , credential . Password ) ;
262
+
263
+ Assert . True ( context . CredentialStore . TryGet ( expectedRefreshTokenService , null , out TestCredential refreshToken ) ) ;
264
+ Assert . Equal ( testUserName , refreshToken . Account ) ;
265
+ Assert . Equal ( testRefreshToken , refreshToken . Password ) ;
266
+
267
+ oauthMock . Verify ( x => x . GetAuthenticationModeAsync ( testResource , OAuthAuthenticationModes . All ) , Times . Once ) ;
268
+ oauthMock . Verify ( x => x . GetTokenByBrowserAsync ( It . IsAny < OAuth2Client > ( ) , scopes ) , Times . Once ) ;
269
+ oauthMock . Verify ( x => x . GetTokenByDeviceCodeAsync ( It . IsAny < OAuth2Client > ( ) , scopes ) , Times . Never ) ;
270
+ wiaAuthMock . Verify ( x => x . GetIsSupportedAsync ( It . IsAny < Uri > ( ) ) , Times . Never ) ;
271
+ basicAuthMock . Verify ( x => x . GetCredentialsAsync ( It . IsAny < string > ( ) , It . IsAny < string > ( ) ) , Times . Never ) ;
272
+ }
273
+
185
274
#region Helpers
186
275
187
276
private static async Task TestCreateCredentialAsync_ReturnsEmptyCredential ( bool wiaSupported )
@@ -199,8 +288,9 @@ private static async Task TestCreateCredentialAsync_ReturnsEmptyCredential(bool
199
288
var wiaAuthMock = new Mock < IWindowsIntegratedAuthentication > ( ) ;
200
289
wiaAuthMock . Setup ( x => x . GetIsSupportedAsync ( It . IsAny < Uri > ( ) ) )
201
290
. ReturnsAsync ( wiaSupported ) ;
291
+ var oauthMock = new Mock < IOAuthAuthentication > ( ) ;
202
292
203
- var provider = new GenericHostProvider ( context , basicAuthMock . Object , wiaAuthMock . Object ) ;
293
+ var provider = new GenericHostProvider ( context , basicAuthMock . Object , wiaAuthMock . Object , oauthMock . Object ) ;
204
294
205
295
ICredential credential = await provider . GenerateCredentialAsync ( input ) ;
206
296
@@ -230,8 +320,9 @@ private static async Task TestCreateCredentialAsync_ReturnsBasicCredential(bool
230
320
var wiaAuthMock = new Mock < IWindowsIntegratedAuthentication > ( ) ;
231
321
wiaAuthMock . Setup ( x => x . GetIsSupportedAsync ( It . IsAny < Uri > ( ) ) )
232
322
. ReturnsAsync ( wiaSupported ) ;
323
+ var oauthMock = new Mock < IOAuthAuthentication > ( ) ;
233
324
234
- var provider = new GenericHostProvider ( context , basicAuthMock . Object , wiaAuthMock . Object ) ;
325
+ var provider = new GenericHostProvider ( context , basicAuthMock . Object , wiaAuthMock . Object , oauthMock . Object ) ;
235
326
236
327
ICredential credential = await provider . GenerateCredentialAsync ( input ) ;
237
328
0 commit comments