1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using System . Net ;
4
+ using System . Net . Http ;
5
+ using System . Threading ;
6
+ using System . Threading . Tasks ;
7
+ using Atlassian . Bitbucket . DataCenter ;
8
+ using GitCredentialManager ;
9
+ using GitCredentialManager . Authentication . OAuth ;
10
+ using Moq ;
11
+ using Xunit ;
12
+
13
+ namespace Atlassian . Bitbucket . Tests . DataCenter
14
+ {
15
+ public class BitbucketOAuth2ClientTest
16
+ {
17
+ private Mock < HttpClient > httpClient = new Mock < HttpClient > ( MockBehavior . Strict ) ;
18
+ private Mock < ISettings > settings = new Mock < ISettings > ( MockBehavior . Loose ) ;
19
+ private Mock < Trace > trace = new Mock < Trace > ( MockBehavior . Loose ) ;
20
+ private Mock < IOAuth2WebBrowser > browser = new Mock < IOAuth2WebBrowser > ( MockBehavior . Strict ) ;
21
+ private Mock < IOAuth2CodeGenerator > codeGenerator = new Mock < IOAuth2CodeGenerator > ( MockBehavior . Strict ) ;
22
+ private CancellationToken ct = new CancellationToken ( ) ;
23
+ private Uri rootCallbackUri = new Uri ( "http://localhost:34106/" ) ;
24
+ private string nonce = "12345" ;
25
+ private string pkceCodeVerifier = "abcde" ;
26
+ private string pkceCodeChallenge = "xyz987" ;
27
+ private string authorization_code = "authorization_token" ;
28
+
29
+ [ Fact ]
30
+ public async Task BitbucketOAuth2Client_GetAuthorizationCodeAsync_ReturnsCode ( )
31
+ {
32
+ var remoteUrl = MockRemoteUri ( "http://example.com" ) ;
33
+ var clientId = MockClientIdOverride ( "dc-client-id" ) ;
34
+ MockClientSecretOverride ( "dc-client-seccret" ) ;
35
+
36
+ Uri finalCallbackUri = MockFinalCallbackUri ( rootCallbackUri ) ;
37
+
38
+ var client = GetBitbucketOAuth2Client ( ) ;
39
+
40
+ MockGetAuthenticationCodeAsync ( remoteUrl , rootCallbackUri , finalCallbackUri , clientId , client . Scopes ) ;
41
+
42
+ MockCodeGenerator ( ) ;
43
+
44
+ var result = await client . GetAuthorizationCodeAsync ( browser . Object , ct ) ;
45
+
46
+ VerifyAuthorizationCodeResult ( result , rootCallbackUri ) ;
47
+ }
48
+
49
+ [ Fact ]
50
+ public async Task BitbucketOAuth2Client_GetAuthorizationCodeAsync_ReturnsCode_WhileRespectingRedirectUriOverride ( )
51
+ {
52
+ var rootCallbackUrl = MockRootCallbackUriOverride ( "http://localhost:12345/" ) ;
53
+ var remoteUrl = MockRemoteUri ( "http://example.com" ) ;
54
+ var clientId = MockClientIdOverride ( "dc-client-id" ) ;
55
+ MockClientSecretOverride ( "dc-client-seccret" ) ;
56
+
57
+ Uri finalCallbackUri = MockFinalCallbackUri ( new Uri ( rootCallbackUrl ) ) ;
58
+
59
+ var client = GetBitbucketOAuth2Client ( ) ;
60
+
61
+ MockGetAuthenticationCodeAsync ( remoteUrl , new Uri ( rootCallbackUrl ) , finalCallbackUri , clientId , client . Scopes ) ;
62
+
63
+ MockCodeGenerator ( ) ;
64
+
65
+ var result = await client . GetAuthorizationCodeAsync ( browser . Object , ct ) ;
66
+
67
+ VerifyAuthorizationCodeResult ( result , new Uri ( rootCallbackUrl ) ) ;
68
+ }
69
+
70
+ private void VerifyAuthorizationCodeResult ( OAuth2AuthorizationCodeResult result , Uri redirectUri )
71
+ {
72
+ Assert . NotNull ( result ) ;
73
+ Assert . Equal ( authorization_code , result . Code ) ;
74
+ Assert . Equal ( redirectUri , result . RedirectUri ) ;
75
+ Assert . Equal ( pkceCodeVerifier , result . CodeVerifier ) ;
76
+ }
77
+
78
+ private Bitbucket . DataCenter . BitbucketOAuth2Client GetBitbucketOAuth2Client ( )
79
+ {
80
+ var client = new Bitbucket . DataCenter . BitbucketOAuth2Client ( httpClient . Object , settings . Object , trace . Object ) ;
81
+ client . CodeGenerator = codeGenerator . Object ;
82
+ return client ;
83
+ }
84
+
85
+ private void MockCodeGenerator ( )
86
+ {
87
+ codeGenerator . Setup ( c => c . CreateNonce ( ) ) . Returns ( nonce ) ;
88
+ codeGenerator . Setup ( c => c . CreatePkceCodeVerifier ( ) ) . Returns ( pkceCodeVerifier ) ;
89
+ codeGenerator . Setup ( c => c . CreatePkceCodeChallenge ( OAuth2PkceChallengeMethod . Sha256 , pkceCodeVerifier ) ) . Returns ( pkceCodeChallenge ) ;
90
+ }
91
+
92
+ private void MockGetAuthenticationCodeAsync ( string url , Uri redirectUri , Uri finalCallbackUri , string overrideClientId , IEnumerable < string > scopes )
93
+ {
94
+ var authorizationUri = new UriBuilder ( url + "/rest/oauth2/latest/authorize" )
95
+ {
96
+ Query = "?response_type=code"
97
+ + "&client_id=" + ( overrideClientId ?? "clientId" )
98
+ + "&state=12345"
99
+ + "&code_challenge_method=" + OAuth2Constants . AuthorizationEndpoint . PkceChallengeMethodS256
100
+ + "&code_challenge=" + WebUtility . UrlEncode ( pkceCodeChallenge ) . ToLower ( )
101
+ + "&redirect_uri=" + WebUtility . UrlEncode ( redirectUri . AbsoluteUri ) . ToLower ( )
102
+ + "&scope=" + WebUtility . UrlEncode ( string . Join ( " " , scopes ) ) . ToUpper ( )
103
+ } . Uri ;
104
+
105
+ browser . Setup ( b => b . GetAuthenticationCodeAsync ( authorizationUri , redirectUri , ct ) ) . Returns ( Task . FromResult ( finalCallbackUri ) ) ;
106
+ }
107
+
108
+ private Uri MockFinalCallbackUri ( Uri redirectUri )
109
+ {
110
+ var finalUri = new Uri ( rootCallbackUri , "?state=" + nonce + "&code=" + authorization_code ) ;
111
+ // This is a simplification but consistent
112
+ browser . Setup ( b => b . UpdateRedirectUri ( redirectUri ) ) . Returns ( redirectUri ) ;
113
+ return finalUri ;
114
+ }
115
+
116
+ private string MockRemoteUri ( string value )
117
+ {
118
+ settings . Setup ( s => s . RemoteUri ) . Returns ( new Uri ( value ) ) ;
119
+ return value ;
120
+ }
121
+
122
+ private string MockClientIdOverride ( string value )
123
+ {
124
+ settings . Setup ( s => s . TryGetSetting (
125
+ DataCenterConstants . EnvironmentVariables . OAuthClientId ,
126
+ Constants . GitConfiguration . Credential . SectionName , DataCenterConstants . GitConfiguration . Credential . OAuthClientId ,
127
+ out value ) ) . Returns ( true ) ;
128
+ return value ;
129
+ }
130
+
131
+ private string MockClientSecretOverride ( string value )
132
+ {
133
+ settings . Setup ( s => s . TryGetSetting (
134
+ DataCenterConstants . EnvironmentVariables . OAuthClientSecret ,
135
+ Constants . GitConfiguration . Credential . SectionName , DataCenterConstants . GitConfiguration . Credential . OAuthClientSecret ,
136
+ out value ) ) . Returns ( true ) ;
137
+ return value ;
138
+ }
139
+
140
+ private string MockRootCallbackUriOverride ( string value )
141
+ {
142
+ settings . Setup ( s => s . TryGetSetting (
143
+ DataCenterConstants . EnvironmentVariables . OAuthRedirectUri ,
144
+ Constants . GitConfiguration . Credential . SectionName , DataCenterConstants . GitConfiguration . Credential . OAuthRedirectUri ,
145
+ out value ) ) . Returns ( true ) ;
146
+ return value ;
147
+ }
148
+ }
149
+ }
0 commit comments