@@ -12,7 +12,7 @@ namespace GitLab
12
12
{
13
13
public interface IGitLabAuthentication : IDisposable
14
14
{
15
- AuthenticationPromptResult GetAuthentication ( Uri targetUri , string userName , AuthenticationModes modes ) ;
15
+ Task < AuthenticationPromptResult > GetAuthenticationAsync ( Uri targetUri , string userName , AuthenticationModes modes ) ;
16
16
17
17
Task < OAuth2TokenResult > GetOAuthTokenViaBrowserAsync ( Uri targetUri , IEnumerable < string > scopes ) ;
18
18
@@ -53,7 +53,7 @@ public class GitLabAuthentication : AuthenticationBase, IGitLabAuthentication
53
53
public GitLabAuthentication ( ICommandContext context )
54
54
: base ( context ) { }
55
55
56
- public AuthenticationPromptResult GetAuthentication ( Uri targetUri , string userName , AuthenticationModes modes )
56
+ public async Task < AuthenticationPromptResult > GetAuthenticationAsync ( Uri targetUri , string userName , AuthenticationModes modes )
57
57
{
58
58
// If we don't have a desktop session/GUI then we cannot offer browser
59
59
if ( ! Context . SessionManager . IsDesktopSession )
@@ -67,70 +67,130 @@ public AuthenticationPromptResult GetAuthentication(Uri targetUri, string userNa
67
67
throw new ArgumentException ( @$ "Must specify at least one { nameof ( AuthenticationModes ) } ", nameof ( modes ) ) ;
68
68
}
69
69
70
- switch ( modes )
70
+ if ( Context . Settings . IsGuiPromptsEnabled && Context . SessionManager . IsDesktopSession &&
71
+ TryFindHelperExecutablePath ( out string helperPath ) )
71
72
{
72
- case AuthenticationModes . Basic :
73
- ThrowIfUserInteractionDisabled ( ) ;
74
- ThrowIfTerminalPromptsDisabled ( ) ;
75
- Context . Terminal . WriteLine ( "Enter GitLab credentials for '{0}'..." , targetUri ) ;
76
-
77
- if ( string . IsNullOrWhiteSpace ( userName ) )
78
- {
79
- userName = Context . Terminal . Prompt ( "Username" ) ;
80
- }
81
- else
82
- {
83
- Context . Terminal . WriteLine ( "Username: {0}" , userName ) ;
84
- }
85
-
86
- string password = Context . Terminal . PromptSecret ( "Password" ) ;
87
- return new AuthenticationPromptResult ( AuthenticationModes . Basic , new GitCredential ( userName , password ) ) ;
88
-
89
- case AuthenticationModes . Pat :
90
- ThrowIfUserInteractionDisabled ( ) ;
91
- ThrowIfTerminalPromptsDisabled ( ) ;
92
- Context . Terminal . WriteLine ( "Enter GitLab credentials for '{0}'..." , targetUri ) ;
93
-
94
- if ( string . IsNullOrWhiteSpace ( userName ) )
95
- {
96
- userName = Context . Terminal . Prompt ( "Username" ) ;
97
- }
98
- else
99
- {
100
- Context . Terminal . WriteLine ( "Username: {0}" , userName ) ;
101
- }
102
-
103
- string token = Context . Terminal . PromptSecret ( "Personal access token" ) ;
104
- return new AuthenticationPromptResult ( AuthenticationModes . Pat , new GitCredential ( userName , token ) ) ;
105
-
106
- case AuthenticationModes . Browser :
107
- return new AuthenticationPromptResult ( AuthenticationModes . Browser ) ;
108
-
109
- case AuthenticationModes . None :
110
- throw new ArgumentOutOfRangeException ( nameof ( modes ) , @$ "At least one { nameof ( AuthenticationModes ) } must be supplied") ;
111
-
112
- default :
113
- ThrowIfUserInteractionDisabled ( ) ;
114
- ThrowIfTerminalPromptsDisabled ( ) ;
115
- var menuTitle = $ "Select an authentication method for '{ targetUri } '";
116
- var menu = new TerminalMenu ( Context . Terminal , menuTitle ) ;
117
-
118
- TerminalMenuItem browserItem = null ;
119
- TerminalMenuItem basicItem = null ;
120
- TerminalMenuItem patItem = null ;
121
-
122
- if ( ( modes & AuthenticationModes . Browser ) != 0 ) browserItem = menu . Add ( "Web browser" ) ;
123
- if ( ( modes & AuthenticationModes . Pat ) != 0 ) patItem = menu . Add ( "Personal access token" ) ;
124
- if ( ( modes & AuthenticationModes . Basic ) != 0 ) basicItem = menu . Add ( "Username/password" ) ;
125
-
126
- // Default to the 'first' choice in the menu
127
- TerminalMenuItem choice = menu . Show ( 0 ) ;
128
-
129
- if ( choice == browserItem ) goto case AuthenticationModes . Browser ;
130
- if ( choice == basicItem ) goto case AuthenticationModes . Basic ;
131
- if ( choice == patItem ) goto case AuthenticationModes . Pat ;
132
-
133
- throw new Exception ( ) ;
73
+ var cmdArgs = new StringBuilder ( "prompt" ) ;
74
+ if ( ! string . IsNullOrWhiteSpace ( userName ) )
75
+ {
76
+ cmdArgs . AppendFormat ( " --username {0}" , QuoteCmdArg ( userName ) ) ;
77
+ }
78
+
79
+ if ( ( modes & AuthenticationModes . Basic ) != 0 ) cmdArgs . Append ( " --basic" ) ;
80
+ if ( ( modes & AuthenticationModes . Browser ) != 0 ) cmdArgs . Append ( " --browser" ) ;
81
+ if ( ( modes & AuthenticationModes . Pat ) != 0 ) cmdArgs . Append ( " --pat" ) ;
82
+
83
+ IDictionary < string , string > resultDict = await InvokeHelperAsync ( helperPath , cmdArgs . ToString ( ) ) ;
84
+
85
+ if ( ! resultDict . TryGetValue ( "mode" , out string responseMode ) )
86
+ {
87
+ throw new Exception ( "Missing 'mode' in response" ) ;
88
+ }
89
+
90
+ switch ( responseMode . ToLowerInvariant ( ) )
91
+ {
92
+ case "pat" :
93
+ if ( ! resultDict . TryGetValue ( "pat" , out string pat ) )
94
+ {
95
+ throw new Exception ( "Missing 'pat' in response" ) ;
96
+ }
97
+
98
+ if ( ! resultDict . TryGetValue ( "username" , out string patUserName ) )
99
+ {
100
+ // Username is optional for PATs
101
+ }
102
+
103
+ return new AuthenticationPromptResult (
104
+ AuthenticationModes . Pat , new GitCredential ( patUserName , pat ) ) ;
105
+
106
+ case "browser" :
107
+ return new AuthenticationPromptResult ( AuthenticationModes . Browser ) ;
108
+
109
+ case "basic" :
110
+ if ( ! resultDict . TryGetValue ( "username" , out userName ) )
111
+ {
112
+ throw new Exception ( "Missing 'username' in response" ) ;
113
+ }
114
+
115
+ if ( ! resultDict . TryGetValue ( "password" , out string password ) )
116
+ {
117
+ throw new Exception ( "Missing 'password' in response" ) ;
118
+ }
119
+
120
+ return new AuthenticationPromptResult (
121
+ AuthenticationModes . Basic , new GitCredential ( userName , password ) ) ;
122
+
123
+ default :
124
+ throw new Exception ( $ "Unknown mode value in response '{ responseMode } '") ;
125
+ }
126
+ }
127
+ else
128
+ {
129
+ switch ( modes )
130
+ {
131
+ case AuthenticationModes . Basic :
132
+ ThrowIfUserInteractionDisabled ( ) ;
133
+ ThrowIfTerminalPromptsDisabled ( ) ;
134
+ Context . Terminal . WriteLine ( "Enter GitLab credentials for '{0}'..." , targetUri ) ;
135
+
136
+ if ( string . IsNullOrWhiteSpace ( userName ) )
137
+ {
138
+ userName = Context . Terminal . Prompt ( "Username" ) ;
139
+ }
140
+ else
141
+ {
142
+ Context . Terminal . WriteLine ( "Username: {0}" , userName ) ;
143
+ }
144
+
145
+ string password = Context . Terminal . PromptSecret ( "Password" ) ;
146
+ return new AuthenticationPromptResult ( AuthenticationModes . Basic , new GitCredential ( userName , password ) ) ;
147
+
148
+ case AuthenticationModes . Pat :
149
+ ThrowIfUserInteractionDisabled ( ) ;
150
+ ThrowIfTerminalPromptsDisabled ( ) ;
151
+ Context . Terminal . WriteLine ( "Enter GitLab credentials for '{0}'..." , targetUri ) ;
152
+
153
+ if ( string . IsNullOrWhiteSpace ( userName ) )
154
+ {
155
+ userName = Context . Terminal . Prompt ( "Username" ) ;
156
+ }
157
+ else
158
+ {
159
+ Context . Terminal . WriteLine ( "Username: {0}" , userName ) ;
160
+ }
161
+
162
+ string token = Context . Terminal . PromptSecret ( "Personal access token" ) ;
163
+ return new AuthenticationPromptResult ( AuthenticationModes . Pat , new GitCredential ( userName , token ) ) ;
164
+
165
+ case AuthenticationModes . Browser :
166
+ return new AuthenticationPromptResult ( AuthenticationModes . Browser ) ;
167
+
168
+ case AuthenticationModes . None :
169
+ throw new ArgumentOutOfRangeException ( nameof ( modes ) , @$ "At least one { nameof ( AuthenticationModes ) } must be supplied") ;
170
+
171
+ default :
172
+ ThrowIfUserInteractionDisabled ( ) ;
173
+ ThrowIfTerminalPromptsDisabled ( ) ;
174
+ var menuTitle = $ "Select an authentication method for '{ targetUri } '";
175
+ var menu = new TerminalMenu ( Context . Terminal , menuTitle ) ;
176
+
177
+ TerminalMenuItem browserItem = null ;
178
+ TerminalMenuItem basicItem = null ;
179
+ TerminalMenuItem patItem = null ;
180
+
181
+ if ( ( modes & AuthenticationModes . Browser ) != 0 ) browserItem = menu . Add ( "Web browser" ) ;
182
+ if ( ( modes & AuthenticationModes . Pat ) != 0 ) patItem = menu . Add ( "Personal access token" ) ;
183
+ if ( ( modes & AuthenticationModes . Basic ) != 0 ) basicItem = menu . Add ( "Username/password" ) ;
184
+
185
+ // Default to the 'first' choice in the menu
186
+ TerminalMenuItem choice = menu . Show ( 0 ) ;
187
+
188
+ if ( choice == browserItem ) goto case AuthenticationModes . Browser ;
189
+ if ( choice == basicItem ) goto case AuthenticationModes . Basic ;
190
+ if ( choice == patItem ) goto case AuthenticationModes . Pat ;
191
+
192
+ throw new Exception ( ) ;
193
+ }
134
194
}
135
195
}
136
196
@@ -164,6 +224,15 @@ public async Task<OAuth2TokenResult> GetOAuthTokenViaRefresh(Uri targetUri, stri
164
224
return await oauthClient . GetTokenByRefreshTokenAsync ( refreshToken , CancellationToken . None ) ;
165
225
}
166
226
227
+ private bool TryFindHelperExecutablePath ( out string path )
228
+ {
229
+ return TryFindHelperExecutablePath (
230
+ GitLabConstants . EnvironmentVariables . AuthenticationHelper ,
231
+ GitLabConstants . GitConfiguration . Credential . AuthenticationHelper ,
232
+ GitLabConstants . DefaultAuthenticationHelper ,
233
+ out path ) ;
234
+ }
235
+
167
236
private HttpClient _httpClient ;
168
237
private HttpClient HttpClient => _httpClient ?? ( _httpClient = Context . HttpClientFactory . CreateClient ( ) ) ;
169
238
0 commit comments