Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add redirect_uri oauth param and configuration #198

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
82101ad
Add redirect_uri and related configuration
tdaniely-dn Aug 5, 2022
2a2f53c
Merge branch 'master' into add-redirect-uri
tdaniely-dn Sep 18, 2022
43099da
Merge branch 'master' into add-redirect-uri
tdaniely-dn Nov 6, 2022
25dfb6a
Merge branch 'master' into add-redirect-uri
tdaniely-dn Apr 21, 2023
3ca538a
Merge branch 'master' into add-redirect-uri
tdaniely-dn May 3, 2023
f2b2ccc
Merge branch 'master' into add-redirect-uri
tdaniely-dn Jul 5, 2023
29a8567
Add support for using jenkins agents that require authentication
AndreBrinkop Oct 18, 2022
a1d6e49
Update read permissions for agent user
AndreBrinkop Oct 21, 2022
a1c5082
Add test cases for agent user rights
AndreBrinkop Oct 21, 2022
5af8fc8
Add javadoc to agentUserName setter
Jul 18, 2023
75ea97e
Make agentUserName property optional to avoid a breaking change
Jul 18, 2023
afea895
Add log output for agent user permission check
AndreBrinkop Jul 18, 2023
6764838
Make agent user permission check type-safe
Jul 18, 2023
bd9637b
Update src/main/webapp/help/auth/agent-user-name-help.html
AndreBrinkop Jul 18, 2023
2feb79b
Fix permission type in checkAgentUserPermission function
Jul 18, 2023
88708ce
Merge pull request #209 from AndreBrinkop/add-jenkins-agent-support
scurvydoggo Jul 31, 2023
40abf0c
Merge branch 'master' into add-redirect-uri
scurvydoggo Jul 31, 2023
c5a6ab3
Merge branch 'master' into add-redirect-uri
scurvydoggo Sep 22, 2023
eddce98
Merge branch 'master' into add-redirect-uri
tdaniely-dn Oct 15, 2023
33e9495
Merge branch 'master' into add-redirect-uri
tdaniely-dn Nov 15, 2023
2ff2987
Merge branch 'master' into add-redirect-uri
gounthar Nov 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 51 additions & 14 deletions src/main/java/org/jenkinsci/plugins/GithubSecurityRealm.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ of this software and associated documentation files (the "Software"), to deal
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
Expand All @@ -73,6 +73,7 @@ of this software and associated documentation files (the "Software"), to deal
import org.kohsuke.github.GHOrganization;
import org.kohsuke.github.GHTeam;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.Header;
import org.kohsuke.stapler.HttpRedirect;
import org.kohsuke.stapler.HttpResponse;
Expand All @@ -85,6 +86,8 @@ of this software and associated documentation files (the "Software"), to deal
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.HashSet;
Expand Down Expand Up @@ -115,6 +118,8 @@ public class GithubSecurityRealm extends AbstractPasswordBasedSecurityRealm impl
private Secret clientSecret;
private String oauthScopes;
private String[] myScopes;
@NonNull
private String redirectUri = "";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this is well intended, as we are not using @nonnull anywhere else in this project I don't think it makes sense to use it here. It would be 'unfair' to all of the other properties/fields, as this one effectively gets arbitrary treatment.

Could you please remove this annotation, and we can discuss adding it as a separate PR?


/**
* @param githubWebUri The URI to the root of the web UI for GitHub or GitHub Enterprise,
Expand Down Expand Up @@ -187,6 +192,15 @@ private void setOauthScopes(String oauthScopes) {
this.oauthScopes = oauthScopes;
}

/**
* @param redirectUri the redirectUri to set
*/
@DataBoundSetter
public void setRedirectUri(String redirectUri) {
if (null == redirectUri) redirectUri = "";
this.redirectUri = redirectUri;
}

/**
* Checks the security realm for a GitHub OAuth scope.
* @param scope A scope to check for in the security realm.
Expand Down Expand Up @@ -245,6 +259,10 @@ public void marshal(Object source, HierarchicalStreamWriter writer,
writer.setValue(realm.getOauthScopes());
writer.endNode();

writer.startNode("redirectUri");
writer.setValue(realm.getRedirectUri());
writer.endNode();

}

public Object unmarshal(HierarchicalStreamReader reader,
Expand Down Expand Up @@ -274,8 +292,7 @@ public Object unmarshal(HierarchicalStreamReader reader,
return realm;
}

private void setValue(GithubSecurityRealm realm, String node,
String value) {
private void setValue(GithubSecurityRealm realm, String node, String value) {
if (node.equalsIgnoreCase("clientid")) {
realm.setClientID(value);
} else if (node.equalsIgnoreCase("clientsecret")) {
Expand All @@ -290,6 +307,8 @@ private void setValue(GithubSecurityRealm realm, String node,
realm.setGithubApiUri(value);
} else if (node.equalsIgnoreCase("oauthscopes")) {
realm.setOauthScopes(value);
} else if (node.equalsIgnoreCase("redirecturi")) {
realm.setRedirectUri(value);
} else {
throw new ConversionException("Invalid node value = " + node);
}
Expand Down Expand Up @@ -334,11 +353,21 @@ public String getOauthScopes() {
return oauthScopes;
}

/**
* @return the redirectUri
*/
@NonNull
public String getRedirectUri() {
return redirectUri;
}

