@@ -62,7 +62,7 @@ of this software and associated documentation files (the "Software"), to deal
62
62
import org .apache .http .client .CredentialsProvider ;
63
63
import org .apache .http .client .config .RequestConfig ;
64
64
import org .apache .http .client .methods .HttpPost ;
65
- import org .apache .http .client .methods . HttpUriRequest ;
65
+ import org .apache .http .client .utils . URIBuilder ;
66
66
import org .apache .http .impl .client .BasicCredentialsProvider ;
67
67
import org .apache .http .impl .client .CloseableHttpClient ;
68
68
import org .apache .http .impl .client .HttpClientBuilder ;
@@ -85,6 +85,8 @@ of this software and associated documentation files (the "Software"), to deal
85
85
import java .io .IOException ;
86
86
import java .net .InetSocketAddress ;
87
87
import java .net .Proxy ;
88
+ import java .net .URISyntaxException ;
89
+ import java .nio .charset .StandardCharsets ;
88
90
import java .security .SecureRandom ;
89
91
import java .util .Arrays ;
90
92
import java .util .HashSet ;
@@ -115,6 +117,8 @@ public class GithubSecurityRealm extends AbstractPasswordBasedSecurityRealm impl
115
117
private Secret clientSecret ;
116
118
private String oauthScopes ;
117
119
private String [] myScopes ;
120
+ @ NonNull
121
+ private String redirectUri = "" ;
118
122
119
123
/**
120
124
* @param githubWebUri The URI to the root of the web UI for GitHub or GitHub Enterprise,
@@ -187,6 +191,15 @@ private void setOauthScopes(String oauthScopes) {
187
191
this .oauthScopes = oauthScopes ;
188
192
}
189
193
194
+ /**
195
+ * @param redirectUri the redirectUri to set
196
+ */
197
+ @ DataBoundSetter
198
+ public void setRedirectUri (String redirectUri ) {
199
+ if (null == redirectUri ) redirectUri = "" ;
200
+ this .redirectUri = redirectUri ;
201
+ }
202
+
190
203
/**
191
204
* Checks the security realm for a GitHub OAuth scope.
192
205
* @param scope A scope to check for in the security realm.
@@ -245,6 +258,10 @@ public void marshal(Object source, HierarchicalStreamWriter writer,
245
258
writer .setValue (realm .getOauthScopes ());
246
259
writer .endNode ();
247
260
261
+ writer .startNode ("redirectUri" );
262
+ writer .setValue (realm .getRedirectUri ());
263
+ writer .endNode ();
264
+
248
265
}
249
266
250
267
public Object unmarshal (HierarchicalStreamReader reader ,
@@ -274,8 +291,7 @@ public Object unmarshal(HierarchicalStreamReader reader,
274
291
return realm ;
275
292
}
276
293
277
- private void setValue (GithubSecurityRealm realm , String node ,
278
- String value ) {
294
+ private void setValue (GithubSecurityRealm realm , String node , String value ) {
279
295
if (node .equalsIgnoreCase ("clientid" )) {
280
296
realm .setClientID (value );
281
297
} else if (node .equalsIgnoreCase ("clientsecret" )) {
@@ -290,6 +306,8 @@ private void setValue(GithubSecurityRealm realm, String node,
290
306
realm .setGithubApiUri (value );
291
307
} else if (node .equalsIgnoreCase ("oauthscopes" )) {
292
308
realm .setOauthScopes (value );
309
+ } else if (node .equalsIgnoreCase ("redirecturi" )) {
310
+ realm .setRedirectUri (value );
293
311
} else {
294
312
throw new ConversionException ("Invalid node value = " + node );
295
313
}
@@ -334,11 +352,21 @@ public String getOauthScopes() {
334
352
return oauthScopes ;
335
353
}
336
354
355
+ /**
356
+ * @return the redirectUri
357
+ */
358
+ @ NonNull
359
+ public String getRedirectUri () {
360
+ return redirectUri ;
361
+ }
362
+
337
363
public HttpResponse doCommenceLogin (StaplerRequest request , @ QueryParameter String from , @ Header ("Referer" ) final String referer )
338
- throws IOException {
364
+ throws IOException , URISyntaxException {
339
365
// https://tools.ietf.org/html/rfc6749#section-10.10 dictates that probability that an attacker guesses the string
340
366
// SHOULD be less than or equal to 2^(-160) and our Strings consist of 65 chars. (65^27 ~= 2^160)
341
367
final String state = getSecureRandomString (27 );
368
+
369
+ // This is to go back to the current page after login, not the oauth callback
342
370
String redirectOnFinish ;
343
371
if (from != null && Util .isSafeToRedirectTo (from )) {
344
372
redirectOnFinish = from ;
@@ -355,17 +383,17 @@ public HttpResponse doCommenceLogin(StaplerRequest request, @QueryParameter Stri
355
383
for (GitHubOAuthScope s : Jenkins .get ().getExtensionList (GitHubOAuthScope .class )) {
356
384
scopes .addAll (s .getScopesToRequest ());
357
385
}
358
- String suffix ="" ;
359
- if (!scopes .isEmpty ()) {
360
- suffix = "&scope=" +Util .join (scopes ,"," )+"&state=" +state ;
361
- } else {
362
- // We need repo scope in order to access private repos
363
- // See https://developer.github.com/v3/oauth/#scopes
364
- suffix = "&scope=" + oauthScopes +"&state=" +state ;
386
+
387
+ URIBuilder builder = new URIBuilder (githubWebUri , StandardCharsets .UTF_8 );
388
+ builder .setPath ("/login/oauth/authorize" );
389
+ builder .setParameter ("client_id" , clientID );
390
+ builder .setParameter ("scope" , scopes .isEmpty () ? oauthScopes : Util .join (scopes ,"," ));
391
+ builder .setParameter ("state" , state );
392
+ if (!redirectUri .isEmpty ()) {
393
+ builder .setParameter ("redirect_uri" , redirectUri );
365
394
}
366
395
367
- return new HttpRedirect (githubWebUri + "/login/oauth/authorize?client_id="
368
- + clientID + suffix );
396
+ return new HttpRedirect (builder .toString ());
369
397
}
370
398
371
399
/**
@@ -630,6 +658,12 @@ public String getDefaultOauthScopes() {
630
658
return DEFAULT_OAUTH_SCOPES ;
631
659
}
632
660
661
+ public String getDefaultRequestUri () {
662
+ // Intentionally making this default in UI and not in code
663
+ // to preserve behaviour for existing groovy init & JCasc setups
664
+ return Jenkins .get ().getRootUrl () + "securityRealm/finishLogin" ;
665
+ }
666
+
633
667
public DescriptorImpl () {
634
668
super ();
635
669
// TODO Auto-generated constructor stub
@@ -728,7 +762,8 @@ public boolean equals(Object object){
728
762
this .getGithubApiUri ().equals (obj .getGithubApiUri ()) &&
729
763
this .getClientID ().equals (obj .getClientID ()) &&
730
764
this .getClientSecret ().equals (obj .getClientSecret ()) &&
731
- this .getOauthScopes ().equals (obj .getOauthScopes ());
765
+ this .getOauthScopes ().equals (obj .getOauthScopes ()) &&
766
+ this .getRedirectUri ().equals (obj .getRedirectUri ());
732
767
} else {
733
768
return false ;
734
769
}
@@ -742,6 +777,7 @@ public int hashCode() {
742
777
.append (this .getClientID ())
743
778
.append (this .getClientSecret ())
744
779
.append (this .getOauthScopes ())
780
+ .append (this .getRedirectUri ())
745
781
.toHashCode ();
746
782
}
747
783
0 commit comments