@@ -62,7 +62,7 @@ of this software and associated documentation files (the "Software"), to deal
6262import org .apache .http .client .CredentialsProvider ;
6363import org .apache .http .client .config .RequestConfig ;
6464import org .apache .http .client .methods .HttpPost ;
65- import org .apache .http .client .methods . HttpUriRequest ;
65+ import org .apache .http .client .utils . URIBuilder ;
6666import org .apache .http .impl .client .BasicCredentialsProvider ;
6767import org .apache .http .impl .client .CloseableHttpClient ;
6868import org .apache .http .impl .client .HttpClientBuilder ;
@@ -73,6 +73,7 @@ of this software and associated documentation files (the "Software"), to deal
7373import org .kohsuke .github .GHOrganization ;
7474import org .kohsuke .github .GHTeam ;
7575import org .kohsuke .stapler .DataBoundConstructor ;
76+ import org .kohsuke .stapler .DataBoundSetter ;
7677import org .kohsuke .stapler .Header ;
7778import org .kohsuke .stapler .HttpRedirect ;
7879import org .kohsuke .stapler .HttpResponse ;
@@ -85,6 +86,8 @@ of this software and associated documentation files (the "Software"), to deal
8586import java .io .IOException ;
8687import java .net .InetSocketAddress ;
8788import java .net .Proxy ;
89+ import java .net .URISyntaxException ;
90+ import java .nio .charset .StandardCharsets ;
8891import java .security .SecureRandom ;
8992import java .util .Arrays ;
9093import java .util .HashSet ;
@@ -115,6 +118,8 @@ public class GithubSecurityRealm extends AbstractPasswordBasedSecurityRealm impl
115118 private Secret clientSecret ;
116119 private String oauthScopes ;
117120 private String [] myScopes ;
121+ @ NonNull
122+ private String redirectUri = "" ;
118123
119124 /**
120125 * @param githubWebUri The URI to the root of the web UI for GitHub or GitHub Enterprise,
@@ -187,6 +192,15 @@ private void setOauthScopes(String oauthScopes) {
187192 this .oauthScopes = oauthScopes ;
188193 }
189194
195+ /**
196+ * @param redirectUri the redirectUri to set
197+ */
198+ @ DataBoundSetter
199+ public void setRedirectUri (String redirectUri ) {
200+ if (null == redirectUri ) redirectUri = "" ;
201+ this .redirectUri = redirectUri ;
202+ }
203+
190204 /**
191205 * Checks the security realm for a GitHub OAuth scope.
192206 * @param scope A scope to check for in the security realm.
@@ -245,6 +259,10 @@ public void marshal(Object source, HierarchicalStreamWriter writer,
245259 writer .setValue (realm .getOauthScopes ());
246260 writer .endNode ();
247261
262+ writer .startNode ("redirectUri" );
263+ writer .setValue (realm .getRedirectUri ());
264+ writer .endNode ();
265+
248266 }
249267
250268 public Object unmarshal (HierarchicalStreamReader reader ,
@@ -274,8 +292,7 @@ public Object unmarshal(HierarchicalStreamReader reader,
274292 return realm ;
275293 }
276294
277- private void setValue (GithubSecurityRealm realm , String node ,
278- String value ) {
295+ private void setValue (GithubSecurityRealm realm , String node , String value ) {
279296 if (node .equalsIgnoreCase ("clientid" )) {
280297 realm .setClientID (value );
281298 } else if (node .equalsIgnoreCase ("clientsecret" )) {
@@ -290,6 +307,8 @@ private void setValue(GithubSecurityRealm realm, String node,
290307 realm .setGithubApiUri (value );
291308 } else if (node .equalsIgnoreCase ("oauthscopes" )) {
292309 realm .setOauthScopes (value );
310+ } else if (node .equalsIgnoreCase ("redirecturi" )) {
311+ realm .setRedirectUri (value );
293312 } else {
294313 throw new ConversionException ("Invalid node value = " + node );
295314 }
@@ -334,11 +353,21 @@ public String getOauthScopes() {
334353 return oauthScopes ;
335354 }
336355
356+ /**
357+ * @return the redirectUri
358+ */
359+ @ NonNull
360+ public String getRedirectUri () {
361+ return redirectUri ;
362+ }
363+
337364 public HttpResponse doCommenceLogin (StaplerRequest request , @ QueryParameter String from , @ Header ("Referer" ) final String referer )
338- throws IOException {
365+ throws IOException , URISyntaxException {
339366 // https://tools.ietf.org/html/rfc6749#section-10.10 dictates that probability that an attacker guesses the string
340367 // SHOULD be less than or equal to 2^(-160) and our Strings consist of 65 chars. (65^27 ~= 2^160)
341368 final String state = getSecureRandomString (27 );
369+
370+ // This is to go back to the current page after login, not the oauth callback
342371 String redirectOnFinish ;
343372 if (from != null && Util .isSafeToRedirectTo (from )) {
344373 redirectOnFinish = from ;
@@ -355,17 +384,17 @@ public HttpResponse doCommenceLogin(StaplerRequest request, @QueryParameter Stri
355384 for (GitHubOAuthScope s : Jenkins .get ().getExtensionList (GitHubOAuthScope .class )) {
356385 scopes .addAll (s .getScopesToRequest ());
357386 }
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 ;
387+
388+ URIBuilder builder = new URIBuilder (githubWebUri , StandardCharsets .UTF_8 );
389+ builder .setPath ("/login/oauth/authorize" );
390+ builder .setParameter ("client_id" , clientID );
391+ builder .setParameter ("scope" , scopes .isEmpty () ? oauthScopes : Util .join (scopes ,"," ));
392+ builder .setParameter ("state" , state );
393+ if (!redirectUri .isEmpty ()) {
394+ builder .setParameter ("redirect_uri" , redirectUri );
365395 }
366396
367- return new HttpRedirect (githubWebUri + "/login/oauth/authorize?client_id="
368- + clientID + suffix );
397+ return new HttpRedirect (builder .toString ());
369398 }
370399
371400 /**
@@ -630,6 +659,12 @@ public String getDefaultOauthScopes() {
630659 return DEFAULT_OAUTH_SCOPES ;
631660 }
632661
662+ public String getDefaultRequestUri () {
663+ // Intentionally making this default in UI and not in code
664+ // to preserve behaviour for existing groovy init & JCasc setups
665+ return Jenkins .get ().getRootUrl () + "securityRealm/finishLogin" ;
666+ }
667+
633668 public DescriptorImpl () {
634669 super ();
635670 // TODO Auto-generated constructor stub
@@ -728,7 +763,8 @@ public boolean equals(Object object){
728763 this .getGithubApiUri ().equals (obj .getGithubApiUri ()) &&
729764 this .getClientID ().equals (obj .getClientID ()) &&
730765 this .getClientSecret ().equals (obj .getClientSecret ()) &&
731- this .getOauthScopes ().equals (obj .getOauthScopes ());
766+ this .getOauthScopes ().equals (obj .getOauthScopes ()) &&
767+ this .getRedirectUri ().equals (obj .getRedirectUri ());
732768 } else {
733769 return false ;
734770 }
@@ -742,6 +778,7 @@ public int hashCode() {
742778 .append (this .getClientID ())
743779 .append (this .getClientSecret ())
744780 .append (this .getOauthScopes ())
781+ .append (this .getRedirectUri ())
745782 .toHashCode ();
746783 }
747784
0 commit comments