diff --git a/pom.xml b/pom.xml index a4a6c7b8..344f2e7d 100644 --- a/pom.xml +++ b/pom.xml @@ -125,6 +125,11 @@ mockito-core test + + org.mockito + mockito-junit-jupiter + test + diff --git a/src/test/java/org/jenkinsci/plugins/GithubAccessTokenPropertySEC797Test.java b/src/test/java/org/jenkinsci/plugins/GithubAccessTokenPropertySEC797Test.java index dbe8fdad..81b53cfa 100644 --- a/src/test/java/org/jenkinsci/plugins/GithubAccessTokenPropertySEC797Test.java +++ b/src/test/java/org/jenkinsci/plugins/GithubAccessTokenPropertySEC797Test.java @@ -23,23 +23,12 @@ */ package org.jenkinsci.plugins; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; - import edu.umd.cs.findbugs.annotations.CheckForNull; import hudson.model.UnprotectedRootAction; import hudson.util.HttpResponses; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; -import java.io.IOException; -import java.net.URI; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; @@ -50,313 +39,321 @@ import org.eclipse.jetty.server.ServerConnector; import org.htmlunit.Page; import org.htmlunit.WebRequest; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.TestExtension; +import org.jvnet.hudson.test.junit.jupiter.WithJenkins; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.StaplerRequest2; +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + //TODO merge with GithubAccessTokenPropertyTest after security release, just meant to ease the security merge // or with GithubSecurityRealmTest, but will require more refactor to move out the mock server -public class GithubAccessTokenPropertySEC797Test { - - @Rule - public JenkinsRule j = new JenkinsRule(); - - private JenkinsRule.WebClient wc; - - private Server server; - private URI serverUri; - private MockGithubServlet servlet; - - public void setupMockGithubServer() throws Exception { - server = new Server(); - ServerConnector connector = new ServerConnector(server); - // auto-bind to available port - connector.setPort(0); - server.addConnector(connector); - - servlet = new MockGithubServlet(j); - - ServletContextHandler context = new ServletContextHandler(); - ServletHolder servletHolder = new ServletHolder("default", servlet); - context.addServlet(servletHolder, "/*"); - server.setHandler(context); - - server.start(); - - String host = connector.getHost(); - if (host == null) { - host = "localhost"; - } - - int port = connector.getLocalPort(); - serverUri = new URI(String.format("http://%s:%d/", host, port)); - servlet.setServerUrl(serverUri); - } - - /** - * Based on documentation found at - * https://developer.github.com/v3/users/ - * https://developer.github.com/v3/orgs/ - * https://developer.github.com/v3/orgs/teams/ - */ - private static class MockGithubServlet extends DefaultServlet { - private String currentLogin; - private List organizations; - private List teams; - - private JenkinsRule jenkinsRule; - private URI serverUri; - - public MockGithubServlet(JenkinsRule jenkinsRule) { - this.jenkinsRule = jenkinsRule; - } - - public void setServerUrl(URI serverUri) { - this.serverUri = serverUri; - } - - @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { - switch (req.getRequestURI()) { - case "/user": - this.onUser(req, resp); - break; - case "/users/_specific_login_": - this.onUser(req, resp); - break; - case "/user/orgs": - this.onUserOrgs(req, resp); - break; - case "/user/teams": - this.onUserTeams(req, resp); - break; - case "/orgs/org-a": - this.onOrgs(req, resp, "org-a"); - break; - case "/orgs/org-a/teams": - this.onOrgsTeam(req, resp, "org-a"); - break; - case "/orgs/org-a/members/alice": - this.onOrgsMember(req, resp, "org-a", "alice"); - break; - case "/teams/7/members/alice": - this.onTeamMember(req, resp, "team-b", "alice"); - break; - case "/orgs/org-c": - this.onOrgs(req, resp, "org-c"); - break; - case "/orgs/org-c/teams": - this.onOrgsTeam(req, resp, "org-c"); - break; - case "/orgs/org-c/members/bob": - this.onOrgsMember(req, resp, "org-c", "bob"); - break; - case "/teams/7/members/bob": - this.onTeamMember(req, resp, "team-d", "bob"); - break; - case "/login/oauth/authorize": - this.onLoginOAuthAuthorize(req, resp); - break; - case "/login/oauth/access_token": - this.onLoginOAuthAccessToken(req, resp); - break; - default: - throw new RuntimeException("Url not mapped yet: " + req.getRequestURI()); - } - } - - private void onUser(HttpServletRequest req, HttpServletResponse resp) throws IOException { - resp.getWriter().write(JSONObject.fromObject( - new HashMap() {{ - put("login", currentLogin); - put("name", currentLogin + "_name"); - // to avoid triggering a second call, due to GithubSecurityRealm:382 - put("created_at", "2008-01-14T04:33:35Z"); - put("url", serverUri + "/users/_specific_login_"); - }} - ).toString()); - } - - private void onUserOrgs(HttpServletRequest req, HttpServletResponse resp) throws IOException { - List> responseBody = new ArrayList<>(); - for (String orgName : organizations) { - final String orgName_ = orgName; - responseBody.add(new HashMap() {{ - put("login", orgName_); - }}); - } - - resp.getWriter().write(JSONArray.fromObject(responseBody).toString()); - } - - private void onOrgs(HttpServletRequest req, HttpServletResponse resp, final String orgName) throws IOException { - Map responseBody = new HashMap() {{ - put("login", orgName); - }}; - - resp.getWriter().write(JSONObject.fromObject(responseBody).toString()); - } - - private void onOrgsMember(HttpServletRequest req, HttpServletResponse resp, String orgName, String userName) { - resp.setStatus(HttpServletResponse.SC_NO_CONTENT); - // 302 / 404 responses not implemented - } - - private void onTeamMember(HttpServletRequest req, HttpServletResponse resp, String orgName, String userName) { - resp.setStatus(HttpServletResponse.SC_NO_CONTENT); - // 302 / 404 responses not implemented - } - - private void onOrgsTeam(HttpServletRequest req, HttpServletResponse resp, final String orgName) throws IOException { - List> responseBody = new ArrayList<>(); - for (String teamName : teams) { - final String teamName_ = teamName; - responseBody.add(new HashMap() {{ - put("id", 7); - put("login", teamName_ + "_login"); - put("name", teamName_); - put("organization", new HashMap() {{ - put("login", orgName); - }}); - }}); - } - - resp.getWriter().write(JSONArray.fromObject(responseBody).toString()); - } - - private void onUserTeams(HttpServletRequest req, HttpServletResponse resp) throws IOException { - List> responseBody = new ArrayList<>(); - for (String teamName : teams) { - final String teamName_ = teamName; - responseBody.add(new HashMap() {{ - put("login", teamName_ + "_login"); - put("name", teamName_); - put("organization", new HashMap() {{ - put("login", organizations.get(0)); - }}); - }}); - } - - resp.getWriter().write(JSONArray.fromObject(responseBody).toString()); - } - - private void onLoginOAuthAuthorize(HttpServletRequest req, HttpServletResponse resp) throws IOException { - String code = "test"; - String state = req.getParameter("state"); - resp.sendRedirect(jenkinsRule.getURL() + "securityRealm/finishLogin?code=" + code + "&state=" + state); - } - - private void onLoginOAuthAccessToken(HttpServletRequest req, HttpServletResponse resp) throws IOException { - resp.getWriter().write("access_token=RANDOM_ACCESS_TOKEN"); - } - } - - @Before - public void prepareRealmAndWebClient() throws Exception { - this.setupMockGithubServer(); - this.setupRealm(); - wc = j.createWebClient(); - } - - private void setupRealm() { - String githubWebUri = serverUri.toString(); - String githubApiUri = serverUri.toString(); - String clientID = "xxx"; - String clientSecret = "yyy"; - String oauthScopes = "read:org"; - - GithubSecurityRealm githubSecurityRealm = new GithubSecurityRealm( - githubWebUri, - githubApiUri, - clientID, - clientSecret, - oauthScopes - ); - - j.jenkins.setSecurityRealm(githubSecurityRealm); - } - - @After - public void stopEmbeddedJettyServer() { - try { - server.stop(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - // all the code above is reused from GithubAccessTokenPropertyTest - - @Issue("SECURITY-797") - @Test - public void preventSessionFixation() throws Exception { - TestRootAction rootAction = j.jenkins.getExtensionList(UnprotectedRootAction.class).get(TestRootAction.class); - assertNotNull(rootAction); - - wc = j.createWebClient(); - - String aliceLogin = "alice"; - servlet.currentLogin = aliceLogin; - servlet.organizations = Collections.singletonList("org-a"); - servlet.teams = Collections.singletonList("team-b"); - - String sessionIdBefore = checkSessionFixationWithOAuth(); - String sessionIdAfter = rootAction.sessionId; - assertNotNull(sessionIdAfter); - assertNotEquals("Session must be invalidated after login", sessionIdBefore, sessionIdAfter); - } - - @TestExtension("preventSessionFixation") - public static final class TestRootAction implements UnprotectedRootAction { - public String sessionId; - - @Override - public @CheckForNull String getIconFileName() { - return null; - } - - @Override - public @CheckForNull String getDisplayName() { - return null; - } - - @Override - public String getUrlName() { - return "test"; - } - - public HttpResponse doIndex(StaplerRequest2 request) { - HttpSession session = request.getSession(false); - if (session == null) { - sessionId = null; - } else { - sessionId = session.getId(); - } - return HttpResponses.text("ok"); - } - } - - private String checkSessionFixationWithOAuth() throws IOException { - WebRequest req = new WebRequest(new URL(j.getURL(), "securityRealm/commenceLogin")); - req.setEncodingType(null); - - String referer = j.getURL() + "test"; - req.setAdditionalHeader("Referer", referer); - wc.getOptions().setRedirectEnabled(false); - wc.getOptions().setThrowExceptionOnFailingStatusCode(false); - Page p = wc.getPage(req); - - String cookie = p.getWebResponse().getResponseHeaderValue("Set-Cookie"); - String sessionId = StringUtils.substringBetween(cookie, "JSESSIONID=", ";"); - - wc.getOptions().setRedirectEnabled(true); - // continue the process of authentication - wc.getPage(new URL(p.getWebResponse().getResponseHeaderValue("Location"))); - return sessionId; - } +@WithJenkins +class GithubAccessTokenPropertySEC797Test { + + private JenkinsRule j; + private JenkinsRule.WebClient wc; + + private Server server; + private URI serverUri; + private MockGithubServlet servlet; + + private void setupMockGithubServer() throws Exception { + server = new Server(); + ServerConnector connector = new ServerConnector(server); + // auto-bind to available port + connector.setPort(0); + server.addConnector(connector); + + servlet = new MockGithubServlet(j); + + ServletContextHandler context = new ServletContextHandler(); + ServletHolder servletHolder = new ServletHolder("default", servlet); + context.addServlet(servletHolder, "/*"); + server.setHandler(context); + + server.start(); + + String host = connector.getHost(); + if (host == null) { + host = "localhost"; + } + + int port = connector.getLocalPort(); + serverUri = new URI(String.format("http://%s:%d/", host, port)); + servlet.setServerUrl(serverUri); + } + + /** + * Based on documentation found at + * https://developer.github.com/v3/users/ + * https://developer.github.com/v3/orgs/ + * https://developer.github.com/v3/orgs/teams/ + */ + private static class MockGithubServlet extends DefaultServlet { + private String currentLogin; + private List organizations; + private List teams; + + private final JenkinsRule jenkinsRule; + private URI serverUri; + + public MockGithubServlet(JenkinsRule jenkinsRule) { + this.jenkinsRule = jenkinsRule; + } + + public void setServerUrl(URI serverUri) { + this.serverUri = serverUri; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + switch (req.getRequestURI()) { + case "/user": + this.onUser(req, resp); + break; + case "/users/_specific_login_": + this.onUser(req, resp); + break; + case "/user/orgs": + this.onUserOrgs(req, resp); + break; + case "/user/teams": + this.onUserTeams(req, resp); + break; + case "/orgs/org-a": + this.onOrgs(req, resp, "org-a"); + break; + case "/orgs/org-a/teams": + this.onOrgsTeam(req, resp, "org-a"); + break; + case "/orgs/org-a/members/alice": + this.onOrgsMember(req, resp, "org-a", "alice"); + break; + case "/teams/7/members/alice": + this.onTeamMember(req, resp, "team-b", "alice"); + break; + case "/orgs/org-c": + this.onOrgs(req, resp, "org-c"); + break; + case "/orgs/org-c/teams": + this.onOrgsTeam(req, resp, "org-c"); + break; + case "/orgs/org-c/members/bob": + this.onOrgsMember(req, resp, "org-c", "bob"); + break; + case "/teams/7/members/bob": + this.onTeamMember(req, resp, "team-d", "bob"); + break; + case "/login/oauth/authorize": + this.onLoginOAuthAuthorize(req, resp); + break; + case "/login/oauth/access_token": + this.onLoginOAuthAccessToken(req, resp); + break; + default: + throw new RuntimeException("Url not mapped yet: " + req.getRequestURI()); + } + } + + private void onUser(HttpServletRequest req, HttpServletResponse resp) throws IOException { + resp.getWriter().write(JSONObject.fromObject( + new HashMap() {{ + put("login", currentLogin); + put("name", currentLogin + "_name"); + // to avoid triggering a second call, due to GithubSecurityRealm:382 + put("created_at", "2008-01-14T04:33:35Z"); + put("url", serverUri + "/users/_specific_login_"); + }} + ).toString()); + } + + private void onUserOrgs(HttpServletRequest req, HttpServletResponse resp) throws IOException { + List> responseBody = new ArrayList<>(); + for (String orgName : organizations) { + final String orgName_ = orgName; + responseBody.add(new HashMap<>() {{ + put("login", orgName_); + }}); + } + + resp.getWriter().write(JSONArray.fromObject(responseBody).toString()); + } + + private void onOrgs(HttpServletRequest req, HttpServletResponse resp, final String orgName) throws IOException { + Map responseBody = new HashMap<>() {{ + put("login", orgName); + }}; + + resp.getWriter().write(JSONObject.fromObject(responseBody).toString()); + } + + private void onOrgsMember(HttpServletRequest req, HttpServletResponse resp, String orgName, String userName) { + resp.setStatus(HttpServletResponse.SC_NO_CONTENT); + // 302 / 404 responses not implemented + } + + private void onTeamMember(HttpServletRequest req, HttpServletResponse resp, String orgName, String userName) { + resp.setStatus(HttpServletResponse.SC_NO_CONTENT); + // 302 / 404 responses not implemented + } + + private void onOrgsTeam(HttpServletRequest req, HttpServletResponse resp, final String orgName) throws IOException { + List> responseBody = new ArrayList<>(); + for (String teamName : teams) { + final String teamName_ = teamName; + responseBody.add(new HashMap<>() {{ + put("id", 7); + put("login", teamName_ + "_login"); + put("name", teamName_); + put("organization", new HashMap() {{ + put("login", orgName); + }}); + }}); + } + + resp.getWriter().write(JSONArray.fromObject(responseBody).toString()); + } + + private void onUserTeams(HttpServletRequest req, HttpServletResponse resp) throws IOException { + List> responseBody = new ArrayList<>(); + for (String teamName : teams) { + final String teamName_ = teamName; + responseBody.add(new HashMap<>() {{ + put("login", teamName_ + "_login"); + put("name", teamName_); + put("organization", new HashMap() {{ + put("login", organizations.get(0)); + }}); + }}); + } + + resp.getWriter().write(JSONArray.fromObject(responseBody).toString()); + } + + private void onLoginOAuthAuthorize(HttpServletRequest req, HttpServletResponse resp) throws IOException { + String code = "test"; + String state = req.getParameter("state"); + resp.sendRedirect(jenkinsRule.getURL() + "securityRealm/finishLogin?code=" + code + "&state=" + state); + } + + private void onLoginOAuthAccessToken(HttpServletRequest req, HttpServletResponse resp) throws IOException { + resp.getWriter().write("access_token=RANDOM_ACCESS_TOKEN"); + } + } + + @BeforeEach + void prepareRealmAndWebClient(JenkinsRule j) throws Exception { + this.j = j; + this.setupMockGithubServer(); + this.setupRealm(); + wc = j.createWebClient(); + } + + private void setupRealm() { + String githubWebUri = serverUri.toString(); + String githubApiUri = serverUri.toString(); + String clientID = "xxx"; + String clientSecret = "yyy"; + String oauthScopes = "read:org"; + + GithubSecurityRealm githubSecurityRealm = new GithubSecurityRealm( + githubWebUri, + githubApiUri, + clientID, + clientSecret, + oauthScopes + ); + + j.jenkins.setSecurityRealm(githubSecurityRealm); + } + + @AfterEach + void stopEmbeddedJettyServer() throws Exception { + server.stop(); + } + + // all the code above is reused from GithubAccessTokenPropertyTest + + @Issue("SECURITY-797") + @Test + void preventSessionFixation() throws Exception { + TestRootAction rootAction = j.jenkins.getExtensionList(UnprotectedRootAction.class).get(TestRootAction.class); + assertNotNull(rootAction); + + wc = j.createWebClient(); + + servlet.currentLogin = "alice"; + servlet.organizations = Collections.singletonList("org-a"); + servlet.teams = Collections.singletonList("team-b"); + + String sessionIdBefore = checkSessionFixationWithOAuth(); + String sessionIdAfter = rootAction.sessionId; + assertNotNull(sessionIdAfter); + assertNotEquals(sessionIdBefore, sessionIdAfter, "Session must be invalidated after login"); + } + + @TestExtension("preventSessionFixation") + public static final class TestRootAction implements UnprotectedRootAction { + public String sessionId; + + @Override + public @CheckForNull String getIconFileName() { + return null; + } + + @Override + public @CheckForNull String getDisplayName() { + return null; + } + + @Override + public String getUrlName() { + return "test"; + } + + public HttpResponse doIndex(StaplerRequest2 request) { + HttpSession session = request.getSession(false); + if (session == null) { + sessionId = null; + } else { + sessionId = session.getId(); + } + return HttpResponses.text("ok"); + } + } + + private String checkSessionFixationWithOAuth() throws IOException { + WebRequest req = new WebRequest(new URL(j.getURL(), "securityRealm/commenceLogin")); + req.setEncodingType(null); + + String referer = j.getURL() + "test"; + req.setAdditionalHeader("Referer", referer); + wc.getOptions().setRedirectEnabled(false); + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + Page p = wc.getPage(req); + + String cookie = p.getWebResponse().getResponseHeaderValue("Set-Cookie"); + String sessionId = StringUtils.substringBetween(cookie, "JSESSIONID=", ";"); + + wc.getOptions().setRedirectEnabled(true); + // continue the process of authentication + wc.getPage(new URL(p.getWebResponse().getResponseHeaderValue("Location"))); + return sessionId; + } } diff --git a/src/test/java/org/jenkinsci/plugins/GithubAccessTokenPropertyTest.java b/src/test/java/org/jenkinsci/plugins/GithubAccessTokenPropertyTest.java index 88fcf17b..244143e3 100644 --- a/src/test/java/org/jenkinsci/plugins/GithubAccessTokenPropertyTest.java +++ b/src/test/java/org/jenkinsci/plugins/GithubAccessTokenPropertyTest.java @@ -23,23 +23,10 @@ */ package org.jenkinsci.plugins; -import static org.junit.Assert.assertEquals; - import hudson.model.User; import hudson.util.Scrambler; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.net.URI; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; import jenkins.security.ApiTokenProperty; import net.sf.json.JSONArray; import net.sf.json.JSONObject; @@ -50,382 +37,393 @@ import org.eclipse.jetty.server.ServerConnector; import org.htmlunit.Page; import org.htmlunit.WebRequest; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.junit.jupiter.WithJenkins; + +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; -public class GithubAccessTokenPropertyTest { - - @Rule - public JenkinsRule j = new JenkinsRule(); - - private JenkinsRule.WebClient wc; - - private Server server; - private URI serverUri; - private MockGithubServlet servlet; - - public void setupMockGithubServer() throws Exception { - server = new Server(); - ServerConnector connector = new ServerConnector(server); - // auto-bind to available port - connector.setPort(0); - server.addConnector(connector); - - servlet = new MockGithubServlet(j); - - ServletContextHandler context = new ServletContextHandler(); - ServletHolder servletHolder = new ServletHolder("default", servlet); - context.addServlet(servletHolder, "/*"); - server.setHandler(context); - - server.start(); - - String host = connector.getHost(); - if (host == null) { - host = "localhost"; - } - - int port = connector.getLocalPort(); - serverUri = new URI(String.format("http://%s:%d/", host, port)); - servlet.setServerUrl(serverUri); - } - - /** - * Based on documentation found at - * https://developer.github.com/v3/users/ - * https://developer.github.com/v3/orgs/ - * https://developer.github.com/v3/orgs/teams/ - */ - private static class MockGithubServlet extends DefaultServlet { - private String currentLogin; - private List organizations; - private List> teams; - - private JenkinsRule jenkinsRule; - private URI serverUri; - - public MockGithubServlet(JenkinsRule jenkinsRule) { - this.jenkinsRule = jenkinsRule; - } - - public void setServerUrl(URI serverUri) { - this.serverUri = serverUri; - } - - @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { - switch (req.getRequestURI()) { - case "/user": - this.onUser(req, resp); - break; - case "/users/_specific_login_": - this.onUser(req, resp); - break; - case "/user/orgs": - this.onUserOrgs(req, resp); - break; - case "/user/teams": - this.onUserTeams(req, resp); - break; - case "/orgs/org-a": - this.onOrgs(req, resp, "org-a"); - break; - case "/orgs/org-a/teams": - this.onOrgsTeam(req, resp, "org-a"); - break; - case "/orgs/org-a/members/alice": - this.onOrgsMember(req, resp, "org-a", "alice"); - break; - case "/teams/7/members/alice": - this.onTeamMember(req, resp, "team-b", "alice"); - break; - case "/orgs/org-c": - this.onOrgs(req, resp, "org-c"); - break; - case "/orgs/org-c/teams": - this.onOrgsTeam(req, resp, "org-c"); - break; - case "/orgs/org-c/members/bob": - this.onOrgsMember(req, resp, "org-c", "bob"); - break; - case "/teams/7/members/bob": - this.onTeamMember(req, resp, "team-d", "bob"); - break; - case "/login/oauth/authorize": - this.onLoginOAuthAuthorize(req, resp); - break; - case "/login/oauth/access_token": - this.onLoginOAuthAccessToken(req, resp); - break; - default: - throw new RuntimeException("Url not mapped yet: " + req.getRequestURI()); - } - } - - private void onUser(HttpServletRequest req, HttpServletResponse resp) throws IOException { - resp.getWriter().write(JSONObject.fromObject( - new HashMap() {{ - put("login", currentLogin); - put("name", currentLogin + "_name"); - // to avoid triggering a second call, due to GithubSecurityRealm:382 - put("created_at", "2008-01-14T04:33:35Z"); - put("url", serverUri + "/users/_specific_login_"); - }} - ).toString()); - } - - private void onUserOrgs(HttpServletRequest req, HttpServletResponse resp) throws IOException { - List> responseBody = new ArrayList<>(); - for (String orgName : organizations) { - final String orgName_ = orgName; - responseBody.add(new HashMap() {{ - put("login", orgName_); - }}); - } - - resp.getWriter().write(JSONArray.fromObject(responseBody).toString()); - } - - private void onOrgs(HttpServletRequest req, HttpServletResponse resp, final String orgName) throws IOException { - Map responseBody = new HashMap() {{ - put("login", orgName); - }}; - - resp.getWriter().write(JSONObject.fromObject(responseBody).toString()); - } - - private void onOrgsMember(HttpServletRequest req, HttpServletResponse resp, String orgName, String userName) { - resp.setStatus(HttpServletResponse.SC_NO_CONTENT); - // 302 / 404 responses not implemented - } - - private void onTeamMember(HttpServletRequest req, HttpServletResponse resp, String orgName, String userName) { - resp.setStatus(HttpServletResponse.SC_NO_CONTENT); - // 302 / 404 responses not implemented - } - - private void onOrgsTeam(HttpServletRequest req, HttpServletResponse resp, final String orgName) throws IOException { - List> responseBody = new ArrayList<>(); - for (Map team : teams) { - final String teamName_ = team.get("name"); - final String slug = team.get("slug"); - responseBody.add(new HashMap() {{ - put("id", 7); - put("login", teamName_ + "_login"); - put("name", teamName_); - put("slug", slug); - put("organization", new HashMap() {{ - put("login", orgName); - }}); - }}); - } - - resp.getWriter().write(JSONArray.fromObject(responseBody).toString()); - } - - private void onUserTeams(HttpServletRequest req, HttpServletResponse resp) throws IOException { - List> responseBody = new ArrayList<>(); - for (Map team : teams) { - final String teamName_ = team.get("name"); - final String slug = team.get("slug"); - responseBody.add(new HashMap() {{ - put("login", teamName_ + "_login"); - put("name", teamName_); - put("slug", slug); - put("organization", new HashMap() {{ - put("login", organizations.get(0)); - }}); - }}); - } - - resp.getWriter().write(JSONArray.fromObject(responseBody).toString()); - } - - private void onLoginOAuthAuthorize(HttpServletRequest req, HttpServletResponse resp) throws IOException { - String code = "test"; - String state = req.getParameter("state"); - resp.sendRedirect(jenkinsRule.getURL() + "securityRealm/finishLogin?code=" + code + "&state=" + state); - } - - private void onLoginOAuthAccessToken(HttpServletRequest req, HttpServletResponse resp) throws IOException { - resp.getWriter().write("access_token=RANDOM_ACCESS_TOKEN"); - } - } - - @Before - public void prepareRealmAndWebClient() throws Exception { - this.setupMockGithubServer(); - this.setupRealm(); - wc = j.createWebClient(); - GithubAuthenticationToken.clearCaches(); - } - - private void setupRealm(){ - String githubWebUri = serverUri.toString(); - String githubApiUri = serverUri.toString(); - String clientID = "xxx"; - String clientSecret = "yyy"; - String oauthScopes = "read:org"; - - GithubSecurityRealm githubSecurityRealm = new GithubSecurityRealm( - githubWebUri, - githubApiUri, - clientID, - clientSecret, - oauthScopes - ); - - j.jenkins.setSecurityRealm(githubSecurityRealm); - } - - @After - public void stopEmbeddedJettyServer() { - try { - server.stop(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Issue("JENKINS-47113") - @Test - public void testUsingGithubToken() throws IOException { - String aliceLogin = "alice"; - servlet.currentLogin = aliceLogin; - servlet.organizations = Collections.singletonList("org-a"); - Map team = new HashMap<>(); - team.put("slug", "team-b"); - team.put("name", "Team D"); - servlet.teams = Collections.singletonList(team); - - User aliceUser = User.getById(aliceLogin, true); - String aliceApiRestToken = aliceUser.getProperty(ApiTokenProperty.class).getApiToken(); - String aliceGitHubToken = "SPECIFIC_TOKEN"; - - // request whoAmI with ApiRestToken => group populated - makeRequestWithAuthCodeAndVerify(encodeBasic(aliceLogin, aliceApiRestToken), "alice", Arrays.asList("authenticated", "org-a", "org-a*team-b")); - - // request whoAmI with GitHubToken => group populated - makeRequestWithAuthCodeAndVerify(encodeBasic(aliceLogin, aliceGitHubToken), "alice", Arrays.asList("authenticated", "org-a", "org-a*team-b")); - - GithubAuthenticationToken.clearCaches(); - - // no authentication in session but use the cache - makeRequestWithAuthCodeAndVerify(encodeBasic(aliceLogin, aliceApiRestToken), "alice", Arrays.asList("authenticated", "org-a", "org-a*team-b")); - - wc = j.createWebClient(); - // no session at all, use the cache also - makeRequestWithAuthCodeAndVerify(encodeBasic(aliceLogin, aliceApiRestToken), "alice", Arrays.asList("authenticated", "org-a", "org-a*team-b")); - } - - @Issue("JENKINS-47113") - @Test - public void testUsingGithubLogin() throws IOException { - String bobLogin = "bob"; - servlet.currentLogin = bobLogin; - servlet.organizations = Collections.singletonList("org-c"); - Map team = new HashMap<>(); - team.put("slug", "team-d"); - team.put("name", "Team D"); - servlet.teams = Collections.singletonList(team); - - User bobUser = User.getById(bobLogin, true); - String bobApiRestToken = bobUser.getProperty(ApiTokenProperty.class).getApiToken(); - - // request whoAmI with ApiRestToken => group populated - makeRequestWithAuthCodeAndVerify(encodeBasic(bobLogin, bobApiRestToken), "bob", Arrays.asList("authenticated", "org-c", "org-c*team-d")); - // request whoAmI with GitHub OAuth => group populated - makeRequestUsingOAuth("bob", Arrays.asList("authenticated", "org-c", "org-c*team-d")); - - // use only the session - // request whoAmI with ApiRestToken => group populated (due to login event) - makeRequestWithAuthCodeAndVerify(encodeBasic(bobLogin, bobApiRestToken), "bob", Arrays.asList("authenticated", "org-c", "org-c*team-d")); - - GithubAuthenticationToken.clearCaches(); - - // retrieve the security group even without the cookie (using LastGrantedAuthorities this time) - makeRequestWithAuthCodeAndVerify(encodeBasic(bobLogin, bobApiRestToken), "bob", Arrays.asList("authenticated", "org-c", "org-c*team-d")); - } - - @Issue("JENKINS-60200") - @Test - public void testInvalidateAuthorizationCacheOnFreshLogin() throws IOException { - String bobLogin = "bob"; - servlet.currentLogin = bobLogin; - servlet.organizations = Collections.singletonList("org-c"); - Map team = new HashMap<>(); - team.put("slug", "team-d"); - team.put("name", "Team D"); - servlet.teams = Collections.singletonList(team); - - User bobUser = User.getById(bobLogin, true); - String bobApiRestToken = bobUser.getProperty(ApiTokenProperty.class).getApiToken(); - - // request whoAmI with ApiRestToken => group populated - makeRequestWithAuthCodeAndVerify(encodeBasic(bobLogin, bobApiRestToken), "bob", Arrays.asList("authenticated", "org-c", "org-c*team-d")); - // request whoAmI with GitHub OAuth => group populated - makeRequestUsingOAuth("bob", Arrays.asList("authenticated", "org-c", "org-c*team-d")); - - // Switch the teams - team.put("slug", "team-e"); - team.put("name", "Team E"); - servlet.teams = Collections.singletonList(team); - - // With just AuthCode, the cache is not invalidated - makeRequestWithAuthCodeAndVerify(encodeBasic(bobLogin, bobApiRestToken), "bob", Arrays.asList("authenticated", "org-c", "org-c*team-d")); - - // With OAuth the cache is invalidated - makeRequestUsingOAuth("bob", Arrays.asList("authenticated", "org-c", "org-c*team-e")); - } - - - private void makeRequestWithAuthCodeAndVerify(String authCode, String expectedLogin, List expectedAuthorities) throws IOException { - WebRequest req = new WebRequest(new URL(j.getURL(), "whoAmI/api/json")); - req.setEncodingType(null); - if (authCode != null) - req.setAdditionalHeader("Authorization", authCode); - Page p = wc.getPage(req); - - assertResponse(p, expectedLogin, expectedAuthorities); - } - - private void makeRequestUsingOAuth(String expectedLogin, List expectedAuthorities) throws IOException { - WebRequest req = new WebRequest(new URL(j.getURL(), "securityRealm/commenceLogin")); - req.setEncodingType(null); - - String referer = j.getURL() + "whoAmI/api/json"; - req.setAdditionalHeader("Referer", referer); - Page p = wc.getPage(req); - - assertResponse(p, expectedLogin, expectedAuthorities); - } - - private void assertResponse(Page p, String expectedLogin, List expectedAuthorities) { - String response = p.getWebResponse().getContentAsString().trim(); - JSONObject respObject = JSONObject.fromObject(response); - if (expectedLogin != null) { - assertEquals(expectedLogin, respObject.getString("name")); - } - if (expectedAuthorities != null) { - // we use set to avoid having duplicated "authenticated" - // as that will be corrected in https://github.com/jenkinsci/jenkins/pull/3123 - Set actualAuthorities = new HashSet<>( - JSONArray.toCollection( - respObject.getJSONArray("authorities"), - String.class - ) - ); - - Set expectedAuthoritiesSet = new HashSet<>(expectedAuthorities); - - assertEquals(String.format("They do not have the same content, expected=%s, actual=%s", expectedAuthorities, actualAuthorities), expectedAuthoritiesSet, actualAuthorities); - } - } - - private String encodeBasic(String login, String credentials) { - return "Basic " + Scrambler.scramble(login + ":" + credentials); - } +import static org.junit.jupiter.api.Assertions.assertEquals; + +@WithJenkins +class GithubAccessTokenPropertyTest { + + private JenkinsRule j; + + private JenkinsRule.WebClient wc; + + private Server server; + private URI serverUri; + private MockGithubServlet servlet; + + public void setupMockGithubServer() throws Exception { + server = new Server(); + ServerConnector connector = new ServerConnector(server); + // auto-bind to available port + connector.setPort(0); + server.addConnector(connector); + + servlet = new MockGithubServlet(j); + + ServletContextHandler context = new ServletContextHandler(); + ServletHolder servletHolder = new ServletHolder("default", servlet); + context.addServlet(servletHolder, "/*"); + server.setHandler(context); + + server.start(); + + String host = connector.getHost(); + if (host == null) { + host = "localhost"; + } + + int port = connector.getLocalPort(); + serverUri = new URI(String.format("http://%s:%d/", host, port)); + servlet.setServerUrl(serverUri); + } + + /** + * Based on documentation found at + * https://developer.github.com/v3/users/ + * https://developer.github.com/v3/orgs/ + * https://developer.github.com/v3/orgs/teams/ + */ + private static class MockGithubServlet extends DefaultServlet { + private String currentLogin; + private List organizations; + private List> teams; + + private final JenkinsRule jenkinsRule; + private URI serverUri; + + public MockGithubServlet(JenkinsRule jenkinsRule) { + this.jenkinsRule = jenkinsRule; + } + + public void setServerUrl(URI serverUri) { + this.serverUri = serverUri; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + switch (req.getRequestURI()) { + case "/user": + this.onUser(req, resp); + break; + case "/users/_specific_login_": + this.onUser(req, resp); + break; + case "/user/orgs": + this.onUserOrgs(req, resp); + break; + case "/user/teams": + this.onUserTeams(req, resp); + break; + case "/orgs/org-a": + this.onOrgs(req, resp, "org-a"); + break; + case "/orgs/org-a/teams": + this.onOrgsTeam(req, resp, "org-a"); + break; + case "/orgs/org-a/members/alice": + this.onOrgsMember(req, resp, "org-a", "alice"); + break; + case "/teams/7/members/alice": + this.onTeamMember(req, resp, "team-b", "alice"); + break; + case "/orgs/org-c": + this.onOrgs(req, resp, "org-c"); + break; + case "/orgs/org-c/teams": + this.onOrgsTeam(req, resp, "org-c"); + break; + case "/orgs/org-c/members/bob": + this.onOrgsMember(req, resp, "org-c", "bob"); + break; + case "/teams/7/members/bob": + this.onTeamMember(req, resp, "team-d", "bob"); + break; + case "/login/oauth/authorize": + this.onLoginOAuthAuthorize(req, resp); + break; + case "/login/oauth/access_token": + this.onLoginOAuthAccessToken(req, resp); + break; + default: + throw new RuntimeException("Url not mapped yet: " + req.getRequestURI()); + } + } + + private void onUser(HttpServletRequest req, HttpServletResponse resp) throws IOException { + resp.getWriter().write(JSONObject.fromObject( + new HashMap() {{ + put("login", currentLogin); + put("name", currentLogin + "_name"); + // to avoid triggering a second call, due to GithubSecurityRealm:382 + put("created_at", "2008-01-14T04:33:35Z"); + put("url", serverUri + "/users/_specific_login_"); + }} + ).toString()); + } + + private void onUserOrgs(HttpServletRequest req, HttpServletResponse resp) throws IOException { + List> responseBody = new ArrayList<>(); + for (String orgName : organizations) { + final String orgName_ = orgName; + responseBody.add(new HashMap<>() {{ + put("login", orgName_); + }}); + } + + resp.getWriter().write(JSONArray.fromObject(responseBody).toString()); + } + + private void onOrgs(HttpServletRequest req, HttpServletResponse resp, final String orgName) throws IOException { + Map responseBody = new HashMap<>() {{ + put("login", orgName); + }}; + + resp.getWriter().write(JSONObject.fromObject(responseBody).toString()); + } + + private void onOrgsMember(HttpServletRequest req, HttpServletResponse resp, String orgName, String userName) { + resp.setStatus(HttpServletResponse.SC_NO_CONTENT); + // 302 / 404 responses not implemented + } + + private void onTeamMember(HttpServletRequest req, HttpServletResponse resp, String orgName, String userName) { + resp.setStatus(HttpServletResponse.SC_NO_CONTENT); + // 302 / 404 responses not implemented + } + + private void onOrgsTeam(HttpServletRequest req, HttpServletResponse resp, final String orgName) throws IOException { + List> responseBody = new ArrayList<>(); + for (Map team : teams) { + final String teamName_ = team.get("name"); + final String slug = team.get("slug"); + responseBody.add(new HashMap<>() {{ + put("id", 7); + put("login", teamName_ + "_login"); + put("name", teamName_); + put("slug", slug); + put("organization", new HashMap() {{ + put("login", orgName); + }}); + }}); + } + + resp.getWriter().write(JSONArray.fromObject(responseBody).toString()); + } + + private void onUserTeams(HttpServletRequest req, HttpServletResponse resp) throws IOException { + List> responseBody = new ArrayList<>(); + for (Map team : teams) { + final String teamName_ = team.get("name"); + final String slug = team.get("slug"); + responseBody.add(new HashMap<>() {{ + put("login", teamName_ + "_login"); + put("name", teamName_); + put("slug", slug); + put("organization", new HashMap() {{ + put("login", organizations.get(0)); + }}); + }}); + } + + resp.getWriter().write(JSONArray.fromObject(responseBody).toString()); + } + + private void onLoginOAuthAuthorize(HttpServletRequest req, HttpServletResponse resp) throws IOException { + String code = "test"; + String state = req.getParameter("state"); + resp.sendRedirect(jenkinsRule.getURL() + "securityRealm/finishLogin?code=" + code + "&state=" + state); + } + + private void onLoginOAuthAccessToken(HttpServletRequest req, HttpServletResponse resp) throws IOException { + resp.getWriter().write("access_token=RANDOM_ACCESS_TOKEN"); + } + } + + @BeforeEach + void prepareRealmAndWebClient(JenkinsRule j) throws Exception { + this.j = j; + this.setupMockGithubServer(); + this.setupRealm(); + wc = j.createWebClient(); + GithubAuthenticationToken.clearCaches(); + } + + private void setupRealm() { + String githubWebUri = serverUri.toString(); + String githubApiUri = serverUri.toString(); + String clientID = "xxx"; + String clientSecret = "yyy"; + String oauthScopes = "read:org"; + + GithubSecurityRealm githubSecurityRealm = new GithubSecurityRealm( + githubWebUri, + githubApiUri, + clientID, + clientSecret, + oauthScopes + ); + + j.jenkins.setSecurityRealm(githubSecurityRealm); + } + + @AfterEach + void stopEmbeddedJettyServer() throws Exception { + server.stop(); + } + + @Issue("JENKINS-47113") + @Test + void testUsingGithubToken() throws IOException { + String aliceLogin = "alice"; + servlet.currentLogin = aliceLogin; + servlet.organizations = Collections.singletonList("org-a"); + Map team = new HashMap<>(); + team.put("slug", "team-b"); + team.put("name", "Team D"); + servlet.teams = Collections.singletonList(team); + + User aliceUser = User.getById(aliceLogin, true); + String aliceApiRestToken = aliceUser.getProperty(ApiTokenProperty.class).getApiToken(); + String aliceGitHubToken = "SPECIFIC_TOKEN"; + + // request whoAmI with ApiRestToken => group populated + makeRequestWithAuthCodeAndVerify(encodeBasic(aliceLogin, aliceApiRestToken), "alice", Arrays.asList("authenticated", "org-a", "org-a*team-b")); + + // request whoAmI with GitHubToken => group populated + makeRequestWithAuthCodeAndVerify(encodeBasic(aliceLogin, aliceGitHubToken), "alice", Arrays.asList("authenticated", "org-a", "org-a*team-b")); + + GithubAuthenticationToken.clearCaches(); + + // no authentication in session but use the cache + makeRequestWithAuthCodeAndVerify(encodeBasic(aliceLogin, aliceApiRestToken), "alice", Arrays.asList("authenticated", "org-a", "org-a*team-b")); + + wc = j.createWebClient(); + // no session at all, use the cache also + makeRequestWithAuthCodeAndVerify(encodeBasic(aliceLogin, aliceApiRestToken), "alice", Arrays.asList("authenticated", "org-a", "org-a*team-b")); + } + + @Issue("JENKINS-47113") + @Test + void testUsingGithubLogin() throws IOException { + String bobLogin = "bob"; + servlet.currentLogin = bobLogin; + servlet.organizations = Collections.singletonList("org-c"); + Map team = new HashMap<>(); + team.put("slug", "team-d"); + team.put("name", "Team D"); + servlet.teams = Collections.singletonList(team); + + User bobUser = User.getById(bobLogin, true); + String bobApiRestToken = bobUser.getProperty(ApiTokenProperty.class).getApiToken(); + + // request whoAmI with ApiRestToken => group populated + makeRequestWithAuthCodeAndVerify(encodeBasic(bobLogin, bobApiRestToken), "bob", Arrays.asList("authenticated", "org-c", "org-c*team-d")); + // request whoAmI with GitHub OAuth => group populated + makeRequestUsingOAuth("bob", Arrays.asList("authenticated", "org-c", "org-c*team-d")); + + // use only the session + // request whoAmI with ApiRestToken => group populated (due to login event) + makeRequestWithAuthCodeAndVerify(encodeBasic(bobLogin, bobApiRestToken), "bob", Arrays.asList("authenticated", "org-c", "org-c*team-d")); + + GithubAuthenticationToken.clearCaches(); + + // retrieve the security group even without the cookie (using LastGrantedAuthorities this time) + makeRequestWithAuthCodeAndVerify(encodeBasic(bobLogin, bobApiRestToken), "bob", Arrays.asList("authenticated", "org-c", "org-c*team-d")); + } + + @Issue("JENKINS-60200") + @Test + void testInvalidateAuthorizationCacheOnFreshLogin() throws IOException { + String bobLogin = "bob"; + servlet.currentLogin = bobLogin; + servlet.organizations = Collections.singletonList("org-c"); + Map team = new HashMap<>(); + team.put("slug", "team-d"); + team.put("name", "Team D"); + servlet.teams = Collections.singletonList(team); + + User bobUser = User.getById(bobLogin, true); + String bobApiRestToken = bobUser.getProperty(ApiTokenProperty.class).getApiToken(); + + // request whoAmI with ApiRestToken => group populated + makeRequestWithAuthCodeAndVerify(encodeBasic(bobLogin, bobApiRestToken), "bob", Arrays.asList("authenticated", "org-c", "org-c*team-d")); + // request whoAmI with GitHub OAuth => group populated + makeRequestUsingOAuth("bob", Arrays.asList("authenticated", "org-c", "org-c*team-d")); + + // Switch the teams + team.put("slug", "team-e"); + team.put("name", "Team E"); + servlet.teams = Collections.singletonList(team); + + // With just AuthCode, the cache is not invalidated + makeRequestWithAuthCodeAndVerify(encodeBasic(bobLogin, bobApiRestToken), "bob", Arrays.asList("authenticated", "org-c", "org-c*team-d")); + + // With OAuth the cache is invalidated + makeRequestUsingOAuth("bob", Arrays.asList("authenticated", "org-c", "org-c*team-e")); + } + + private void makeRequestWithAuthCodeAndVerify(String authCode, String expectedLogin, List expectedAuthorities) throws IOException { + WebRequest req = new WebRequest(new URL(j.getURL(), "whoAmI/api/json")); + req.setEncodingType(null); + if (authCode != null) + req.setAdditionalHeader("Authorization", authCode); + Page p = wc.getPage(req); + + assertResponse(p, expectedLogin, expectedAuthorities); + } + + private void makeRequestUsingOAuth(String expectedLogin, List expectedAuthorities) throws IOException { + WebRequest req = new WebRequest(new URL(j.getURL(), "securityRealm/commenceLogin")); + req.setEncodingType(null); + + String referer = j.getURL() + "whoAmI/api/json"; + req.setAdditionalHeader("Referer", referer); + Page p = wc.getPage(req); + + assertResponse(p, expectedLogin, expectedAuthorities); + } + + private static void assertResponse(Page p, String expectedLogin, List expectedAuthorities) { + String response = p.getWebResponse().getContentAsString().trim(); + JSONObject respObject = JSONObject.fromObject(response); + if (expectedLogin != null) { + assertEquals(expectedLogin, respObject.getString("name")); + } + if (expectedAuthorities != null) { + // we use set to avoid having duplicated "authenticated" + // as that will be corrected in https://github.com/jenkinsci/jenkins/pull/3123 + Set actualAuthorities = new HashSet<>( + JSONArray.toCollection( + respObject.getJSONArray("authorities"), + String.class + ) + ); + + Set expectedAuthoritiesSet = new HashSet<>(expectedAuthorities); + + assertEquals(expectedAuthoritiesSet, actualAuthorities, String.format("They do not have the same content, expected=%s, actual=%s", expectedAuthorities, actualAuthorities)); + } + } + + private static String encodeBasic(String login, String credentials) { + return "Basic " + Scrambler.scramble(login + ":" + credentials); + } } diff --git a/src/test/java/org/jenkinsci/plugins/GithubAuthenticationTokenTest.java b/src/test/java/org/jenkinsci/plugins/GithubAuthenticationTokenTest.java index 1f301bca..198e942c 100644 --- a/src/test/java/org/jenkinsci/plugins/GithubAuthenticationTokenTest.java +++ b/src/test/java/org/jenkinsci/plugins/GithubAuthenticationTokenTest.java @@ -1,13 +1,10 @@ package org.jenkinsci.plugins; -import static org.junit.Assert.assertEquals; - -import java.io.IOException; import jenkins.model.Jenkins; import org.apache.commons.lang.SerializationUtils; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.kohsuke.github.GHMyself; import org.kohsuke.github.GitHub; import org.kohsuke.github.GitHubBuilder; @@ -16,66 +13,58 @@ import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -public class GithubAuthenticationTokenTest { +import org.mockito.junit.jupiter.MockitoExtension; - @Mock - private GithubSecurityRealm securityRealm; - - private AutoCloseable closeable; +import java.io.IOException; - @Before - public void setUp() { - closeable = MockitoAnnotations.openMocks(this); - } +import static org.junit.jupiter.api.Assertions.assertEquals; - @After - public void tearDown() throws Exception { - closeable.close(); - } +@ExtendWith(MockitoExtension.class) +class GithubAuthenticationTokenTest { - private void mockJenkins(MockedStatic mockedJenkins) { - Jenkins jenkins = Mockito.mock(Jenkins.class); - mockedJenkins.when(Jenkins::get).thenReturn(jenkins); - Mockito.when(jenkins.getSecurityRealm()).thenReturn(securityRealm); - Mockito.when(securityRealm.getOauthScopes()).thenReturn("read:org"); - } + @Mock(strictness = Mock.Strictness.LENIENT) + private GithubSecurityRealm securityRealm; - @Test - public void testTokenSerialization() throws IOException { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); - MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { - mockJenkins(mockedJenkins); - mockGHMyselfAs(mockedGitHubBuilder, "bob"); - GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - byte[] serializedToken = SerializationUtils.serialize(authenticationToken); - GithubAuthenticationToken deserializedToken = (GithubAuthenticationToken) SerializationUtils.deserialize(serializedToken); - assertEquals(deserializedToken.getAccessToken(), authenticationToken.getAccessToken()); - assertEquals(deserializedToken.getPrincipal(), authenticationToken.getPrincipal()); - assertEquals(deserializedToken.getGithubServer(), authenticationToken.getGithubServer()); - assertEquals(deserializedToken.getMyself().getLogin(), deserializedToken.getMyself().getLogin()); - } - } + @AfterEach + public void tearDown() { + GithubAuthenticationToken.clearCaches(); + } - @After - public void after() { - GithubAuthenticationToken.clearCaches(); - } + private void mockJenkins(MockedStatic mockedJenkins) { + Jenkins jenkins = Mockito.mock(Jenkins.class); + mockedJenkins.when(Jenkins::get).thenReturn(jenkins); + Mockito.when(jenkins.getSecurityRealm()).thenReturn(securityRealm); + Mockito.when(securityRealm.getOauthScopes()).thenReturn("read:org"); + } - private GHMyself mockGHMyselfAs(MockedStatic mockedGitHubBuilder, String username) throws IOException { - GitHub gh = Mockito.mock(GitHub.class); - GitHubBuilder builder = Mockito.mock(GitHubBuilder.class); - mockedGitHubBuilder.when(GitHubBuilder::fromEnvironment).thenReturn(builder); - Mockito.when(builder.withEndpoint("https://api.github.com")).thenReturn(builder); - Mockito.when(builder.withOAuthToken("accessToken")).thenReturn(builder); - Mockito.when(builder.withRateLimitHandler(RateLimitHandler.FAIL)).thenReturn(builder); - Mockito.when(builder.withConnector(Mockito.any(OkHttpGitHubConnector.class))).thenReturn(builder); - Mockito.when(builder.build()).thenReturn(gh); - GHMyself me = Mockito.mock(GHMyself.class); - Mockito.when(gh.getMyself()).thenReturn(me); - Mockito.when(me.getLogin()).thenReturn(username); - return me; - } + @Test + void testTokenSerialization() throws IOException { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); + MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { + mockJenkins(mockedJenkins); + mockGHMyselfAs(mockedGitHubBuilder, "bob"); + GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); + byte[] serializedToken = SerializationUtils.serialize(authenticationToken); + GithubAuthenticationToken deserializedToken = (GithubAuthenticationToken) SerializationUtils.deserialize(serializedToken); + assertEquals(deserializedToken.getAccessToken(), authenticationToken.getAccessToken()); + assertEquals(deserializedToken.getPrincipal(), authenticationToken.getPrincipal()); + assertEquals(deserializedToken.getGithubServer(), authenticationToken.getGithubServer()); + assertEquals(deserializedToken.getMyself().getLogin(), deserializedToken.getMyself().getLogin()); + } + } + private static GHMyself mockGHMyselfAs(MockedStatic mockedGitHubBuilder, String username) throws IOException { + GitHub gh = Mockito.mock(GitHub.class); + GitHubBuilder builder = Mockito.mock(GitHubBuilder.class); + mockedGitHubBuilder.when(GitHubBuilder::fromEnvironment).thenReturn(builder); + Mockito.when(builder.withEndpoint("https://api.github.com")).thenReturn(builder); + Mockito.when(builder.withOAuthToken("accessToken")).thenReturn(builder); + Mockito.when(builder.withRateLimitHandler(RateLimitHandler.FAIL)).thenReturn(builder); + Mockito.when(builder.withConnector(Mockito.any(OkHttpGitHubConnector.class))).thenReturn(builder); + Mockito.when(builder.build()).thenReturn(gh); + GHMyself me = Mockito.mock(GHMyself.class); + Mockito.when(gh.getMyself()).thenReturn(me); + Mockito.when(me.getLogin()).thenReturn(username); + return me; + } } diff --git a/src/test/java/org/jenkinsci/plugins/GithubAuthorizationStrategyTest.java b/src/test/java/org/jenkinsci/plugins/GithubAuthorizationStrategyTest.java index f5307fbe..2ae83206 100644 --- a/src/test/java/org/jenkinsci/plugins/GithubAuthorizationStrategyTest.java +++ b/src/test/java/org/jenkinsci/plugins/GithubAuthorizationStrategyTest.java @@ -1,46 +1,47 @@ /** - The MIT License - -Copyright (c) 2015 Sam Gleske - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. + * The MIT License + *

