Skip to content

Commit 6d88ee2

Browse files
committed
Add redirect_uri and related configuration
1 parent db7b3ba commit 6d88ee2

File tree

4 files changed

+76
-15
lines changed

4 files changed

+76
-15
lines changed

src/main/java/org/jenkinsci/plugins/GithubSecurityRealm.java

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ of this software and associated documentation files (the "Software"), to deal
6262
import org.apache.http.client.CredentialsProvider;
6363
import org.apache.http.client.config.RequestConfig;
6464
import org.apache.http.client.methods.HttpPost;
65-
import org.apache.http.client.methods.HttpUriRequest;
65+
import org.apache.http.client.utils.URIBuilder;
6666
import org.apache.http.impl.client.BasicCredentialsProvider;
6767
import org.apache.http.impl.client.CloseableHttpClient;
6868
import org.apache.http.impl.client.HttpClientBuilder;
@@ -85,6 +85,8 @@ of this software and associated documentation files (the "Software"), to deal
8585
import java.io.IOException;
8686
import java.net.InetSocketAddress;
8787
import java.net.Proxy;
88+
import java.net.URISyntaxException;
89+
import java.nio.charset.StandardCharsets;
8890
import java.security.SecureRandom;
8991
import java.util.Arrays;
9092
import java.util.HashSet;
@@ -115,6 +117,8 @@ public class GithubSecurityRealm extends AbstractPasswordBasedSecurityRealm impl
115117
private Secret clientSecret;
116118
private String oauthScopes;
117119
private String[] myScopes;
120+
@NonNull
121+
private String redirectUri = "";
118122