public HttpResponse doCommenceLogin(StaplerRequest request, @QueryParameter String from, @Header("Referer") final String referer)
throws IOException {
throws IOException, URISyntaxException {
// https://tools.ietf.org/html/rfc6749#section-10.10 dictates that probability that an attacker guesses the string
// SHOULD be less than or equal to 2^(-160) and our Strings consist of 65 chars. (65^27 ~= 2^160)
final String state = getSecureRandomString(27);

// This is to go back to the current page after login, not the oauth callback
String redirectOnFinish;
if (from != null && Util.isSafeToRedirectTo(from)) {
redirectOnFinish = from;
Expand All @@ -355,17 +384,17 @@ public HttpResponse doCommenceLogin(StaplerRequest request, @QueryParameter Stri
for (GitHubOAuthScope s : Jenkins.get().getExtensionList(GitHubOAuthScope.class)) {
scopes.addAll(s.getScopesToRequest());
}
String suffix="";
if (!scopes.isEmpty()) {
suffix = "&scope="+Util.join(scopes,",")+"&state="+state;
} else {
// We need repo scope in order to access private repos
// See https://developer.github.com/v3/oauth/#scopes
suffix = "&scope=" + oauthScopes +"&state="+state;

URIBuilder builder = new URIBuilder(githubWebUri, StandardCharsets.UTF_8);
builder.setPath("/login/oauth/authorize");
builder.setParameter("client_id", clientID);
builder.setParameter("scope", scopes.isEmpty() ? oauthScopes : Util.join(scopes,","));
builder.setParameter("state", state);
if (!redirectUri.isEmpty()) {
builder.setParameter("redirect_uri", redirectUri);
}

return new HttpRedirect(githubWebUri + "/login/oauth/authorize?client_id="
+ clientID + suffix);
return new HttpRedirect(builder.toString());
}

/**
Expand Down Expand Up @@ -630,6 +659,12 @@ public String getDefaultOauthScopes() {
return DEFAULT_OAUTH_SCOPES;
}

public String getDefaultRequestUri() {
// Intentionally making this default in UI and not in code
// to preserve behaviour for existing groovy init & JCasc setups
return Jenkins.get().getRootUrl() + "securityRealm/finishLogin";
}

public DescriptorImpl() {
super();
// TODO Auto-generated constructor stub
Expand Down Expand Up @@ -728,7 +763,8 @@ public boolean equals(Object object){
this.getGithubApiUri().equals(obj.getGithubApiUri()) &&
this.getClientID().equals(obj.getClientID()) &&
this.getClientSecret().equals(obj.getClientSecret()) &&
this.getOauthScopes().equals(obj.getOauthScopes());
this.getOauthScopes().equals(obj.getOauthScopes()) &&
this.getRedirectUri().equals(obj.getRedirectUri());
} else {
return false;
}
Expand All @@ -742,6 +778,7 @@ public int hashCode() {
.append(this.getClientID())
.append(this.getClientSecret())
.append(this.getOauthScopes())
.append(this.getRedirectUri())
.toHashCode();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,10 @@
<f:entry title="OAuth Scope(s)" field="oauthScopes" help="/plugin/github-oauth/help/realm/oauth-scopes-help.html">
<f:textbox default="${descriptor.getDefaultOauthScopes()}" />
</f:entry>

<f:entry title="Redirect URI" field="redirectUri" help="/plugin/github-oauth/help/realm/redirect-uri-help.html">
<f:textbox default="${descriptor.getDefaultRequestUri()}" />
</f:entry>

</f:section>
</j:jelly>
3 changes: 3 additions & 0 deletions src/main/webapp/help/realm/redirect-uri-help.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div>
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>.
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,24 @@ public void testEquals_false() {
GithubSecurityRealm a = new GithubSecurityRealm("http://jenkins.acme.com", "http://jenkins.acme.com/api/v3", "someid", "somesecret", "read:org");
GithubSecurityRealm b = new GithubSecurityRealm("http://jenkins.acme.com", "http://jenkins.acme.com/api/v3", "someid", "somesecret", "read:org,repo");
assertNotEquals(a, b);
assertNotEquals("", a);
}

@Test
public void testEqualsRedirectUri() {
GithubSecurityRealm _default = new GithubSecurityRealm("http://jenkins.acme.com", "http://jenkins.acme.com/api/v3", "someid", "somesecret", "read:org");
GithubSecurityRealm empty = new GithubSecurityRealm("http://jenkins.acme.com", "http://jenkins.acme.com/api/v3", "someid", "somesecret", "read:org");
empty.setRedirectUri("");
GithubSecurityRealm sameValue1 = new GithubSecurityRealm("http://jenkins.acme.com", "http://jenkins.acme.com/api/v3", "someid", "somesecret", "read:org");
sameValue1.setRedirectUri("http://jenkins1.awesomecorp.com");
GithubSecurityRealm sameValue2 = new GithubSecurityRealm("http://jenkins.acme.com", "http://jenkins.acme.com/api/v3", "someid", "somesecret", "read:org");
sameValue2.setRedirectUri("http://jenkins1.awesomecorp.com");
GithubSecurityRealm otherValue = new GithubSecurityRealm("http://jenkins.acme.com", "http://jenkins.acme.com/api/v3", "someid", "somesecret", "read:org");
otherValue.setRedirectUri("http://jenkins1.notsogoodcorp.com");
assertEquals(_default, empty);
assertEquals(sameValue1, sameValue2);
assertNotEquals(sameValue1, _default);
assertNotEquals(sameValue1, empty);
assertNotEquals(sameValue1, otherValue);
}

@Test
Expand Down