+ * Copyright (c) 2015 Sam Gleske + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ package org.jenkinsci.plugins; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +class GithubAuthorizationStrategyTest { -import org.junit.Test; + @Test + void testEquals_true() { + GithubAuthorizationStrategy a = new GithubAuthorizationStrategy("", false, true, false, "", false, false, false, false); + GithubAuthorizationStrategy b = new GithubAuthorizationStrategy("", false, true, false, "", false, false, false, false); + assertEquals(a, b); + } -public class GithubAuthorizationStrategyTest { - @Test - public void testEquals_true() { - GithubAuthorizationStrategy a = new GithubAuthorizationStrategy("", false, true, false, "", false, false, false, false); - GithubAuthorizationStrategy b = new GithubAuthorizationStrategy("", false, true, false, "", false, false, false, false); - assertEquals(a, b); - } - @Test - public void testEquals_false() { - GithubAuthorizationStrategy a = new GithubAuthorizationStrategy("", false, true, false, "", false, false, false, false); - GithubAuthorizationStrategy b = new GithubAuthorizationStrategy("", false, false, false, "", false, false, false, false); - assertNotEquals(a, b); - assertNotEquals("", a); - } + @Test + void testEquals_false() { + GithubAuthorizationStrategy a = new GithubAuthorizationStrategy("", false, true, false, "", false, false, false, false); + GithubAuthorizationStrategy b = new GithubAuthorizationStrategy("", false, false, false, "", false, false, false, false); + assertNotEquals(a, b); + } } diff --git a/src/test/java/org/jenkinsci/plugins/GithubLogoutActionTest.java b/src/test/java/org/jenkinsci/plugins/GithubLogoutActionTest.java index d6fcb9f7..a766f7ac 100644 --- a/src/test/java/org/jenkinsci/plugins/GithubLogoutActionTest.java +++ b/src/test/java/org/jenkinsci/plugins/GithubLogoutActionTest.java @@ -1,77 +1,77 @@ /** - The MIT License - -Copyright (c) 2016 Sam Gleske - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. + * The MIT License + *

+ * Copyright (c) 2016 Sam Gleske + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ package org.jenkinsci.plugins; -import static org.junit.Assert.assertEquals; - import jenkins.model.Jenkins; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; -@RunWith(MockitoJUnitRunner.class) -public class GithubLogoutActionTest { +@ExtendWith(MockitoExtension.class) +class GithubLogoutActionTest { - @Mock - private GithubSecurityRealm securityRealm; + @Mock + private GithubSecurityRealm securityRealm; - @Mock - private GithubSecurityRealm.DescriptorImpl descriptor; + @Mock + private GithubSecurityRealm.DescriptorImpl descriptor; - private void mockJenkins(MockedStatic mockedJenkins) { - Jenkins jenkins = Mockito.mock(Jenkins.class); - mockedJenkins.when(Jenkins::get).thenReturn(jenkins); - Mockito.when(jenkins.getSecurityRealm()).thenReturn(securityRealm); - Mockito.when(securityRealm.getDescriptor()).thenReturn(descriptor); - Mockito.when(descriptor.getDefaultGithubWebUri()).thenReturn("https://github.com"); - } + private void mockJenkins(MockedStatic mockedJenkins) { + Jenkins jenkins = Mockito.mock(Jenkins.class); + mockedJenkins.when(Jenkins::get).thenReturn(jenkins); + Mockito.when(jenkins.getSecurityRealm()).thenReturn(securityRealm); + Mockito.when(securityRealm.getDescriptor()).thenReturn(descriptor); + Mockito.when(descriptor.getDefaultGithubWebUri()).thenReturn("https://github.com"); + } - private void mockGithubSecurityRealmWebUriFor(String host) { - Mockito.when(securityRealm.getGithubWebUri()).thenReturn(host); - } + private void mockGithubSecurityRealmWebUriFor(String host) { + Mockito.when(securityRealm.getGithubWebUri()).thenReturn(host); + } - @Test - public void testGetGitHubText_gh() { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class)) { - mockJenkins(mockedJenkins); - mockGithubSecurityRealmWebUriFor("https://github.com"); - GithubLogoutAction ghlogout = new GithubLogoutAction(); - assertEquals("GitHub", ghlogout.getGitHubText()); - } - } + @Test + void testGetGitHubText_gh() { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class)) { + mockJenkins(mockedJenkins); + mockGithubSecurityRealmWebUriFor("https://github.com"); + GithubLogoutAction ghlogout = new GithubLogoutAction(); + assertEquals("GitHub", ghlogout.getGitHubText()); + } + } - @Test - public void testGetGitHubText_ghe() { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class)) { - mockJenkins(mockedJenkins); - mockGithubSecurityRealmWebUriFor("https://ghe.example.com"); - GithubLogoutAction ghlogout = new GithubLogoutAction(); - assertEquals("GitHub Enterprise", ghlogout.getGitHubText()); - } - } + @Test + void testGetGitHubText_ghe() { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class)) { + mockJenkins(mockedJenkins); + mockGithubSecurityRealmWebUriFor("https://ghe.example.com"); + GithubLogoutAction ghlogout = new GithubLogoutAction(); + assertEquals("GitHub Enterprise", ghlogout.getGitHubText()); + } + } } diff --git a/src/test/java/org/jenkinsci/plugins/GithubRequireOrganizationMembershipACLTest.java b/src/test/java/org/jenkinsci/plugins/GithubRequireOrganizationMembershipACLTest.java index 830d8aee..05a4932c 100644 --- a/src/test/java/org/jenkinsci/plugins/GithubRequireOrganizationMembershipACLTest.java +++ b/src/test/java/org/jenkinsci/plugins/GithubRequireOrganizationMembershipACLTest.java @@ -1,34 +1,28 @@ /** - The MIT License - -Copyright (c) 2014 Alex Rothenberg - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - - + * The MIT License + *

+ * Copyright (c) 2014 Alex Rothenberg + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ package org.jenkinsci.plugins; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - import hudson.model.Computer; import hudson.model.Hudson; import hudson.model.Item; @@ -39,10 +33,6 @@ of this software and associated documentation files (the "Software"), to deal import hudson.scm.NullSCM; import hudson.security.Permission; import hudson.security.PermissionScope; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import jenkins.branch.Branch; import jenkins.branch.MultiBranchProject; import jenkins.model.Jenkins; @@ -51,9 +41,10 @@ of this software and associated documentation files (the "Software"), to deal import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.multibranch.BranchJobProperty; import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.kohsuke.github.GHMyself; import org.kohsuke.github.GHPerson; import org.kohsuke.github.GHRepository; @@ -67,619 +58,624 @@ of this software and associated documentation files (the "Software"), to deal import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.SimpleGrantedAuthority; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + /** - * * @author alex */ -public class GithubRequireOrganizationMembershipACLTest { - - private GitHub gh; - - @Mock - private GithubSecurityRealm securityRealm; - - private boolean allowAnonymousReadPermission; - private boolean allowAnonymousJobStatusPermission; - private boolean useRepositoryPermissions; - private boolean authenticatedUserReadPermission; - private boolean authenticatedUserCreateJobPermission; - private boolean allowAnonymousWebhookPermission; - private boolean allowAnonymousCCTrayPermission; - - private AutoCloseable closeable; - - @Before - public void setUp() { - // default to: use repository permissions; don't allow anonymous read/view status; don't allow authenticated read/create - allowAnonymousReadPermission = false; - allowAnonymousJobStatusPermission = false; - useRepositoryPermissions = true; - authenticatedUserReadPermission = false; - authenticatedUserCreateJobPermission = false; - allowAnonymousWebhookPermission = false; - allowAnonymousCCTrayPermission = false; - - closeable = MockitoAnnotations.openMocks(this); - - Mockito.when(securityRealm.getOauthScopes()).thenReturn("read:org,repo"); - Mockito.when(securityRealm.hasScope("read:org")).thenReturn(true); - Mockito.when(securityRealm.hasScope("repo")).thenReturn(true); - } - - private void mockJenkins(MockedStatic mockedJenkins) { - Jenkins jenkins = Mockito.mock(Jenkins.class); - mockedJenkins.when(Jenkins::get).thenReturn(jenkins); - Mockito.when(jenkins.getSecurityRealm()).thenReturn(securityRealm); - Mockito.when(jenkins.getRootUrl()).thenReturn("https://www.jenkins.org/"); - } - - private static final Permission VIEW_JOBSTATUS_PERMISSION = new Permission(Item.PERMISSIONS, - "ViewStatus", - Messages._Item_READ_description(), - Permission.READ, - PermissionScope.ITEM); - private final Authentication ANONYMOUS_USER = new AnonymousAuthenticationToken("anonymous", - "anonymous", - List.of(new SimpleGrantedAuthority("anonymous"))); - - private GithubRequireOrganizationMembershipACL createACL() { - GithubRequireOrganizationMembershipACL acl = new GithubRequireOrganizationMembershipACL( - "admin", - "myOrg", - authenticatedUserReadPermission, - useRepositoryPermissions, - authenticatedUserCreateJobPermission, - allowAnonymousWebhookPermission, - allowAnonymousCCTrayPermission, - allowAnonymousReadPermission, - allowAnonymousJobStatusPermission); - acl.setAgentUserName("agent"); - return acl; - } - - private GithubRequireOrganizationMembershipACL aclForProject(Project project) { - return createACL().cloneForProject(project); - } - - private GithubRequireOrganizationMembershipACL aclForMultiBranchProject(MultiBranchProject multiBranchProject) { - return createACL().cloneForProject(multiBranchProject); - } - - private GithubRequireOrganizationMembershipACL aclForWorkflowJob(WorkflowJob workflowJob) { - return createACL().cloneForProject(workflowJob); - } - - private GHMyself mockGHMyselfAs(MockedStatic mockedGitHubBuilder, String username) throws IOException { - gh = Mockito.mock(GitHub.class); - GitHubBuilder builder = Mockito.mock(GitHubBuilder.class); - mockedGitHubBuilder.when(GitHubBuilder::fromEnvironment).thenReturn(builder); - Mockito.when(builder.withEndpoint("https://api.github.com")).thenReturn(builder); - Mockito.when(builder.withOAuthToken("accessToken")).thenReturn(builder); - Mockito.when(builder.withRateLimitHandler(RateLimitHandler.FAIL)).thenReturn(builder); - Mockito.when(builder.withConnector(Mockito.any(OkHttpGitHubConnector.class))).thenReturn(builder); - Mockito.when(builder.build()).thenReturn(gh); - GHMyself me = Mockito.mock(GHMyself.class); - Mockito.when(gh.getMyself()).thenReturn(me); - Mockito.when(me.getLogin()).thenReturn(username); - mockReposFor(me, Collections.emptyList()); - return me; - } - - // TODO: Add ability to set list of orgs user belongs to to check whitelisting! - - private void mockReposFor(GHPerson person, List repositories) { - PagedIterable pagedRepositories = Mockito.mock(PagedIterable.class); - Mockito.when(person.listRepositories(100)).thenReturn(pagedRepositories); - Mockito.when(pagedRepositories.asList()).thenReturn(repositories); - } - - private GHRepository mockRepository(String repositoryName, boolean isPublic, boolean admin, boolean push, boolean pull) throws IOException { - GHRepository ghRepository = Mockito.mock(GHRepository.class); - Mockito.when(gh.getRepository(repositoryName)).thenReturn(ghRepository); - Mockito.when(ghRepository.isPrivate()).thenReturn(!isPublic); - Mockito.when(ghRepository.hasAdminAccess()).thenReturn(admin); - Mockito.when(ghRepository.hasPushAccess()).thenReturn(push); - Mockito.when(ghRepository.hasPullAccess()).thenReturn(pull); - Mockito.when(ghRepository.getFullName()).thenReturn(repositoryName); - return ghRepository; - } - - private GHRepository mockPublicRepository(String repositoryName) throws IOException { - return mockRepository(repositoryName, true, false, false, false); - } - - private Project mockProject(String url) { - Project project = Mockito.mock(Project.class); - GitSCM gitSCM = Mockito.mock(GitSCM.class); - UserRemoteConfig userRemoteConfig = Mockito.mock(UserRemoteConfig.class); - List userRemoteConfigs = Collections.singletonList(userRemoteConfig); - Mockito.when(project.getScm()).thenReturn(gitSCM); - Mockito.when(gitSCM.getUserRemoteConfigs()).thenReturn(userRemoteConfigs); - Mockito.when(userRemoteConfig.getUrl()).thenReturn(url); - return project; - } - - private WorkflowJob mockWorkflowJob(String url) { - WorkflowJob project = Mockito.mock(WorkflowJob.class); - GitSCM gitSCM = Mockito.mock(GitSCM.class); - Branch branch = Mockito.mock(Branch.class); - BranchJobProperty branchJobProperty = Mockito.mock(BranchJobProperty.class); - UserRemoteConfig userRemoteConfig = Mockito.mock(UserRemoteConfig.class); - List userRemoteConfigs = Collections.singletonList(userRemoteConfig); - Mockito.when(project.getProperty(BranchJobProperty.class)).thenReturn(branchJobProperty); - Mockito.when(branchJobProperty.getBranch()).thenReturn(branch); - Mockito.when(branch.getScm()).thenReturn(gitSCM); - Mockito.when(gitSCM.getUserRemoteConfigs()).thenReturn(userRemoteConfigs); - Mockito.when(userRemoteConfig.getUrl()).thenReturn(url); - return project; - } - - private MultiBranchProject mockMultiBranchProject(String url) { - WorkflowMultiBranchProject multiBranchProject = Mockito.mock(WorkflowMultiBranchProject.class); - GitHubSCMSource gitHubSCM = Mockito.mock(GitHubSCMSource.class); - ArrayList scmSources = new ArrayList<>(); - scmSources.add(gitHubSCM); - Mockito.when(multiBranchProject.getSCMSources()).thenReturn(scmSources); - Mockito.when(gitHubSCM.getRemote()).thenReturn(url); - return multiBranchProject; - } - - @After - public void tearDown() throws Exception { - closeable.close(); - gh = null; - GithubAuthenticationToken.clearCaches(); - } - - @Test - public void testCanReadAndBuildOneOfMyPrivateRepositories() throws IOException { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); - MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { - mockJenkins(mockedJenkins); - GHMyself me = mockGHMyselfAs(mockedGitHubBuilder, "Me"); - GHRepository repo = mockRepository("me/a-repo", false, true, true, true); // private; admin, push, and pull rights - mockReposFor(me, Collections.singletonList(repo)); // hook to my listing - String repoUrl = "https://github.com/me/a-repo.git"; - Project mockProject = mockProject(repoUrl); - MultiBranchProject mockMultiBranchProject = mockMultiBranchProject(repoUrl); - WorkflowJob mockWorkflowJob = mockWorkflowJob(repoUrl); - GithubRequireOrganizationMembershipACL workflowJobAcl = aclForWorkflowJob(mockWorkflowJob); - GithubRequireOrganizationMembershipACL multiBranchProjectAcl = aclForMultiBranchProject(mockMultiBranchProject); - GithubRequireOrganizationMembershipACL projectAcl = aclForProject(mockProject); - GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - - assertTrue(projectAcl.hasPermission2(authenticationToken, Item.READ)); - assertTrue(projectAcl.hasPermission2(authenticationToken, Item.DISCOVER)); - assertTrue(projectAcl.hasPermission2(authenticationToken, Item.BUILD)); - assertTrue(workflowJobAcl.hasPermission2(authenticationToken, Item.READ)); - assertTrue(workflowJobAcl.hasPermission2(authenticationToken, Item.DISCOVER)); - assertTrue(workflowJobAcl.hasPermission2(authenticationToken, Item.BUILD)); - assertTrue(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.READ)); - assertTrue(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.DISCOVER)); - assertTrue(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.BUILD)); - } - } - - @Test - public void testCanReadAndBuildAPublicRepository() throws IOException { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); - MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { - mockJenkins(mockedJenkins); - GHMyself me = mockGHMyselfAs(mockedGitHubBuilder, "Me"); - GHRepository repo = mockPublicRepository("node/node"); - String repoUrl = "https://github.com/node/node.git"; - Project mockProject = mockProject(repoUrl); - MultiBranchProject mockMultiBranchProject = mockMultiBranchProject(repoUrl); - WorkflowJob mockWorkflowJob = mockWorkflowJob(repoUrl); - GithubRequireOrganizationMembershipACL workflowJobAcl = aclForWorkflowJob(mockWorkflowJob); - GithubRequireOrganizationMembershipACL multiBranchProjectAcl = aclForMultiBranchProject(mockMultiBranchProject); - GithubRequireOrganizationMembershipACL projectAcl = aclForProject(mockProject); - GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - - assertTrue(projectAcl.hasPermission2(authenticationToken, Item.READ)); - assertTrue(projectAcl.hasPermission2(authenticationToken, Item.DISCOVER)); - assertTrue(projectAcl.hasPermission2(authenticationToken, Item.BUILD)); - assertTrue(workflowJobAcl.hasPermission2(authenticationToken, Item.READ)); - assertTrue(workflowJobAcl.hasPermission2(authenticationToken, Item.DISCOVER)); - assertTrue(workflowJobAcl.hasPermission2(authenticationToken, Item.BUILD)); - assertTrue(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.READ)); - assertTrue(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.DISCOVER)); - assertTrue(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.BUILD)); - } - } - - @Test - public void testCanReadAndBuildPrivateRepositoryIHavePullRightsOn() throws IOException { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); - MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { - mockJenkins(mockedJenkins); - GHMyself me = mockGHMyselfAs(mockedGitHubBuilder, "Me"); - // private repo I have pull rights to - GHRepository repo = mockRepository("some-org/a-private-repo", false, false, false, true); - mockReposFor(me, Collections.singletonList(repo)); - String repoUrl = "https://github.com/some-org/a-private-repo.git"; - Project mockProject = mockProject(repoUrl); - MultiBranchProject mockMultiBranchProject = mockMultiBranchProject(repoUrl); - WorkflowJob mockWorkflowJob = mockWorkflowJob(repoUrl); - GithubRequireOrganizationMembershipACL workflowJobAcl = aclForWorkflowJob(mockWorkflowJob); - GithubRequireOrganizationMembershipACL multiBranchProjectAcl = aclForMultiBranchProject(mockMultiBranchProject); - GithubRequireOrganizationMembershipACL projectAcl = aclForProject(mockProject); - - GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - - assertTrue(projectAcl.hasPermission2(authenticationToken, Item.READ)); - assertTrue(projectAcl.hasPermission2(authenticationToken, Item.DISCOVER)); - assertTrue(projectAcl.hasPermission2(authenticationToken, Item.BUILD)); - assertTrue(workflowJobAcl.hasPermission2(authenticationToken, Item.READ)); - assertTrue(workflowJobAcl.hasPermission2(authenticationToken, Item.DISCOVER)); - assertTrue(workflowJobAcl.hasPermission2(authenticationToken, Item.BUILD)); - assertTrue(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.READ)); - assertTrue(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.DISCOVER)); - assertTrue(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.BUILD)); - } - } - - @Test - public void testCanNotReadOrBuildRepositoryIDoNotCollaborateOn() throws IOException { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); - MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { - mockJenkins(mockedJenkins); - GHMyself me = mockGHMyselfAs(mockedGitHubBuilder, "Me"); - - String repoUrl = "https://github.com/some-org/another-private-repo.git"; - Project mockProject = mockProject(repoUrl); - MultiBranchProject mockMultiBranchProject = mockMultiBranchProject(repoUrl); - WorkflowJob mockWorkflowJob = mockWorkflowJob(repoUrl); - GithubRequireOrganizationMembershipACL workflowJobAcl = aclForWorkflowJob(mockWorkflowJob); - GithubRequireOrganizationMembershipACL multiBranchProjectAcl = aclForMultiBranchProject(mockMultiBranchProject); - GithubRequireOrganizationMembershipACL projectAcl = aclForProject(mockProject); - - GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - - assertFalse(projectAcl.hasPermission2(authenticationToken, Item.READ)); - assertFalse(projectAcl.hasPermission2(authenticationToken, Item.DISCOVER)); - assertFalse(projectAcl.hasPermission2(authenticationToken, Item.BUILD)); - assertFalse(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.READ)); - assertFalse(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.DISCOVER)); - assertFalse(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.BUILD)); - assertFalse(workflowJobAcl.hasPermission2(authenticationToken, Item.READ)); - assertFalse(workflowJobAcl.hasPermission2(authenticationToken, Item.DISCOVER)); - assertFalse(workflowJobAcl.hasPermission2(authenticationToken, Item.BUILD)); - } - } - - @Test - public void testNotGrantedBuildWhenNotUsingGitSCM() throws IOException { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); - MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { - mockJenkins(mockedJenkins); - mockGHMyselfAs(mockedGitHubBuilder, "Me"); - Project mockProject = Mockito.mock(Project.class); - Mockito.when(mockProject.getScm()).thenReturn(new NullSCM()); - - GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); - - GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - - assertFalse(acl.hasPermission2(authenticationToken, Item.READ)); - assertFalse(acl.hasPermission2(authenticationToken, Item.DISCOVER)); - } - } - - @Test - public void testNotGrantedBuildWhenRepositoryIsEmpty() throws IOException { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); - MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { - mockJenkins(mockedJenkins); - mockGHMyselfAs(mockedGitHubBuilder, "Me"); - Project mockProject = mockProject(null); - GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); - - GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - - assertFalse(acl.hasPermission2(authenticationToken, Item.READ)); - assertFalse(acl.hasPermission2(authenticationToken, Item.DISCOVER)); - } - } - - @Test - public void testNotGrantedReadWhenRepositoryUrlIsEmpty() throws IOException { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); - MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { - mockJenkins(mockedJenkins); - mockGHMyselfAs(mockedGitHubBuilder, "Me"); - Project mockProject = Mockito.mock(Project.class); - Mockito.when(mockProject.getScm()).thenReturn(new NullSCM()); - GitSCM gitSCM = Mockito.mock(GitSCM.class); - List userRemoteConfigs = Collections.emptyList(); - Mockito.when(mockProject.getScm()).thenReturn(gitSCM); - Mockito.when(gitSCM.getUserRemoteConfigs()).thenReturn(userRemoteConfigs); - - GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); - - GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - - assertFalse(acl.hasPermission2(authenticationToken, Item.READ)); - assertFalse(acl.hasPermission2(authenticationToken, Item.DISCOVER)); - } - } - - @Test - public void testGlobalReadAvailableDueToAuthenticatedUserReadPermission() throws IOException { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); - MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { - mockJenkins(mockedJenkins); - this.useRepositoryPermissions = false; - this.authenticatedUserReadPermission = true; - - mockGHMyselfAs(mockedGitHubBuilder, "Me"); - GithubRequireOrganizationMembershipACL acl = createACL(); - GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - - assertTrue(acl.hasPermission2(authenticationToken, Hudson.READ)); - } - } - - @Test - public void testWithoutUseRepositoryPermissionsSetCanReadDueToAuthenticatedUserReadPermission() throws IOException { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); - MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { - mockJenkins(mockedJenkins); - this.useRepositoryPermissions = false; - this.authenticatedUserReadPermission = true; - - mockGHMyselfAs(mockedGitHubBuilder, "Me"); - GithubRequireOrganizationMembershipACL acl = createACL(); - GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - - assertTrue(acl.hasPermission2(authenticationToken, Item.READ)); - } - } - - @Test - public void testWithoutUseRepositoryPermissionsSetCannotReadWithoutAuthenticatedUserReadPermission() throws IOException { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); - MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { - mockJenkins(mockedJenkins); - this.useRepositoryPermissions = false; - this.authenticatedUserReadPermission = false; - - mockGHMyselfAs(mockedGitHubBuilder, "Me"); - GithubRequireOrganizationMembershipACL acl = createACL(); - GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - - assertFalse(acl.hasPermission2(authenticationToken, Item.READ)); - } - } - - @Test - public void testUsersCannotCreateWithoutConfigurationEnabledPermission() throws IOException { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); - MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { - mockJenkins(mockedJenkins); - this.authenticatedUserCreateJobPermission = false; - - mockGHMyselfAs(mockedGitHubBuilder, "Me"); - GithubRequireOrganizationMembershipACL acl = createACL(); - GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - - assertFalse(acl.hasPermission2(authenticationToken, Item.CREATE)); - } - } - - @Test - public void testUsersCanCreateWithConfigurationEnabledPermission() throws IOException { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); - MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { - mockJenkins(mockedJenkins); - this.authenticatedUserCreateJobPermission = true; - - mockGHMyselfAs(mockedGitHubBuilder, "Me"); - GithubRequireOrganizationMembershipACL acl = createACL(); - GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - - assertTrue(acl.hasPermission2(authenticationToken, Item.CREATE)); - } - } - - @Test - public void testCanReadAProjectWithAuthenticatedUserReadPermission() throws IOException { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); - MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { - mockJenkins(mockedJenkins); - this.authenticatedUserReadPermission = true; - - String nullProjectName = null; - Project mockProject = mockProject(nullProjectName); - mockGHMyselfAs(mockedGitHubBuilder, "Me"); - GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); - GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - - // Gives the user rights to see the project - assertTrue(acl.hasPermission2(authenticationToken, Item.READ)); - assertTrue(acl.hasPermission2(authenticationToken, Item.DISCOVER)); - // but not to build, cancel, configure, view configuration, delete it - assertFalse(acl.hasPermission2(authenticationToken, Item.BUILD)); - assertFalse(acl.hasPermission2(authenticationToken, Item.CONFIGURE)); - assertFalse(acl.hasPermission2(authenticationToken, Item.DELETE)); - assertFalse(acl.hasPermission2(authenticationToken, Item.EXTENDED_READ)); - assertFalse(acl.hasPermission2(authenticationToken, Item.CANCEL)); - } - } - - @Test - public void testCannotReadAProjectWithoutAuthenticatedUserReadPermission() throws IOException { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); - MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { - mockJenkins(mockedJenkins); - this.authenticatedUserReadPermission = false; - - String nullProjectName = null; - Project mockProject = mockProject(nullProjectName); - mockGHMyselfAs(mockedGitHubBuilder, "Me"); - GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); - GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - - assertFalse(acl.hasPermission2(authenticationToken, Item.READ)); - assertFalse(acl.hasPermission2(authenticationToken, Item.DISCOVER)); - assertFalse(acl.hasPermission2(authenticationToken, Item.BUILD)); - assertFalse(acl.hasPermission2(authenticationToken, Item.CONFIGURE)); - assertFalse(acl.hasPermission2(authenticationToken, Item.DELETE)); - assertFalse(acl.hasPermission2(authenticationToken, Item.EXTENDED_READ)); - assertFalse(acl.hasPermission2(authenticationToken, Item.CANCEL)); - } - } - - @Test - public void testCannotReadRepositoryWithInvalidRepoUrl() throws IOException { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); - MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { - mockJenkins(mockedJenkins); - GHMyself me = mockGHMyselfAs(mockedGitHubBuilder, "Me"); - // private repo I have pull rights to - GHRepository repo = mockRepository("some-org/a-repo", false, false, false, true); - mockReposFor(me, Collections.singletonList(repo)); - String invalidRepoUrl = "git@github.com//some-org/a-repo.git"; - Project mockProject = mockProject(invalidRepoUrl); - GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); - - GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - - assertFalse(acl.hasPermission2(authenticationToken, Item.READ)); - } - } - - @Test - public void testAgentUserCanCreateConnectAndConfigureAgents() { - GithubAuthenticationToken authenticationToken = Mockito.mock(GithubAuthenticationToken.class); - Mockito.when(authenticationToken.isAuthenticated()).thenReturn(true); - Mockito.when(authenticationToken.getName()).thenReturn("agent"); - GithubRequireOrganizationMembershipACL acl = createACL(); - - assertTrue(acl.hasPermission2(authenticationToken, Computer.CREATE)); - assertTrue(acl.hasPermission2(authenticationToken, Computer.CONFIGURE)); - assertTrue(acl.hasPermission2(authenticationToken, Computer.CONNECT)); - } - - @Test - public void testAuthenticatedCanNotCreateConnectAndConfigureAgents() { - GithubAuthenticationToken authenticationToken = Mockito.mock(GithubAuthenticationToken.class); - Mockito.when(authenticationToken.isAuthenticated()).thenReturn(true); - Mockito.when(authenticationToken.getName()).thenReturn("authenticated"); - GithubRequireOrganizationMembershipACL acl = createACL(); - - assertFalse(acl.hasPermission2(authenticationToken, Computer.CREATE)); - assertFalse(acl.hasPermission2(authenticationToken, Computer.CONFIGURE)); - assertFalse(acl.hasPermission2(authenticationToken, Computer.CONNECT)); - } - - @Test - public void testAnonymousCanViewJobStatusWhenGranted() { - this.allowAnonymousJobStatusPermission = true; - - Project mockProject = mockProject("https://github.com/some-org/a-public-repo.git"); - GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); - - assertTrue(acl.hasPermission2(ANONYMOUS_USER, VIEW_JOBSTATUS_PERMISSION)); - } - - @Test - public void testAnonymousCannotViewJobStatusWhenNotGranted() { - this.allowAnonymousJobStatusPermission = false; - - Project mockProject = mockProject("https://github.com/some-org/a-public-repo.git"); - GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); - - assertFalse(acl.hasPermission2(ANONYMOUS_USER, VIEW_JOBSTATUS_PERMISSION)); - } - - @Test - public void testAnonymousCanReachWebhookWhenGranted() { - try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); - MockedStatic mockedStapler = Mockito.mockStatic(Stapler.class)) { - mockJenkins(mockedJenkins); - this.allowAnonymousWebhookPermission = true; - - StaplerRequest2 currentRequest = Mockito.mock(StaplerRequest2.class); - mockedStapler.when(Stapler::getCurrentRequest2).thenReturn(currentRequest); - Mockito.when(currentRequest.getOriginalRequestURI()).thenReturn("https://www.jenkins.org/github-webhook/"); - - GithubRequireOrganizationMembershipACL acl = createACL(); - - assertTrue(acl.hasPermission2(ANONYMOUS_USER, Item.READ)); - } - } - - @Test - public void testAnonymousCannotReachWebhookIfNotGranted() { - try (MockedStatic mockedStapler = Mockito.mockStatic(Stapler.class)) { - this.allowAnonymousWebhookPermission = false; - - StaplerRequest2 currentRequest = Mockito.mock(StaplerRequest2.class); - mockedStapler.when(Stapler::getCurrentRequest2).thenReturn(currentRequest); - Mockito.when(currentRequest.getOriginalRequestURI()).thenReturn("https://www.jenkins.org/github-webhook/"); - - GithubRequireOrganizationMembershipACL acl = createACL(); - - assertFalse(acl.hasPermission2(ANONYMOUS_USER, Item.READ)); - } - } - - @Test - public void testAnonymousCanReadAndDiscoverWhenGranted() { - this.allowAnonymousReadPermission = true; - - Project mockProject = mockProject("https://github.com/some-org/a-public-repo.git"); - GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); - - assertTrue(acl.hasPermission2(ANONYMOUS_USER, Item.READ)); - assertTrue(acl.hasPermission2(ANONYMOUS_USER, Item.DISCOVER)); - } - - @Test - public void testAnonymousCantReadAndDiscoverWhenNotGranted() { - this.allowAnonymousReadPermission = false; - - Project mockProject = mockProject("https://github.com/some-org/a-public-repo.git"); - GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +class GithubRequireOrganizationMembershipACLTest { + + private GitHub gh; + + @Mock + private GithubSecurityRealm securityRealm; + + private boolean allowAnonymousReadPermission; + private boolean allowAnonymousJobStatusPermission; + private boolean useRepositoryPermissions; + private boolean authenticatedUserReadPermission; + private boolean authenticatedUserCreateJobPermission; + private boolean allowAnonymousWebhookPermission; + private boolean allowAnonymousCCTrayPermission; + + @BeforeEach + void setUp() { + // default to: use repository permissions; don't allow anonymous read/view status; don't allow authenticated read/create + allowAnonymousReadPermission = false; + allowAnonymousJobStatusPermission = false; + useRepositoryPermissions = true; + authenticatedUserReadPermission = false; + authenticatedUserCreateJobPermission = false; + allowAnonymousWebhookPermission = false; + allowAnonymousCCTrayPermission = false; + + Mockito.when(securityRealm.getOauthScopes()).thenReturn("read:org,repo"); + Mockito.when(securityRealm.hasScope("read:org")).thenReturn(true); + Mockito.when(securityRealm.hasScope("repo")).thenReturn(true); + } + + private void mockJenkins(MockedStatic mockedJenkins) { + Jenkins jenkins = Mockito.mock(Jenkins.class); + mockedJenkins.when(Jenkins::get).thenReturn(jenkins); + Mockito.when(jenkins.getSecurityRealm()).thenReturn(securityRealm); + Mockito.when(jenkins.getRootUrl()).thenReturn("https://www.jenkins.org/"); + } + + private static final Permission VIEW_JOBSTATUS_PERMISSION = new Permission(Item.PERMISSIONS, + "ViewStatus", + Messages._Item_READ_description(), + Permission.READ, + PermissionScope.ITEM); + private static final Authentication ANONYMOUS_USER = new AnonymousAuthenticationToken("anonymous", + "anonymous", + List.of(new SimpleGrantedAuthority("anonymous"))); + + private GithubRequireOrganizationMembershipACL createACL() { + GithubRequireOrganizationMembershipACL acl = new GithubRequireOrganizationMembershipACL( + "admin", + "myOrg", + authenticatedUserReadPermission, + useRepositoryPermissions, + authenticatedUserCreateJobPermission, + allowAnonymousWebhookPermission, + allowAnonymousCCTrayPermission, + allowAnonymousReadPermission, + allowAnonymousJobStatusPermission); + acl.setAgentUserName("agent"); + return acl; + } + + private GithubRequireOrganizationMembershipACL aclForProject(Project project) { + return createACL().cloneForProject(project); + } + + private GithubRequireOrganizationMembershipACL aclForMultiBranchProject(MultiBranchProject multiBranchProject) { + return createACL().cloneForProject(multiBranchProject); + } + + private GithubRequireOrganizationMembershipACL aclForWorkflowJob(WorkflowJob workflowJob) { + return createACL().cloneForProject(workflowJob); + } + + private GHMyself mockGHMyselfAs(MockedStatic mockedGitHubBuilder, String username) throws IOException { + gh = Mockito.mock(GitHub.class); + GitHubBuilder builder = Mockito.mock(GitHubBuilder.class); + mockedGitHubBuilder.when(GitHubBuilder::fromEnvironment).thenReturn(builder); + Mockito.when(builder.withEndpoint("https://api.github.com")).thenReturn(builder); + Mockito.when(builder.withOAuthToken("accessToken")).thenReturn(builder); + Mockito.when(builder.withRateLimitHandler(RateLimitHandler.FAIL)).thenReturn(builder); + Mockito.when(builder.withConnector(Mockito.any(OkHttpGitHubConnector.class))).thenReturn(builder); + Mockito.when(builder.build()).thenReturn(gh); + GHMyself me = Mockito.mock(GHMyself.class); + Mockito.when(gh.getMyself()).thenReturn(me); + Mockito.when(me.getLogin()).thenReturn(username); + mockReposFor(me, Collections.emptyList()); + return me; + } + + // TODO: Add ability to set list of orgs user belongs to to check whitelisting! + + private static void mockReposFor(GHPerson person, List repositories) throws IOException { + PagedIterable pagedRepositories = Mockito.mock(PagedIterable.class); + Mockito.when(person.listRepositories(100)).thenReturn(pagedRepositories); + Mockito.when(pagedRepositories.toList()).thenReturn(repositories); + } + + private GHRepository mockRepository(String repositoryName, boolean isPublic, boolean admin, boolean push, boolean pull) throws IOException { + GHRepository ghRepository = Mockito.mock(GHRepository.class); + Mockito.when(gh.getRepository(repositoryName)).thenReturn(ghRepository); + Mockito.when(ghRepository.isPrivate()).thenReturn(!isPublic); + Mockito.when(ghRepository.hasAdminAccess()).thenReturn(admin); + Mockito.when(ghRepository.hasPushAccess()).thenReturn(push); + Mockito.when(ghRepository.hasPullAccess()).thenReturn(pull); + Mockito.when(ghRepository.getFullName()).thenReturn(repositoryName); + return ghRepository; + } + + private GHRepository mockPublicRepository(String repositoryName) throws IOException { + return mockRepository(repositoryName, true, false, false, false); + } + + private static Project mockProject(String url) { + Project project = Mockito.mock(Project.class); + GitSCM gitSCM = Mockito.mock(GitSCM.class); + UserRemoteConfig userRemoteConfig = Mockito.mock(UserRemoteConfig.class); + List userRemoteConfigs = Collections.singletonList(userRemoteConfig); + Mockito.when(project.getScm()).thenReturn(gitSCM); + Mockito.when(gitSCM.getUserRemoteConfigs()).thenReturn(userRemoteConfigs); + Mockito.when(userRemoteConfig.getUrl()).thenReturn(url); + return project; + } + + private static WorkflowJob mockWorkflowJob(String url) { + WorkflowJob project = Mockito.mock(WorkflowJob.class); + GitSCM gitSCM = Mockito.mock(GitSCM.class); + Branch branch = Mockito.mock(Branch.class); + BranchJobProperty branchJobProperty = Mockito.mock(BranchJobProperty.class); + UserRemoteConfig userRemoteConfig = Mockito.mock(UserRemoteConfig.class); + List userRemoteConfigs = Collections.singletonList(userRemoteConfig); + Mockito.when(project.getProperty(BranchJobProperty.class)).thenReturn(branchJobProperty); + Mockito.when(branchJobProperty.getBranch()).thenReturn(branch); + Mockito.when(branch.getScm()).thenReturn(gitSCM); + Mockito.when(gitSCM.getUserRemoteConfigs()).thenReturn(userRemoteConfigs); + Mockito.when(userRemoteConfig.getUrl()).thenReturn(url); + return project; + } + + private static MultiBranchProject mockMultiBranchProject(String url) { + WorkflowMultiBranchProject multiBranchProject = Mockito.mock(WorkflowMultiBranchProject.class); + GitHubSCMSource gitHubSCM = Mockito.mock(GitHubSCMSource.class); + ArrayList scmSources = new ArrayList<>(); + scmSources.add(gitHubSCM); + Mockito.when(multiBranchProject.getSCMSources()).thenReturn(scmSources); + Mockito.when(gitHubSCM.getRemote()).thenReturn(url); + return multiBranchProject; + } + + @AfterEach + void tearDown() { + gh = null; + GithubAuthenticationToken.clearCaches(); + } + + @Test + void testCanReadAndBuildOneOfMyPrivateRepositories() throws IOException { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); + MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { + mockJenkins(mockedJenkins); + GHMyself me = mockGHMyselfAs(mockedGitHubBuilder, "Me"); + GHRepository repo = mockRepository("me/a-repo", false, true, true, true); // private; admin, push, and pull rights + mockReposFor(me, Collections.singletonList(repo)); // hook to my listing + String repoUrl = "https://github.com/me/a-repo.git"; + Project mockProject = mockProject(repoUrl); + MultiBranchProject mockMultiBranchProject = mockMultiBranchProject(repoUrl); + WorkflowJob mockWorkflowJob = mockWorkflowJob(repoUrl); + GithubRequireOrganizationMembershipACL workflowJobAcl = aclForWorkflowJob(mockWorkflowJob); + GithubRequireOrganizationMembershipACL multiBranchProjectAcl = aclForMultiBranchProject(mockMultiBranchProject); + GithubRequireOrganizationMembershipACL projectAcl = aclForProject(mockProject); + GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); + + assertTrue(projectAcl.hasPermission2(authenticationToken, Item.READ)); + assertTrue(projectAcl.hasPermission2(authenticationToken, Item.DISCOVER)); + assertTrue(projectAcl.hasPermission2(authenticationToken, Item.BUILD)); + assertTrue(workflowJobAcl.hasPermission2(authenticationToken, Item.READ)); + assertTrue(workflowJobAcl.hasPermission2(authenticationToken, Item.DISCOVER)); + assertTrue(workflowJobAcl.hasPermission2(authenticationToken, Item.BUILD)); + assertTrue(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.READ)); + assertTrue(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.DISCOVER)); + assertTrue(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.BUILD)); + } + } + + @Test + void testCanReadAndBuildAPublicRepository() throws IOException { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); + MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { + mockJenkins(mockedJenkins); + GHMyself me = mockGHMyselfAs(mockedGitHubBuilder, "Me"); + GHRepository repo = mockPublicRepository("node/node"); + String repoUrl = "https://github.com/node/node.git"; + Project mockProject = mockProject(repoUrl); + MultiBranchProject mockMultiBranchProject = mockMultiBranchProject(repoUrl); + WorkflowJob mockWorkflowJob = mockWorkflowJob(repoUrl); + GithubRequireOrganizationMembershipACL workflowJobAcl = aclForWorkflowJob(mockWorkflowJob); + GithubRequireOrganizationMembershipACL multiBranchProjectAcl = aclForMultiBranchProject(mockMultiBranchProject); + GithubRequireOrganizationMembershipACL projectAcl = aclForProject(mockProject); + GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); + + assertTrue(projectAcl.hasPermission2(authenticationToken, Item.READ)); + assertTrue(projectAcl.hasPermission2(authenticationToken, Item.DISCOVER)); + assertTrue(projectAcl.hasPermission2(authenticationToken, Item.BUILD)); + assertTrue(workflowJobAcl.hasPermission2(authenticationToken, Item.READ)); + assertTrue(workflowJobAcl.hasPermission2(authenticationToken, Item.DISCOVER)); + assertTrue(workflowJobAcl.hasPermission2(authenticationToken, Item.BUILD)); + assertTrue(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.READ)); + assertTrue(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.DISCOVER)); + assertTrue(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.BUILD)); + } + } + + @Test + void testCanReadAndBuildPrivateRepositoryIHavePullRightsOn() throws IOException { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); + MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { + mockJenkins(mockedJenkins); + GHMyself me = mockGHMyselfAs(mockedGitHubBuilder, "Me"); + // private repo I have pull rights to + GHRepository repo = mockRepository("some-org/a-private-repo", false, false, false, true); + mockReposFor(me, Collections.singletonList(repo)); + String repoUrl = "https://github.com/some-org/a-private-repo.git"; + Project mockProject = mockProject(repoUrl); + MultiBranchProject mockMultiBranchProject = mockMultiBranchProject(repoUrl); + WorkflowJob mockWorkflowJob = mockWorkflowJob(repoUrl); + GithubRequireOrganizationMembershipACL workflowJobAcl = aclForWorkflowJob(mockWorkflowJob); + GithubRequireOrganizationMembershipACL multiBranchProjectAcl = aclForMultiBranchProject(mockMultiBranchProject); + GithubRequireOrganizationMembershipACL projectAcl = aclForProject(mockProject); + + GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); + + assertTrue(projectAcl.hasPermission2(authenticationToken, Item.READ)); + assertTrue(projectAcl.hasPermission2(authenticationToken, Item.DISCOVER)); + assertTrue(projectAcl.hasPermission2(authenticationToken, Item.BUILD)); + assertTrue(workflowJobAcl.hasPermission2(authenticationToken, Item.READ)); + assertTrue(workflowJobAcl.hasPermission2(authenticationToken, Item.DISCOVER)); + assertTrue(workflowJobAcl.hasPermission2(authenticationToken, Item.BUILD)); + assertTrue(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.READ)); + assertTrue(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.DISCOVER)); + assertTrue(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.BUILD)); + } + } + + @Test + void testCanNotReadOrBuildRepositoryIDoNotCollaborateOn() throws IOException { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); + MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { + mockJenkins(mockedJenkins); + GHMyself me = mockGHMyselfAs(mockedGitHubBuilder, "Me"); + + String repoUrl = "https://github.com/some-org/another-private-repo.git"; + Project mockProject = mockProject(repoUrl); + MultiBranchProject mockMultiBranchProject = mockMultiBranchProject(repoUrl); + WorkflowJob mockWorkflowJob = mockWorkflowJob(repoUrl); + GithubRequireOrganizationMembershipACL workflowJobAcl = aclForWorkflowJob(mockWorkflowJob); + GithubRequireOrganizationMembershipACL multiBranchProjectAcl = aclForMultiBranchProject(mockMultiBranchProject); + GithubRequireOrganizationMembershipACL projectAcl = aclForProject(mockProject); + + GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); + + assertFalse(projectAcl.hasPermission2(authenticationToken, Item.READ)); + assertFalse(projectAcl.hasPermission2(authenticationToken, Item.DISCOVER)); + assertFalse(projectAcl.hasPermission2(authenticationToken, Item.BUILD)); + assertFalse(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.READ)); + assertFalse(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.DISCOVER)); + assertFalse(multiBranchProjectAcl.hasPermission2(authenticationToken, Item.BUILD)); + assertFalse(workflowJobAcl.hasPermission2(authenticationToken, Item.READ)); + assertFalse(workflowJobAcl.hasPermission2(authenticationToken, Item.DISCOVER)); + assertFalse(workflowJobAcl.hasPermission2(authenticationToken, Item.BUILD)); + } + } + + @Test + void testNotGrantedBuildWhenNotUsingGitSCM() throws IOException { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); + MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { + mockJenkins(mockedJenkins); + mockGHMyselfAs(mockedGitHubBuilder, "Me"); + Project mockProject = Mockito.mock(Project.class); + Mockito.when(mockProject.getScm()).thenReturn(new NullSCM()); + + GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); + + GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); + + assertFalse(acl.hasPermission2(authenticationToken, Item.READ)); + assertFalse(acl.hasPermission2(authenticationToken, Item.DISCOVER)); + } + } + + @Test + void testNotGrantedBuildWhenRepositoryIsEmpty() throws IOException { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); + MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { + mockJenkins(mockedJenkins); + mockGHMyselfAs(mockedGitHubBuilder, "Me"); + Project mockProject = mockProject(null); + GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); + + GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); + + assertFalse(acl.hasPermission2(authenticationToken, Item.READ)); + assertFalse(acl.hasPermission2(authenticationToken, Item.DISCOVER)); + } + } + + @Test + void testNotGrantedReadWhenRepositoryUrlIsEmpty() throws IOException { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); + MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { + mockJenkins(mockedJenkins); + mockGHMyselfAs(mockedGitHubBuilder, "Me"); + Project mockProject = Mockito.mock(Project.class); + Mockito.when(mockProject.getScm()).thenReturn(new NullSCM()); + GitSCM gitSCM = Mockito.mock(GitSCM.class); + List userRemoteConfigs = Collections.emptyList(); + Mockito.when(mockProject.getScm()).thenReturn(gitSCM); + Mockito.when(gitSCM.getUserRemoteConfigs()).thenReturn(userRemoteConfigs); + + GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); + + GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); + + assertFalse(acl.hasPermission2(authenticationToken, Item.READ)); + assertFalse(acl.hasPermission2(authenticationToken, Item.DISCOVER)); + } + } + + @Test + void testGlobalReadAvailableDueToAuthenticatedUserReadPermission() throws IOException { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); + MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { + mockJenkins(mockedJenkins); + this.useRepositoryPermissions = false; + this.authenticatedUserReadPermission = true; + + mockGHMyselfAs(mockedGitHubBuilder, "Me"); + GithubRequireOrganizationMembershipACL acl = createACL(); + GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); + + assertTrue(acl.hasPermission2(authenticationToken, Hudson.READ)); + } + } + + @Test + void testWithoutUseRepositoryPermissionsSetCanReadDueToAuthenticatedUserReadPermission() throws IOException { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); + MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { + mockJenkins(mockedJenkins); + this.useRepositoryPermissions = false; + this.authenticatedUserReadPermission = true; + + mockGHMyselfAs(mockedGitHubBuilder, "Me"); + GithubRequireOrganizationMembershipACL acl = createACL(); + GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); + + assertTrue(acl.hasPermission2(authenticationToken, Item.READ)); + } + } + + @Test + void testWithoutUseRepositoryPermissionsSetCannotReadWithoutAuthenticatedUserReadPermission() throws IOException { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); + MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { + mockJenkins(mockedJenkins); + this.useRepositoryPermissions = false; + this.authenticatedUserReadPermission = false; + + mockGHMyselfAs(mockedGitHubBuilder, "Me"); + GithubRequireOrganizationMembershipACL acl = createACL(); + GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); + + assertFalse(acl.hasPermission2(authenticationToken, Item.READ)); + } + } + + @Test + void testUsersCannotCreateWithoutConfigurationEnabledPermission() throws IOException { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); + MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { + mockJenkins(mockedJenkins); + this.authenticatedUserCreateJobPermission = false; + + mockGHMyselfAs(mockedGitHubBuilder, "Me"); + GithubRequireOrganizationMembershipACL acl = createACL(); + GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); + + assertFalse(acl.hasPermission2(authenticationToken, Item.CREATE)); + } + } + + @Test + void testUsersCanCreateWithConfigurationEnabledPermission() throws IOException { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); + MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { + mockJenkins(mockedJenkins); + this.authenticatedUserCreateJobPermission = true; + + mockGHMyselfAs(mockedGitHubBuilder, "Me"); + GithubRequireOrganizationMembershipACL acl = createACL(); + GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); + + assertTrue(acl.hasPermission2(authenticationToken, Item.CREATE)); + } + } + + @Test + void testCanReadAProjectWithAuthenticatedUserReadPermission() throws IOException { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); + MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { + mockJenkins(mockedJenkins); + this.authenticatedUserReadPermission = true; + + String nullProjectName = null; + Project mockProject = mockProject(nullProjectName); + mockGHMyselfAs(mockedGitHubBuilder, "Me"); + GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); + GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); + + // Gives the user rights to see the project + assertTrue(acl.hasPermission2(authenticationToken, Item.READ)); + assertTrue(acl.hasPermission2(authenticationToken, Item.DISCOVER)); + // but not to build, cancel, configure, view configuration, delete it + assertFalse(acl.hasPermission2(authenticationToken, Item.BUILD)); + assertFalse(acl.hasPermission2(authenticationToken, Item.CONFIGURE)); + assertFalse(acl.hasPermission2(authenticationToken, Item.DELETE)); + assertFalse(acl.hasPermission2(authenticationToken, Item.EXTENDED_READ)); + assertFalse(acl.hasPermission2(authenticationToken, Item.CANCEL)); + } + } + + @Test + void testCannotReadAProjectWithoutAuthenticatedUserReadPermission() throws IOException { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); + MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { + mockJenkins(mockedJenkins); + this.authenticatedUserReadPermission = false; + + String nullProjectName = null; + Project mockProject = mockProject(nullProjectName); + mockGHMyselfAs(mockedGitHubBuilder, "Me"); + GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); + GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); + + assertFalse(acl.hasPermission2(authenticationToken, Item.READ)); + assertFalse(acl.hasPermission2(authenticationToken, Item.DISCOVER)); + assertFalse(acl.hasPermission2(authenticationToken, Item.BUILD)); + assertFalse(acl.hasPermission2(authenticationToken, Item.CONFIGURE)); + assertFalse(acl.hasPermission2(authenticationToken, Item.DELETE)); + assertFalse(acl.hasPermission2(authenticationToken, Item.EXTENDED_READ)); + assertFalse(acl.hasPermission2(authenticationToken, Item.CANCEL)); + } + } + + @Test + void testCannotReadRepositoryWithInvalidRepoUrl() throws IOException { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); + MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { + mockJenkins(mockedJenkins); + GHMyself me = mockGHMyselfAs(mockedGitHubBuilder, "Me"); + // private repo I have pull rights to + GHRepository repo = mockRepository("some-org/a-repo", false, false, false, true); + mockReposFor(me, Collections.singletonList(repo)); + String invalidRepoUrl = "git@github.com//some-org/a-repo.git"; + Project mockProject = mockProject(invalidRepoUrl); + GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); + + GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); + + assertFalse(acl.hasPermission2(authenticationToken, Item.READ)); + } + } + + @Test + void testAgentUserCanCreateConnectAndConfigureAgents() { + GithubAuthenticationToken authenticationToken = Mockito.mock(GithubAuthenticationToken.class); + Mockito.when(authenticationToken.isAuthenticated()).thenReturn(true); + Mockito.when(authenticationToken.getName()).thenReturn("agent"); + GithubRequireOrganizationMembershipACL acl = createACL(); + + assertTrue(acl.hasPermission2(authenticationToken, Computer.CREATE)); + assertTrue(acl.hasPermission2(authenticationToken, Computer.CONFIGURE)); + assertTrue(acl.hasPermission2(authenticationToken, Computer.CONNECT)); + } + + @Test + void testAuthenticatedCanNotCreateConnectAndConfigureAgents() { + GithubAuthenticationToken authenticationToken = Mockito.mock(GithubAuthenticationToken.class); + Mockito.when(authenticationToken.isAuthenticated()).thenReturn(true); + Mockito.when(authenticationToken.getName()).thenReturn("authenticated"); + GithubRequireOrganizationMembershipACL acl = createACL(); + + assertFalse(acl.hasPermission2(authenticationToken, Computer.CREATE)); + assertFalse(acl.hasPermission2(authenticationToken, Computer.CONFIGURE)); + assertFalse(acl.hasPermission2(authenticationToken, Computer.CONNECT)); + } + + @Test + void testAnonymousCanViewJobStatusWhenGranted() { + this.allowAnonymousJobStatusPermission = true; + + Project mockProject = mockProject("https://github.com/some-org/a-public-repo.git"); + GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); + + assertTrue(acl.hasPermission2(ANONYMOUS_USER, VIEW_JOBSTATUS_PERMISSION)); + } + + @Test + void testAnonymousCannotViewJobStatusWhenNotGranted() { + this.allowAnonymousJobStatusPermission = false; + + Project mockProject = mockProject("https://github.com/some-org/a-public-repo.git"); + GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); + + assertFalse(acl.hasPermission2(ANONYMOUS_USER, VIEW_JOBSTATUS_PERMISSION)); + } + + @Test + void testAnonymousCanReachWebhookWhenGranted() { + try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); + MockedStatic mockedStapler = Mockito.mockStatic(Stapler.class)) { + mockJenkins(mockedJenkins); + this.allowAnonymousWebhookPermission = true; + + StaplerRequest2 currentRequest = Mockito.mock(StaplerRequest2.class); + mockedStapler.when(Stapler::getCurrentRequest2).thenReturn(currentRequest); + Mockito.when(currentRequest.getOriginalRequestURI()).thenReturn("https://www.jenkins.org/github-webhook/"); + + GithubRequireOrganizationMembershipACL acl = createACL(); + + assertTrue(acl.hasPermission2(ANONYMOUS_USER, Item.READ)); + } + } + + @Test + void testAnonymousCannotReachWebhookIfNotGranted() { + try (MockedStatic mockedStapler = Mockito.mockStatic(Stapler.class)) { + this.allowAnonymousWebhookPermission = false; + + StaplerRequest2 currentRequest = Mockito.mock(StaplerRequest2.class); + mockedStapler.when(Stapler::getCurrentRequest2).thenReturn(currentRequest); + Mockito.when(currentRequest.getOriginalRequestURI()).thenReturn("https://www.jenkins.org/github-webhook/"); + + GithubRequireOrganizationMembershipACL acl = createACL(); + + assertFalse(acl.hasPermission2(ANONYMOUS_USER, Item.READ)); + } + } + + @Test + void testAnonymousCanReadAndDiscoverWhenGranted() { + this.allowAnonymousReadPermission = true; + + Project mockProject = mockProject("https://github.com/some-org/a-public-repo.git"); + GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); + + assertTrue(acl.hasPermission2(ANONYMOUS_USER, Item.READ)); + assertTrue(acl.hasPermission2(ANONYMOUS_USER, Item.DISCOVER)); + } + + @Test + void testAnonymousCantReadAndDiscoverWhenNotGranted() { + this.allowAnonymousReadPermission = false; - assertFalse(acl.hasPermission2(ANONYMOUS_USER, Item.READ)); - assertFalse(acl.hasPermission2(ANONYMOUS_USER, Item.DISCOVER)); - } + Project mockProject = mockProject("https://github.com/some-org/a-public-repo.git"); + GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); - @Test - public void testAnonymousCanReachCCTrayWhenGranted() { - try (MockedStatic mockedStapler = Mockito.mockStatic(Stapler.class)) { - this.allowAnonymousCCTrayPermission = true; + assertFalse(acl.hasPermission2(ANONYMOUS_USER, Item.READ)); + assertFalse(acl.hasPermission2(ANONYMOUS_USER, Item.DISCOVER)); + } - StaplerRequest2 currentRequest = Mockito.mock(StaplerRequest2.class); - mockedStapler.when(Stapler::getCurrentRequest2).thenReturn(currentRequest); - Mockito.when(currentRequest.getOriginalRequestURI()).thenReturn("https://www.jenkins.org/cc.xml"); + @Test + void testAnonymousCanReachCCTrayWhenGranted() { + try (MockedStatic mockedStapler = Mockito.mockStatic(Stapler.class)) { + this.allowAnonymousCCTrayPermission = true; - GithubRequireOrganizationMembershipACL acl = createACL(); + StaplerRequest2 currentRequest = Mockito.mock(StaplerRequest2.class); + mockedStapler.when(Stapler::getCurrentRequest2).thenReturn(currentRequest); + Mockito.when(currentRequest.getOriginalRequestURI()).thenReturn("https://www.jenkins.org/cc.xml"); - assertTrue(acl.hasPermission2(ANONYMOUS_USER, Item.READ)); - } - } + GithubRequireOrganizationMembershipACL acl = createACL(); - @Test - public void testAnonymousCannotReachCCTrayIfNotGranted() { - try (MockedStatic mockedStapler = Mockito.mockStatic(Stapler.class)) { - this.allowAnonymousCCTrayPermission = false; + assertTrue(acl.hasPermission2(ANONYMOUS_USER, Item.READ)); + } + } - StaplerRequest2 currentRequest = Mockito.mock(StaplerRequest2.class); - mockedStapler.when(Stapler::getCurrentRequest2).thenReturn(currentRequest); - Mockito.when(currentRequest.getOriginalRequestURI()).thenReturn("https://www.jenkins.org/cc.xml"); + @Test + void testAnonymousCannotReachCCTrayIfNotGranted() { + try (MockedStatic mockedStapler = Mockito.mockStatic(Stapler.class)) { + this.allowAnonymousCCTrayPermission = false; - GithubRequireOrganizationMembershipACL acl = createACL(); + StaplerRequest2 currentRequest = Mockito.mock(StaplerRequest2.class); + mockedStapler.when(Stapler::getCurrentRequest2).thenReturn(currentRequest); + Mockito.when(currentRequest.getOriginalRequestURI()).thenReturn("https://www.jenkins.org/cc.xml"); - assertFalse(acl.hasPermission2(ANONYMOUS_USER, Item.READ)); - } - } + GithubRequireOrganizationMembershipACL acl = createACL(); + assertFalse(acl.hasPermission2(ANONYMOUS_USER, Item.READ)); + } + } } diff --git a/src/test/java/org/jenkinsci/plugins/GithubSecretStorageTest.java b/src/test/java/org/jenkinsci/plugins/GithubSecretStorageTest.java index 358f9d8d..3a0c798d 100644 --- a/src/test/java/org/jenkinsci/plugins/GithubSecretStorageTest.java +++ b/src/test/java/org/jenkinsci/plugins/GithubSecretStorageTest.java @@ -24,71 +24,70 @@ package org.jenkinsci.plugins; import hudson.model.User; -import org.junit.Assert; -import org.junit.Rule; -import org.junit.Test; -import org.jvnet.hudson.test.JenkinsSessionRule; +import org.junit.jupiter.api.Test; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.junit.jupiter.WithJenkins; -public class GithubSecretStorageTest { +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; - @Rule - public JenkinsSessionRule sessions = new JenkinsSessionRule(); +@WithJenkins +class GithubSecretStorageTest { - @Test - public void correctBehavior() throws Throwable { - sessions.then(j -> { - User.getById("alice", true); - User.getById("bob", true); + @Test + void correctBehavior(JenkinsRule j) { + User.getById("alice", true); + User.getById("bob", true); - String secret = "$3cR3t"; + String secret = "$3cR3t"; - Assert.assertFalse(GithubSecretStorage.contains(retrieveUser())); - Assert.assertNull(GithubSecretStorage.retrieve(retrieveUser())); + assertFalse(GithubSecretStorage.contains(retrieveUser())); + assertNull(GithubSecretStorage.retrieve(retrieveUser())); - Assert.assertFalse(GithubSecretStorage.contains(retrieveOtherUser())); + assertFalse(GithubSecretStorage.contains(retrieveOtherUser())); - GithubSecretStorage.put(retrieveUser(), secret); + GithubSecretStorage.put(retrieveUser(), secret); - Assert.assertTrue(GithubSecretStorage.contains(retrieveUser())); - Assert.assertFalse(GithubSecretStorage.contains(retrieveOtherUser())); + assertTrue(GithubSecretStorage.contains(retrieveUser())); + assertFalse(GithubSecretStorage.contains(retrieveOtherUser())); - Assert.assertEquals(secret, GithubSecretStorage.retrieve(retrieveUser())); - }); - } + assertEquals(secret, GithubSecretStorage.retrieve(retrieveUser())); + } - private User retrieveUser() { - return User.getById("alice", false); - } + private static User retrieveUser() { + return User.getById("alice", false); + } - private User retrieveOtherUser() { - return User.getById("bob", false); - } + private static User retrieveOtherUser() { + return User.getById("bob", false); + } - @Test - public void correctBehaviorEvenAfterRestart() throws Throwable { - final String secret = "$3cR3t"; + @Test + void correctBehaviorEvenAfterRestart(JenkinsRule j) throws Throwable { + final String secret = "$3cR3t"; - sessions.then(j -> { - User.getById("alice", true).save(); - User.getById("bob", true).save(); + User.getById("alice", true).save(); + User.getById("bob", true).save(); - Assert.assertFalse(GithubSecretStorage.contains(retrieveUser())); - Assert.assertNull(GithubSecretStorage.retrieve(retrieveUser())); + assertFalse(GithubSecretStorage.contains(retrieveUser())); + assertNull(GithubSecretStorage.retrieve(retrieveUser())); - Assert.assertFalse(GithubSecretStorage.contains(retrieveOtherUser())); + assertFalse(GithubSecretStorage.contains(retrieveOtherUser())); - GithubSecretStorage.put(retrieveUser(), secret); + GithubSecretStorage.put(retrieveUser(), secret); - Assert.assertTrue(GithubSecretStorage.contains(retrieveUser())); - Assert.assertFalse(GithubSecretStorage.contains(retrieveOtherUser())); + assertTrue(GithubSecretStorage.contains(retrieveUser())); + assertFalse(GithubSecretStorage.contains(retrieveOtherUser())); - Assert.assertEquals(secret, GithubSecretStorage.retrieve(retrieveUser())); - }); - sessions.then(j -> { - Assert.assertTrue(GithubSecretStorage.contains(retrieveUser())); - Assert.assertFalse(GithubSecretStorage.contains(retrieveOtherUser())); + assertEquals(secret, GithubSecretStorage.retrieve(retrieveUser())); - Assert.assertEquals(secret, GithubSecretStorage.retrieve(retrieveUser())); - }); - } + j.restart(); + + assertTrue(GithubSecretStorage.contains(retrieveUser())); + assertFalse(GithubSecretStorage.contains(retrieveOtherUser())); + + assertEquals(secret, GithubSecretStorage.retrieve(retrieveUser())); + } } diff --git a/src/test/java/org/jenkinsci/plugins/GithubSecurityRealmTest.java b/src/test/java/org/jenkinsci/plugins/GithubSecurityRealmTest.java index 1be14084..30f0f7ba 100644 --- a/src/test/java/org/jenkinsci/plugins/GithubSecurityRealmTest.java +++ b/src/test/java/org/jenkinsci/plugins/GithubSecurityRealmTest.java @@ -1,87 +1,84 @@ /** - The MIT License - -Copyright (c) 2015 Sam Gleske - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. + * The MIT License + *

+ * Copyright (c) 2015 Sam Gleske + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ package org.jenkinsci.plugins; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.ClassRule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.jvnet.hudson.test.JenkinsRule; - -public class GithubSecurityRealmTest { - - @ClassRule - public final static JenkinsRule rule = new JenkinsRule(); - - @Test - public void testEquals_true() { - 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"); - assertEquals(a, b); - } - - @Test - 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 testHasScope_true() { - GithubSecurityRealm a = new GithubSecurityRealm("http://jenkins.acme.com", "http://jenkins.acme.com/api/v3", "someid", "somesecret", "read:org,user,user:email"); - assertTrue(a.hasScope("user")); - assertTrue(a.hasScope("read:org")); - assertTrue(a.hasScope("user:email")); - } - - @Test - public void testHasScope_false() { - GithubSecurityRealm a = new GithubSecurityRealm("http://jenkins.acme.com", "http://jenkins.acme.com/api/v3", "someid", "somesecret", "read:org,user,user:email"); - assertFalse(a.hasScope("somescope")); - } - - @Test - public void testDescriptorImplGetDefaultGithubWebUri() { - GithubSecurityRealm.DescriptorImpl descriptor = new GithubSecurityRealm.DescriptorImpl(); - assertEquals("https://github.com", descriptor.getDefaultGithubWebUri()); - } - - @Test - public void testDescriptorImplGetDefaultGithubApiUri() { - GithubSecurityRealm.DescriptorImpl descriptor = new GithubSecurityRealm.DescriptorImpl(); - assertEquals("https://api.github.com", descriptor.getDefaultGithubApiUri()); - } - - @Test - public void testDescriptorImplGetDefaultOauthScopes() { - GithubSecurityRealm.DescriptorImpl descriptor = new GithubSecurityRealm.DescriptorImpl(); - assertEquals("read:org,user:email,repo", descriptor.getDefaultOauthScopes()); - } +import org.jvnet.hudson.test.junit.jupiter.WithJenkins; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@WithJenkins +class GithubSecurityRealmTest { + + @Test + void testEquals_true(JenkinsRule rule) { + 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"); + assertEquals(a, b); + } + + @Test + void testEquals_false(JenkinsRule rule) { + 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); + } + + @Test + void testHasScope_true(JenkinsRule rule) { + GithubSecurityRealm a = new GithubSecurityRealm("http://jenkins.acme.com", "http://jenkins.acme.com/api/v3", "someid", "somesecret", "read:org,user,user:email"); + assertTrue(a.hasScope("user")); + assertTrue(a.hasScope("read:org")); + assertTrue(a.hasScope("user:email")); + } + + @Test + void testHasScope_false(JenkinsRule rule) { + GithubSecurityRealm a = new GithubSecurityRealm("http://jenkins.acme.com", "http://jenkins.acme.com/api/v3", "someid", "somesecret", "read:org,user,user:email"); + assertFalse(a.hasScope("somescope")); + } + + @Test + void testDescriptorImplGetDefaultGithubWebUri(JenkinsRule rule) { + GithubSecurityRealm.DescriptorImpl descriptor = new GithubSecurityRealm.DescriptorImpl(); + assertEquals("https://github.com", descriptor.getDefaultGithubWebUri()); + } + + @Test + void testDescriptorImplGetDefaultGithubApiUri(JenkinsRule rule) { + GithubSecurityRealm.DescriptorImpl descriptor = new GithubSecurityRealm.DescriptorImpl(); + assertEquals("https://api.github.com", descriptor.getDefaultGithubApiUri()); + } + + @Test + void testDescriptorImplGetDefaultOauthScopes(JenkinsRule rule) { + GithubSecurityRealm.DescriptorImpl descriptor = new GithubSecurityRealm.DescriptorImpl(); + assertEquals("read:org,user:email,repo", descriptor.getDefaultOauthScopes()); + } } diff --git a/src/test/java/org/jenkinsci/plugins/JenkinsProxyAuthenticatorTest.java b/src/test/java/org/jenkinsci/plugins/JenkinsProxyAuthenticatorTest.java index e8ef98d5..1a06b179 100644 --- a/src/test/java/org/jenkinsci/plugins/JenkinsProxyAuthenticatorTest.java +++ b/src/test/java/org/jenkinsci/plugins/JenkinsProxyAuthenticatorTest.java @@ -5,83 +5,83 @@ import okhttp3.Protocol; import okhttp3.Request; import okhttp3.Response; -import org.junit.Assert; -import org.junit.Test; - -public class JenkinsProxyAuthenticatorTest { - - - @Test - public void refusesChallengeIfAuthenticationAlreadyFailed() { - Request previousRequest = - new Request.Builder() - .url("https://example.com") - .header("Proxy-Authorization", "notNull") - .build(); - - Response response = - new Response.Builder() - .code(407) - .request(previousRequest) - .protocol(Protocol.HTTP_1_0) - .message("Unauthorized") - .build(); - - Assert.assertNull(new JenkinsProxyAuthenticator(null).authenticate(null, response)); - } - - @Test - public void refusesPreemptiveOkHttpChallenge() { - Request previousRequest = new Request.Builder().url("https://example.com").build(); - - Response response = - new Response.Builder() - .request(previousRequest) - .header("Proxy-Authenticate", "OkHttp-Preemptive") - .code(407) - .protocol(Protocol.HTTP_1_0) - .message("Unauthorized") - .build(); - - Assert.assertNull(new JenkinsProxyAuthenticator(null).authenticate(null, response)); - } - - @Test - public void acceptsBasicChallenge() { - Request previousRequest = new Request.Builder().url("https://example.com").build(); - - Response response = - new Response.Builder() - .request(previousRequest) - .header("Proxy-Authenticate", "Basic") - .code(407) - .protocol(Protocol.HTTP_1_0) - .message("Unauthorized") - .build(); - - ProxyConfiguration proxyConfiguration = - new ProxyConfiguration("proxy", 80, "user", "password"); - String credentials = Credentials.basic("user", "password"); - Request requestWithBasicAuth = - new JenkinsProxyAuthenticator(proxyConfiguration).authenticate(null, response); - - Assert.assertEquals(requestWithBasicAuth.header("Proxy-Authorization"), credentials); - } - - @Test - public void refusesAnyChallengeWhichIsNotBasicAuthentication() { - Request previousRequest = new Request.Builder().url("https://example.com").build(); - - Response response = - new Response.Builder() - .request(previousRequest) - .code(407) - .protocol(Protocol.HTTP_1_0) - .header("Proxy-Authenticate", "Digest") - .message("Unauthorized") - .build(); - - Assert.assertNull(new JenkinsProxyAuthenticator(null).authenticate(null, response)); - } - +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +class JenkinsProxyAuthenticatorTest { + + @Test + void refusesChallengeIfAuthenticationAlreadyFailed() { + Request previousRequest = + new Request.Builder() + .url("https://example.com") + .header("Proxy-Authorization", "notNull") + .build(); + + Response response = + new Response.Builder() + .code(407) + .request(previousRequest) + .protocol(Protocol.HTTP_1_0) + .message("Unauthorized") + .build(); + + assertNull(new JenkinsProxyAuthenticator(null).authenticate(null, response)); + } + + @Test + void refusesPreemptiveOkHttpChallenge() { + Request previousRequest = new Request.Builder().url("https://example.com").build(); + + Response response = + new Response.Builder() + .request(previousRequest) + .header("Proxy-Authenticate", "OkHttp-Preemptive") + .code(407) + .protocol(Protocol.HTTP_1_0) + .message("Unauthorized") + .build(); + + assertNull(new JenkinsProxyAuthenticator(null).authenticate(null, response)); + } + + @Test + void acceptsBasicChallenge() { + Request previousRequest = new Request.Builder().url("https://example.com").build(); + + Response response = + new Response.Builder() + .request(previousRequest) + .header("Proxy-Authenticate", "Basic") + .code(407) + .protocol(Protocol.HTTP_1_0) + .message("Unauthorized") + .build(); + + ProxyConfiguration proxyConfiguration = + new ProxyConfiguration("proxy", 80, "user", "password"); + String credentials = Credentials.basic("user", "password"); + Request requestWithBasicAuth = + new JenkinsProxyAuthenticator(proxyConfiguration).authenticate(null, response); + + assertEquals(requestWithBasicAuth.header("Proxy-Authorization"), credentials); + } + + @Test + void refusesAnyChallengeWhichIsNotBasicAuthentication() { + Request previousRequest = new Request.Builder().url("https://example.com").build(); + + Response response = + new Response.Builder() + .request(previousRequest) + .code(407) + .protocol(Protocol.HTTP_1_0) + .header("Proxy-Authenticate", "Digest") + .message("Unauthorized") + .build(); + + assertNull(new JenkinsProxyAuthenticator(null).authenticate(null, response)); + } } diff --git a/src/test/java/org/jenkinsci/plugins/api/GihubAPITest.java b/src/test/java/org/jenkinsci/plugins/api/GihubAPITest.java index 8e66a4bf..311f417b 100644 --- a/src/test/java/org/jenkinsci/plugins/api/GihubAPITest.java +++ b/src/test/java/org/jenkinsci/plugins/api/GihubAPITest.java @@ -1,165 +1,156 @@ /** - The MIT License - -Copyright (c) 2011 Michael O'Cleirigh - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - - + * The MIT License + *

+ * Copyright (c) 2011 Michael O'Cleirigh + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ package org.jenkinsci.plugins.api; -import java.io.IOException; -import java.util.Map; -import java.util.Set; -import junit.framework.TestCase; -import org.junit.Ignore; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.kohsuke.github.GHOrganization; import org.kohsuke.github.GHTeam; import org.kohsuke.github.GHUser; import org.kohsuke.github.GitHub; -//TODO could use JUnit Assume.* instead of @Ignore +import java.io.IOException; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertTrue; + /** * @author mocleiri - * + *

* we ignore this test when running the automated tests. */ -@Ignore -public class GihubAPITest extends TestCase { - - /** - * - */ - public GihubAPITest() { - // TODO Auto-generated constructor stub - } - - private static final String LOGIN = System.getProperty("github.login"); - private static final String API_TOKEN = System.getProperty("github.api"); +@Disabled("we ignore this test when running the automated tests") +class GihubAPITest { - // I would sugest with the repo level of permission. - private static final String OAUTH_TOKEN = System.getProperty("github.oauth"); + private static final String LOGIN = System.getProperty("github.login"); + private static final String API_TOKEN = System.getProperty("github.api"); - // the name of the organization to which the login is a participant. - private static final String PARTICPATING_ORG = System.getProperty("github.org"); + // I would suggest with the repo level of permission. + private static final String OAUTH_TOKEN = System.getProperty("github.oauth"); - public GihubAPITest(String name) { - super(name); - // TODO Auto-generated constructor stub - } + // the name of the organization to which the login is a participant. + private static final String PARTICIPATING_ORG = System.getProperty("github.org"); - public void testWithUserAPIToken() throws IOException { - GitHub gh = GitHub.connect(LOGIN, API_TOKEN); + @Test + void testWithUserAPIToken() throws IOException { + GitHub gh = GitHub.connect(LOGIN, API_TOKEN); - GHOrganization org = gh.getOrganization(PARTICPATING_ORG); + GHOrganization org = gh.getOrganization(PARTICIPATING_ORG); - Map teams = org.getTeams(); + Map teams = org.getTeams(); - boolean found = false; + boolean found = false; - for (GHTeam team : teams.values()) { - System.out.println("team = " + team.getName() + ", permission = " - + team.getPermission()); + for (GHTeam team : teams.values()) { + System.out.println("team = " + team.getName() + ", permission = " + + team.getPermission()); - // check for membership - for (GHUser member : team.getMembers()) { - System.out.println("member = " + member.getLogin()); + // check for membership + for (GHUser member : team.getMembers()) { + System.out.println("member = " + member.getLogin()); - if (member.getLogin().equals(LOGIN)) { - found = true; - } - } - } + if (member.getLogin().equals(LOGIN)) { + found = true; + } + } + } - assertTrue(found); - } + assertTrue(found); + } - public void testOrganizationMembership () throws IOException { - GitHub gh = GitHub.connectUsingOAuth(OAUTH_TOKEN); + @Test + void testOrganizationMembership() throws IOException { + GitHub gh = GitHub.connectUsingOAuth(OAUTH_TOKEN); - Map orgs = gh.getMyOrganizations(); + Map orgs = gh.getMyOrganizations(); - for (String orgName : orgs.keySet()) { - GHOrganization org = orgs.get(orgName); + for (String orgName : orgs.keySet()) { + GHOrganization org = orgs.get(orgName); - Map teams = org.getTeams(); + Map teams = org.getTeams(); - System.out.println("org = " + orgName); + System.out.println("org = " + orgName); - for (String name : teams.keySet()) { - GHTeam team = teams.get(name); + for (String name : teams.keySet()) { + GHTeam team = teams.get(name); - Set members = team.getMembers(); + Set members = team.getMembers(); - System.out.println("team = " + team.getName()); + System.out.println("team = " + team.getName()); - for (GHUser ghUser : members) { - System.out.println("member = " + ghUser.getLogin()); - } - } - } + for (GHUser ghUser : members) { + System.out.println("member = " + ghUser.getLogin()); + } + } + } - assertTrue(true); - } + assertTrue(true); + } - public void testOrganizationMembershipAPI () throws IOException { - GitHub gh = GitHub.connect(LOGIN, API_TOKEN); + @Test + void testOrganizationMembershipAPI() throws IOException { + GitHub gh = GitHub.connect(LOGIN, API_TOKEN); - Map orgs = gh.getMyOrganizations(); + Map orgs = gh.getMyOrganizations(); - for (String orgName : orgs.keySet()) { - GHOrganization org = orgs.get(orgName); + for (String orgName : orgs.keySet()) { + GHOrganization org = orgs.get(orgName); - System.out.println("org = " + orgName); - } + System.out.println("org = " + orgName); + } - assertTrue(true); - } + assertTrue(true); + } - // /organizations - public void testWithOAuthToken() throws IOException { - GitHub gh = GitHub.connectUsingOAuth(OAUTH_TOKEN); + // /organizations + @Test + void testWithOAuthToken() throws IOException { + GitHub gh = GitHub.connectUsingOAuth(OAUTH_TOKEN); - GHUser me = gh.getMyself(); + GHUser me = gh.getMyself(); - GHOrganization org = gh.getOrganization(PARTICPATING_ORG); + GHOrganization org = gh.getOrganization(PARTICIPATING_ORG); - Map teams = org.getTeams(); + Map teams = org.getTeams(); - boolean found = false; + boolean found = false; - for (GHTeam team : teams.values()) { - System.out.println("team = " + team.getName() + ", permission = " - + team.getPermission()); + for (GHTeam team : teams.values()) { + System.out.println("team = " + team.getName() + ", permission = " + + team.getPermission()); - // check for membership - for (GHUser member : team.getMembers()) { - System.out.println("member = " + member.getLogin()); + // check for membership + for (GHUser member : team.getMembers()) { + System.out.println("member = " + member.getLogin()); - if (member.getLogin().equals(LOGIN)) { - found = true; - } - } - } + if (member.getLogin().equals(LOGIN)) { + found = true; + } + } + } - assertTrue(found); - } + assertTrue(found); + } }