119123
/**
120124
* @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) {
187191
this.oauthScopes = oauthScopes;
188192
}
189193

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+
190203
/**
191204
* Checks the security realm for a GitHub OAuth scope.
192205
* @param scope A scope to check for in the security realm.
@@ -245,6 +258,10 @@ public void marshal(Object source, HierarchicalStreamWriter writer,
245258
writer.setValue(realm.getOauthScopes());
246259
writer.endNode();
247260

261+
writer.startNode("redirectUri");
262+
writer.setValue(realm.getRedirectUri());
263+
writer.endNode();
264+
248265
}
249266

250267
public Object unmarshal(HierarchicalStreamReader reader,
@@ -274,8 +291,7 @@ public Object unmarshal(HierarchicalStreamReader reader,
274291
return realm;
275292
}
276293

277-
private void setValue(GithubSecurityRealm realm, String node,
278-
String value) {
294+
private void setValue(GithubSecurityRealm realm, String node, String value) {
279295
if (node.equalsIgnoreCase("clientid")) {
280296
realm.setClientID(value);
281297
} else if (node.equalsIgnoreCase("clientsecret")) {
@@ -290,6 +306,8 @@ private void setValue(GithubSecurityRealm realm, String node,
290306
realm.setGithubApiUri(value);
291307
} else if (node.equalsIgnoreCase("oauthscopes")) {
292308
realm.setOauthScopes(value);
309+
} else if (node.equalsIgnoreCase("redirecturi")) {
310+
realm.setRedirectUri(value);
293311
} else {
294312
throw new ConversionException("Invalid node value = " + node);
295313
}
@@ -334,11 +352,21 @@ public String getOauthScopes() {
334352
return oauthScopes;
335353
}
336354

355+
/**
356+
* @return the redirectUri
357+
*/
358+
@NonNull
359+
public String getRedirectUri() {
360+
return redirectUri;
361+
}
362+
337363
public HttpResponse doCommenceLogin(StaplerRequest request, @QueryParameter String from, @Header("Referer") final String referer)
338-
throws IOException {
364+
throws IOException, URISyntaxException {
339365
// https://tools.ietf.org/html/rfc6749#section-10.10 dictates that probability that an attacker guesses the string
340366
// SHOULD be less than or equal to 2^(-160) and our Strings consist of 65 chars. (65^27 ~= 2^160)
341367
final String state = getSecureRandomString(27);
368+
369+
// This is to go back to the current page after login, not the oauth callback
342370
String redirectOnFinish;
343371
if (from != null && Util.isSafeToRedirectTo(from)) {
344372
redirectOnFinish = from;
@@ -355,17 +383,17 @@ public HttpResponse doCommenceLogin(StaplerRequest request, @QueryParameter Stri
355383
for (GitHubOAuthScope s : Jenkins.get().getExtensionList(GitHubOAuthScope.class)) {
356384
scopes.addAll(s.getScopesToRequest());
357385
}
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);
365394
}
366395

367-
return new HttpRedirect(githubWebUri + "/login/oauth/authorize?client_id="
368-
+ clientID + suffix);
396+
return new HttpRedirect(builder.toString());
369397
}
370398

371399
/**
@@ -630,6 +658,12 @@ public String getDefaultOauthScopes() {
630658
return DEFAULT_OAUTH_SCOPES;
631659
}
632660

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+
633667
public DescriptorImpl() {
634668
super();
635669
// TODO Auto-generated constructor stub
@@ -728,7 +762,8 @@ public boolean equals(Object object){
728762
this.getGithubApiUri().equals(obj.getGithubApiUri()) &&
729763
this.getClientID().equals(obj.getClientID()) &&
730764
this.getClientSecret().equals(obj.getClientSecret()) &&
731-
this.getOauthScopes().equals(obj.getOauthScopes());
765+
this.getOauthScopes().equals(obj.getOauthScopes()) &&
766+
this.getRedirectUri().equals(obj.getRedirectUri());
732767
} else {
733768
return false;
734769
}
@@ -742,6 +777,7 @@ public int hashCode() {
742777
.append(this.getClientID())
743778
.append(this.getClientSecret())
744779
.append(this.getOauthScopes())
780+
.append(this.getRedirectUri())
745781
.toHashCode();
746782
}
747783

src/main/resources/org/jenkinsci/plugins/GithubSecurityRealm/config.jelly

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,10 @@
2323
<f:entry title="OAuth Scope(s)" field="oauthScopes" help="/plugin/github-oauth/help/realm/oauth-scopes-help.html">
2424
<f:textbox default="${descriptor.getDefaultOauthScopes()}" />
2525
</f:entry>
26+
27+
<f:entry title="Redirect URI" field="redirectUri" help="/plugin/github-oauth/help/realm/redirect-uri-help.html">
28+
<f:textbox default="${descriptor.getDefaultRequestUri()}" />
29+
</f:entry>
30+
2631
</f:section>
2732
</j:jelly>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<div>
2+
An optional redirect URI to be used by GitHub. See <a href="https://docs.github.com/en/developers/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps">https://docs.github.com/en/developers/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps</a>.
3+
</div>

src/test/java/org/jenkinsci/plugins/GithubSecurityRealmTest.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,24 @@ public void testEquals_false() {
5252
GithubSecurityRealm a = new GithubSecurityRealm("http://jenkins.acme.com", "http://jenkins.acme.com/api/v3", "someid", "somesecret", "read:org");
5353
GithubSecurityRealm b = new GithubSecurityRealm("http://jenkins.acme.com", "http://jenkins.acme.com/api/v3", "someid", "somesecret", "read:org,repo");
5454
assertNotEquals(a, b);
55-
assertNotEquals("", a);
55+
}
56+
57+
@Test
58+
public void testEqualsRedirectUri() {
59+
GithubSecurityRealm _default = new GithubSecurityRealm("http://jenkins.acme.com", "http://jenkins.acme.com/api/v3", "someid", "somesecret", "read:org");
60+
GithubSecurityRealm empty = new GithubSecurityRealm("http://jenkins.acme.com", "http://jenkins.acme.com/api/v3", "someid", "somesecret", "read:org");
61+
empty.setRedirectUri("");
62+
GithubSecurityRealm sameValue1 = new GithubSecurityRealm("http://jenkins.acme.com", "http://jenkins.acme.com/api/v3", "someid", "somesecret", "read:org");
63+
sameValue1.setRedirectUri("http://jenkins1.awesomecorp.com");
64+
GithubSecurityRealm sameValue2 = new GithubSecurityRealm("http://jenkins.acme.com", "http://jenkins.acme.com/api/v3", "someid", "somesecret", "read:org");
65+
sameValue2.setRedirectUri("http://jenkins1.awesomecorp.com");
66+
GithubSecurityRealm otherValue = new GithubSecurityRealm("http://jenkins.acme.com", "http://jenkins.acme.com/api/v3", "someid", "somesecret", "read:org");
67+
otherValue.setRedirectUri("http://jenkins1.notsogoodcorp.com");
68+
assertEquals(_default, empty);
69+
assertEquals(sameValue1, sameValue2);
70+
assertNotEquals(sameValue1, _default);
71+
assertNotEquals(sameValue1, empty);
72+
assertNotEquals(sameValue1, otherValue);
5673
}
5774

5875
@Test

0 commit comments

Comments
 (0)