diff --git a/.github/dependabot.yml b/.github/dependabot.yml index fdc58d1e..03b4d66f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,12 +1,12 @@ -# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuring-dependabot-version-updates version: 2 updates: - - package-ecosystem: "maven" - directory: "/" - schedule: - interval: "weekly" - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" +- package-ecosystem: maven + directory: / + schedule: + interval: monthly +- package-ecosystem: github-actions + directory: / + schedule: + interval: monthly diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml deleted file mode 100644 index e55385dd..00000000 --- a/.github/release-drafter.yml +++ /dev/null @@ -1,4 +0,0 @@ -# https://github.com/jenkinsci/.github/blob/master/.github/release-drafter.adoc - -_extends: .github -tag-template: github-oauth-$NEXT_MINOR_VERSION diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml new file mode 100644 index 00000000..f3717736 --- /dev/null +++ b/.github/workflows/cd.yaml @@ -0,0 +1,19 @@ +# Note: additional setup is required, see https://www.jenkins.io/redirect/continuous-delivery-of-plugins + +name: cd +on: + workflow_dispatch: + check_run: + types: + - completed + +permissions: + checks: read + contents: write + +jobs: + maven-cd: + uses: jenkins-infra/github-reusable-workflows/.github/workflows/maven-cd.yml@v1 + secrets: + MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} + MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }} diff --git a/.github/workflows/jenkins-security-scan.yml b/.github/workflows/jenkins-security-scan.yml index 2e50fbda..2e130112 100644 --- a/.github/workflows/jenkins-security-scan.yml +++ b/.github/workflows/jenkins-security-scan.yml @@ -20,5 +20,5 @@ jobs: security-scan: uses: jenkins-infra/jenkins-security-scan/.github/workflows/jenkins-security-scan.yaml@v2 with: - java-cache: 'maven' - java-version: 11 + java-cache: 'maven' # Optionally enable use of a build dependency cache. Specify 'maven' or 'gradle' as appropriate. + # java-version: 21 # Optionally specify what version of Java to set up for the build, or remove to use a recent default. diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml deleted file mode 100644 index 14c2affe..00000000 --- a/.github/workflows/release-drafter.yml +++ /dev/null @@ -1,16 +0,0 @@ -# Automates creation of Release Drafts using Release Drafter -# More Info: https://github.com/jenkinsci/.github/blob/master/.github/release-drafter.adoc - -on: - push: - branches: - - master - -jobs: - update_release_draft: - runs-on: ubuntu-latest - steps: - # Drafts your next Release notes as Pull Requests are merged into the default branch - - uses: release-drafter/release-drafter@v5 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 00000000..a096e0db --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,12 @@ +tasks: + - init: mvn clean verify + +vscode: + extensions: + - bierner.markdown-preview-github-styles + - vscjava.vscode-java-pack + - redhat.java + - vscjava.vscode-java-debug + - vscjava.vscode-java-dependency + - vscjava.vscode-java-test + - vscjava.vscode-maven diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml index a65d82e1..4e0774d5 100644 --- a/.mvn/extensions.xml +++ b/.mvn/extensions.xml @@ -2,6 +2,6 @@ io.jenkins.tools.incrementals git-changelist-maven-extension - 1.3 + 1.8 diff --git a/.mvn/maven.config b/.mvn/maven.config index 2a0299c4..f7daf60d 100644 --- a/.mvn/maven.config +++ b/.mvn/maven.config @@ -1,2 +1,3 @@ -Pconsume-incrementals -Pmight-produce-incrementals +-Dchangelist.format=%d.v%s diff --git a/Jenkinsfile b/Jenkinsfile index ccf2add3..71ce5ff1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,11 @@ /* - * See the documentation for more options: - * https://github.com/jenkins-infra/pipeline-library/ - */ -buildPlugin(useContainerAgent: true) + See the documentation for more options: + https://github.com/jenkins-infra/pipeline-library/ +*/ +buildPlugin( + forkCount: '1C', // Run parallel tests on ci.jenkins.io for lower costs, faster feedback + useContainerAgent: true, // Set to `false` if you need to use Docker for containerized tests + configurations: [ + [platform: 'linux', jdk: 21], + [platform: 'windows', jdk: 17], +]) diff --git a/pom.xml b/pom.xml index 95f18692..2186ebd1 100644 --- a/pom.xml +++ b/pom.xml @@ -4,20 +4,21 @@ org.jenkins-ci.plugins plugin - 4.40 + 5.12 - 0.39 - -SNAPSHOT - 2.289.3 + 999999-SNAPSHOT + + 2.479 + ${jenkins.baseline}.3 jenkinsci/${project.artifactId}-plugin org.jenkins-ci.plugins github-oauth - 0.39 + ${changelist} GitHub Authentication plugin A Jenkins authentication plugin that delegates to GitHub. We also implement an Authorization Strategy that uses the acquired OAuth token to interact with the GitHub API to determine a user's level of access to Jenkins. hpi @@ -49,7 +50,7 @@ scm:git:https://github.com/${gitHubRepo}.git scm:git:git@github.com:${gitHubRepo}.git https://github.com/${gitHubRepo} - github-oauth-0.39 + ${scmTag} @@ -69,16 +70,11 @@ io.jenkins.tools.bom - bom-2.289.x - 1409.v7659b_c072f18 + bom-${jenkins.baseline}.x + 4669.v0e99c712a_30e import pom - - org.apache.commons - commons-lang3 - 3.12.0 - @@ -112,11 +108,12 @@ org.jenkins-ci.plugins.workflow workflow-multibranch + org.jenkins-ci.plugins github-branch-source - 2.11.4 + org.jenkins-ci.plugins git @@ -125,15 +122,12 @@ org.mockito - mockito-inline + mockito-core test - - - org.jenkins-ci.plugins - external-monitor-job - 191.v363d0d1efdf8 + org.mockito + mockito-junit-jupiter test @@ -149,163 +143,5 @@ - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - org.kohsuke - - access-modifier-checker - - - [1.0,) - - - enforce - - - - - - - - - com.cloudbees - - maven-license-plugin - - - [1.4,) - - - process - - - - - - - - - - org.jvnet.hudson.tools - - - maven-hpi-plugin - - - [2.1.1,) - - - insert-test - - resolve-test-dependencies - - test-hpl - - - - - - - - - - org.apache.maven.plugins - - - maven-enforcer-plugin - - - [1.0,) - - - display-info - - - - - - - - - - org.codehaus.gmaven - - - gmaven-plugin - - - [1.3,) - - - - generateTestStubs - - testCompile - - - - - - - - - - org.jvnet.localizer - - - maven-localizer-plugin - - - [1.12,) - - - generate - - - - - - - - - - org.jenkins-ci.tools - - - maven-hpi-plugin - - - [1.74,) - - - insert-test - - resolve-test-dependencies - - test-hpl - validate - - - - - - - - - - - - diff --git a/src/main/java/org/jenkinsci/plugins/GitHubOAuthScope.java b/src/main/java/org/jenkinsci/plugins/GitHubOAuthScope.java index 4d7650d5..82c26cb6 100644 --- a/src/main/java/org/jenkinsci/plugins/GitHubOAuthScope.java +++ b/src/main/java/org/jenkinsci/plugins/GitHubOAuthScope.java @@ -1,7 +1,6 @@ package org.jenkinsci.plugins; import hudson.ExtensionPoint; - import java.util.Collection; /** diff --git a/src/main/java/org/jenkinsci/plugins/GithubAccessTokenProperty.java b/src/main/java/org/jenkinsci/plugins/GithubAccessTokenProperty.java index a189e1b3..305537b6 100644 --- a/src/main/java/org/jenkinsci/plugins/GithubAccessTokenProperty.java +++ b/src/main/java/org/jenkinsci/plugins/GithubAccessTokenProperty.java @@ -23,6 +23,7 @@ */ package org.jenkinsci.plugins; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Extension; import hudson.model.User; import hudson.model.UserProperty; @@ -30,8 +31,6 @@ import hudson.util.Secret; import org.jenkinsci.Symbol; -import edu.umd.cs.findbugs.annotations.NonNull; - /** * Remembers the access token used to connect to the Github server * diff --git a/src/main/java/org/jenkinsci/plugins/GithubAuthenticationToken.java b/src/main/java/org/jenkinsci/plugins/GithubAuthenticationToken.java index 0a997f45..e1e4562a 100644 --- a/src/main/java/org/jenkinsci/plugins/GithubAuthenticationToken.java +++ b/src/main/java/org/jenkinsci/plugins/GithubAuthenticationToken.java @@ -28,35 +28,17 @@ of this software and associated documentation files (the "Software"), to deal import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; - +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import hudson.model.Item; import hudson.security.Permission; import hudson.security.SecurityRealm; -import jenkins.model.Jenkins; -import okhttp3.OkHttpClient; -import org.acegisecurity.GrantedAuthority; -import org.acegisecurity.GrantedAuthorityImpl; -import org.acegisecurity.providers.AbstractAuthenticationToken; -import org.acegisecurity.userdetails.UsernameNotFoundException; -import org.kohsuke.github.GHMyself; -import org.kohsuke.github.GHOrganization; -import org.kohsuke.github.GHRepository; -import org.kohsuke.github.GHTeam; -import org.kohsuke.github.GHUser; -import org.kohsuke.github.GitHub; -import org.kohsuke.github.GitHubBuilder; -import org.kohsuke.github.RateLimitHandler; -import org.kohsuke.github.extras.okhttp3.OkHttpGitHubConnector; - import java.io.IOException; import java.io.UncheckedIOException; import java.net.MalformedURLException; import java.net.Proxy; import java.net.URL; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -65,10 +47,21 @@ of this software and associated documentation files (the "Software"), to deal import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; - -import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; - +import jenkins.model.Jenkins; +import okhttp3.OkHttpClient; +import org.kohsuke.github.GHMyself; +import org.kohsuke.github.GHOrganization; +import org.kohsuke.github.GHRepository; +import org.kohsuke.github.GHTeam; +import org.kohsuke.github.GHUser; +import org.kohsuke.github.GitHub; +import org.kohsuke.github.GitHubBuilder; +import org.kohsuke.github.RateLimitHandler; +import org.kohsuke.github.extras.okhttp3.OkHttpGitHubConnector; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UsernameNotFoundException; /** * @author mocleiri @@ -189,14 +182,12 @@ public boolean isPrivate() { } } - @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE") public GithubAuthenticationToken(final String accessToken, final String githubServer) throws IOException { this(accessToken, githubServer, false); } - @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE") public GithubAuthenticationToken(final String accessToken, final String githubServer, final boolean clearUserCache) throws IOException { - super(new GrantedAuthority[] {}); + super(List.of()); this.accessToken = accessToken; this.githubServer = githubServer; @@ -215,7 +206,7 @@ public GithubAuthenticationToken(final String accessToken, final String githubSe // may have changed due to SSO [JENKINS-60200]. clearCacheForUser(this.userName); } - authorities.add(SecurityRealm.AUTHENTICATED_AUTHORITY); + authorities.add(SecurityRealm.AUTHENTICATED_AUTHORITY2); // This stuff only really seems useful if *not* using GithubAuthorizationStrategy // but instead using matrix so org/team can be granted rights @@ -248,11 +239,11 @@ public GithubAuthenticationToken(final String accessToken, final String githubSe for (Map.Entry> teamEntry : myTeams.entrySet()) { String orgLogin = teamEntry.getKey(); LOGGER.log(Level.FINE, "Fetch teams for user " + userName + " in organization " + orgLogin); - authorities.add(new GrantedAuthorityImpl(orgLogin)); + authorities.add(new SimpleGrantedAuthority(orgLogin)); for (GHTeam team : teamEntry.getValue()) { String teamIdentifier = team.getSlug() == null ? team.getName() : team.getSlug(); - authorities.add(new GrantedAuthorityImpl(orgLogin + GithubOAuthGroupDetails.ORG_TEAM_SEPARATOR + authorities.add(new SimpleGrantedAuthority(orgLogin + GithubOAuthGroupDetails.ORG_TEAM_SEPARATOR + teamIdentifier)); } } @@ -346,8 +337,8 @@ private static Proxy getProxy(@NonNull String host) { } @Override - public GrantedAuthority[] getAuthorities() { - return authorities.toArray(new GrantedAuthority[0]); + public Collection getAuthorities() { + return authorities; } @Override @@ -380,7 +371,6 @@ public GHMyself getMyself() throws IOException { * @return the Set of org names current user is a member of */ @NonNull - @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE") private Set getUserOrgs() { return userOrganizationCache.get(this.userName, unused -> { try { @@ -445,7 +435,6 @@ private boolean isReadRelatedPermission(@NonNull Permission permission) { * @return [description] */ @NonNull - @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE") private Cache myRepositories() { return repositoriesByUserCache.get(this.userName, unused -> { // listRepositories returns all repos owned by user, where they are a collaborator, @@ -525,7 +514,6 @@ GHOrganization loadOrganization(@NonNull String organization) { } @NonNull - @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE") private RepoRights loadRepository(@NonNull final String repositoryName) { try { if (gh != null && isAuthenticated() && (myRealm.hasScope("repo") || myRealm.hasScope("public_repo"))) { @@ -578,7 +566,7 @@ GHTeam loadTeam(@NonNull String organization, @NonNull String team) { GithubOAuthUserDetails getUserDetails(@NonNull String username) throws IOException { GHUser user = loadUser(username); if (user != null) { - return new GithubOAuthUserDetails(user.getLogin(), this); + return new GithubOAuthUserDetails(user.getLogin(), getAuthorities()); } return null; } diff --git a/src/main/java/org/jenkinsci/plugins/GithubAuthorizationStrategy.java b/src/main/java/org/jenkinsci/plugins/GithubAuthorizationStrategy.java index b9927aa6..6210b10e 100644 --- a/src/main/java/org/jenkinsci/plugins/GithubAuthorizationStrategy.java +++ b/src/main/java/org/jenkinsci/plugins/GithubAuthorizationStrategy.java @@ -26,16 +26,7 @@ of this software and associated documentation files (the "Software"), to deal */ package org.jenkinsci.plugins; -import org.apache.commons.lang.StringUtils; -import org.jenkinsci.plugins.workflow.job.WorkflowJob; -import org.jenkinsci.plugins.workflow.multibranch.BranchJobProperty; -import org.kohsuke.stapler.DataBoundConstructor; - -import java.util.Collection; -import java.util.Collections; - import edu.umd.cs.findbugs.annotations.NonNull; - import hudson.Extension; import hudson.model.AbstractItem; import hudson.model.AbstractProject; @@ -43,7 +34,14 @@ of this software and associated documentation files (the "Software"), to deal import hudson.model.Job; import hudson.security.ACL; import hudson.security.AuthorizationStrategy; +import java.util.Collection; +import java.util.Collections; import jenkins.branch.MultiBranchProject; +import org.apache.commons.lang.StringUtils; +import org.jenkinsci.plugins.workflow.job.WorkflowJob; +import org.jenkinsci.plugins.workflow.multibranch.BranchJobProperty; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; /** * @author mocleiri @@ -140,6 +138,23 @@ public String getAdminUserNames() { return StringUtils.join(rootACL.getAdminUserNameList().iterator(), ", "); } + /** Set the agent username. We use a setter instead of a constructor to make this an optional field + * to avoid a breaking change. + * @see org.jenkinsci.plugins.GithubRequireOrganizationMembershipACL#setAgentUserName(String) + */ + @DataBoundSetter + public void setAgentUserName(String agentUserName) { + rootACL.setAgentUserName(agentUserName); + } + + /** + * @return agentUserName + * @see GithubRequireOrganizationMembershipACL#getAgentUserName() + */ + public String getAgentUserName() { + return rootACL.getAgentUserName(); + } + /** * @return isUseRepositoryPermissions * @see org.jenkinsci.plugins.GithubRequireOrganizationMembershipACL#isUseRepositoryPermissions() @@ -208,6 +223,7 @@ public boolean equals(Object object){ GithubAuthorizationStrategy obj = (GithubAuthorizationStrategy) object; return this.getOrganizationNames().equals(obj.getOrganizationNames()) && this.getAdminUserNames().equals(obj.getAdminUserNames()) && + this.getAgentUserName().equals(obj.getAgentUserName()) && this.isUseRepositoryPermissions() == obj.isUseRepositoryPermissions() && this.isAuthenticatedUserCreateJobPermission() == obj.isAuthenticatedUserCreateJobPermission() && this.isAuthenticatedUserReadPermission() == obj.isAuthenticatedUserReadPermission() && diff --git a/src/main/java/org/jenkinsci/plugins/GithubLogoutAction.java b/src/main/java/org/jenkinsci/plugins/GithubLogoutAction.java index e662ad02..2f25a357 100644 --- a/src/main/java/org/jenkinsci/plugins/GithubLogoutAction.java +++ b/src/main/java/org/jenkinsci/plugins/GithubLogoutAction.java @@ -23,13 +23,12 @@ */ package org.jenkinsci.plugins; -import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.NoExternalUse; - import hudson.Extension; import hudson.model.UnprotectedRootAction; import hudson.security.SecurityRealm; import jenkins.model.Jenkins; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; /** * A page that shows a simple message when the user logs out. diff --git a/src/main/java/org/jenkinsci/plugins/GithubOAuthGroupDetails.java b/src/main/java/org/jenkinsci/plugins/GithubOAuthGroupDetails.java index 0449d556..ab0bb5dd 100644 --- a/src/main/java/org/jenkinsci/plugins/GithubOAuthGroupDetails.java +++ b/src/main/java/org/jenkinsci/plugins/GithubOAuthGroupDetails.java @@ -3,13 +3,11 @@ */ package org.jenkinsci.plugins; -import org.kohsuke.github.GHOrganization; -import org.kohsuke.github.GHTeam; - import hudson.security.GroupDetails; - import java.io.IOException; import java.io.UncheckedIOException; +import org.kohsuke.github.GHOrganization; +import org.kohsuke.github.GHTeam; /** * @author Mike diff --git a/src/main/java/org/jenkinsci/plugins/GithubOAuthUserDetails.java b/src/main/java/org/jenkinsci/plugins/GithubOAuthUserDetails.java index c4acbd23..3bb01a02 100644 --- a/src/main/java/org/jenkinsci/plugins/GithubOAuthUserDetails.java +++ b/src/main/java/org/jenkinsci/plugins/GithubOAuthUserDetails.java @@ -3,52 +3,22 @@ */ package org.jenkinsci.plugins; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import org.acegisecurity.GrantedAuthority; -import org.acegisecurity.userdetails.User; -import org.acegisecurity.userdetails.UserDetails; -import org.kohsuke.github.GHUser; - import edu.umd.cs.findbugs.annotations.NonNull; -import java.io.IOException; +import java.util.Collection; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; /** * @author Mike * */ -@SuppressFBWarnings("EQ_DOESNT_OVERRIDE_EQUALS") public class GithubOAuthUserDetails extends User implements UserDetails { private static final long serialVersionUID = 1L; - private boolean hasGrantedAuthorities; - - private final GithubAuthenticationToken authenticationToken; - - public GithubOAuthUserDetails(@NonNull String login, @NonNull GrantedAuthority[] authorities) { + public GithubOAuthUserDetails(@NonNull String login, @NonNull Collection authorities) { super(login, "", true, true, true, true, authorities); - this.authenticationToken = null; - this.hasGrantedAuthorities = true; } - public GithubOAuthUserDetails(@NonNull String login, @NonNull GithubAuthenticationToken authenticationToken) { - super(login, "", true, true, true, true, new GrantedAuthority[0]); - this.authenticationToken = authenticationToken; - this.hasGrantedAuthorities = false; - } - - @Override - public GrantedAuthority[] getAuthorities() { - if (!hasGrantedAuthorities) { - try { - GHUser user = authenticationToken.loadUser(getUsername()); - if(user != null) { - setAuthorities(authenticationToken.getAuthorities()); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - return super.getAuthorities(); - } } diff --git a/src/main/java/org/jenkinsci/plugins/GithubRequireOrganizationMembershipACL.java b/src/main/java/org/jenkinsci/plugins/GithubRequireOrganizationMembershipACL.java index a823c017..62cc3c6b 100644 --- a/src/main/java/org/jenkinsci/plugins/GithubRequireOrganizationMembershipACL.java +++ b/src/main/java/org/jenkinsci/plugins/GithubRequireOrganizationMembershipACL.java @@ -26,32 +26,31 @@ of this software and associated documentation files (the "Software"), to deal */ package org.jenkinsci.plugins; -import org.acegisecurity.Authentication; -import org.jenkinsci.plugins.github_branch_source.GitHubSCMSource; -import org.jenkinsci.plugins.workflow.job.WorkflowJob; -import org.jenkinsci.plugins.workflow.multibranch.BranchJobProperty; -import org.kohsuke.stapler.Stapler; -import org.kohsuke.stapler.StaplerRequest; - -import java.net.URI; -import java.util.LinkedList; -import java.util.List; -import java.util.logging.Logger; - import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; - import hudson.model.AbstractItem; import hudson.model.AbstractProject; +import hudson.model.Computer; import hudson.model.Describable; +import hudson.model.Hudson; import hudson.model.Item; import hudson.plugins.git.GitSCM; import hudson.plugins.git.UserRemoteConfig; import hudson.security.ACL; import hudson.security.Permission; +import java.net.URI; +import java.util.LinkedList; +import java.util.List; +import java.util.logging.Logger; import jenkins.branch.MultiBranchProject; import jenkins.model.Jenkins; import jenkins.scm.api.SCMSource; +import org.jenkinsci.plugins.github_branch_source.GitHubSCMSource; +import org.jenkinsci.plugins.workflow.job.WorkflowJob; +import org.jenkinsci.plugins.workflow.multibranch.BranchJobProperty; +import org.kohsuke.stapler.Stapler; +import org.kohsuke.stapler.StaplerRequest2; +import org.springframework.security.core.Authentication; /** * @author Mike @@ -64,6 +63,7 @@ public class GithubRequireOrganizationMembershipACL extends ACL { private final List organizationNameList; private final List adminUserNameList; + private String agentUserName; private final boolean authenticatedUserReadPermission; private final boolean useRepositoryPermissions; private final boolean authenticatedUserCreateJobPermission; @@ -76,11 +76,11 @@ public class GithubRequireOrganizationMembershipACL extends ACL { /* * (non-Javadoc) * - * @see hudson.security.ACL#hasPermission(org.acegisecurity.Authentication, + * @see hudson.security.ACL#hasPermission(org.springframework.security.core.Authentication, * hudson.security.Permission) */ @Override - public boolean hasPermission(@NonNull Authentication a, @NonNull Permission permission) { + public boolean hasPermission2(@NonNull Authentication a, @NonNull Permission permission) { if (a instanceof GithubAuthenticationToken) { if (!a.isAuthenticated()) return false; @@ -102,6 +102,12 @@ public boolean hasPermission(@NonNull Authentication a, @NonNull Permission perm return true; } + // Grant agent permissions to agent user + if (candidateName.equalsIgnoreCase(agentUserName) && checkAgentUserPermission(permission)) { + log.finest("Granting Agent Connect rights to user " + candidateName); + return true; + } + // Are they trying to read? if (checkReadPermission(permission)) { // if we support authenticated read return early @@ -147,12 +153,18 @@ else if (testBuildPermission(permission) && isInWhitelistedOrgs(authenticationTo throw new IllegalArgumentException("Authentication must have a valid name"); } - if (authenticatedUserName.equals(SYSTEM.getPrincipal())) { + if (authenticatedUserName.equals(SYSTEM2.getPrincipal())) { // give system user full access log.finest("Granting Full rights to SYSTEM user."); return true; } + // Grant agent permissions to agent user + if (authenticatedUserName.equalsIgnoreCase(agentUserName) && checkAgentUserPermission(permission)) { + log.finest("Granting Agent Connect rights to user " + authenticatedUserName); + return true; + } + if (authenticatedUserName.equals("anonymous")) { if (checkJobStatusPermission(permission) && allowAnonymousJobStatusPermission) { return true; @@ -221,7 +233,7 @@ private boolean currentUriPathEndsWithSegment( String segment ) { @Nullable private String requestURI() { - StaplerRequest currentRequest = Stapler.getCurrentRequest(); + StaplerRequest2 currentRequest = Stapler.getCurrentRequest2(); return (currentRequest == null) ? null : currentRequest.getOriginalRequestURI(); } @@ -239,6 +251,13 @@ private boolean checkReadPermission(@NonNull Permission permission) { || id.equals("hudson.model.Item.Read")); } + private boolean checkAgentUserPermission(@NonNull Permission permission) { + return permission.equals(Hudson.READ) + || permission.equals(Computer.CREATE) + || permission.equals(Computer.CONNECT) + || permission.equals(Computer.CONFIGURE); + } + private boolean checkJobStatusPermission(@NonNull Permission permission) { return permission.getId().equals("hudson.model.Item.ViewStatus"); } @@ -314,10 +333,11 @@ public GithubRequireOrganizationMembershipACL(String adminUserNames, } this.item = null; + this.agentUserName = ""; // Initially blank - populated by a setter since this field is optional } public GithubRequireOrganizationMembershipACL cloneForProject(AbstractItem item) { - return new GithubRequireOrganizationMembershipACL( + GithubRequireOrganizationMembershipACL acl = new GithubRequireOrganizationMembershipACL( this.adminUserNameList, this.organizationNameList, this.authenticatedUserReadPermission, @@ -328,6 +348,8 @@ public GithubRequireOrganizationMembershipACL cloneForProject(AbstractItem item) this.allowAnonymousReadPermission, this.allowAnonymousJobStatusPermission, item); + acl.setAgentUserName(agentUserName); + return acl; } public GithubRequireOrganizationMembershipACL(List adminUserNameList, @@ -362,6 +384,11 @@ public List getAdminUserNameList() { return adminUserNameList; } + public void setAgentUserName(String agentUserName) { + this.agentUserName = agentUserName; + } + public String getAgentUserName() { return agentUserName; } + public boolean isUseRepositoryPermissions() { return useRepositoryPermissions; } diff --git a/src/main/java/org/jenkinsci/plugins/GithubSecretStorage.java b/src/main/java/org/jenkinsci/plugins/GithubSecretStorage.java index ca8659dc..b08bcc0c 100644 --- a/src/main/java/org/jenkinsci/plugins/GithubSecretStorage.java +++ b/src/main/java/org/jenkinsci/plugins/GithubSecretStorage.java @@ -23,13 +23,12 @@ */ package org.jenkinsci.plugins; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.model.User; - import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; -import edu.umd.cs.findbugs.annotations.CheckForNull; -import edu.umd.cs.findbugs.annotations.NonNull; public class GithubSecretStorage { diff --git a/src/main/java/org/jenkinsci/plugins/GithubSecurityRealm.java b/src/main/java/org/jenkinsci/plugins/GithubSecurityRealm.java index 803fd1c5..2a7207ea 100644 --- a/src/main/java/org/jenkinsci/plugins/GithubSecurityRealm.java +++ b/src/main/java/org/jenkinsci/plugins/GithubSecurityRealm.java @@ -32,6 +32,8 @@ of this software and associated documentation files (the "Software"), to deal import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import hudson.Extension; import hudson.ProxyConfiguration; import hudson.Util; @@ -40,20 +42,24 @@ of this software and associated documentation files (the "Software"), to deal import hudson.security.AbstractPasswordBasedSecurityRealm; import hudson.security.GroupDetails; import hudson.security.SecurityRealm; -import hudson.security.UserMayOrMayNotExistException; +import hudson.security.UserMayOrMayNotExistException2; import hudson.tasks.Mailer; import hudson.util.Secret; +import jakarta.servlet.http.HttpSession; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; import jenkins.model.Jenkins; import jenkins.security.SecurityListener; -import org.acegisecurity.Authentication; -import org.acegisecurity.AuthenticationException; -import org.acegisecurity.AuthenticationManager; -import org.acegisecurity.BadCredentialsException; -import org.acegisecurity.context.SecurityContextHolder; -import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; -import org.acegisecurity.userdetails.UserDetails; -import org.acegisecurity.userdetails.UserDetailsService; -import org.acegisecurity.userdetails.UsernameNotFoundException; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; @@ -62,7 +68,6 @@ of this software and associated documentation files (the "Software"), to deal import org.apache.http.client.CredentialsProvider; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; @@ -72,28 +77,24 @@ of this software and associated documentation files (the "Software"), to deal import org.kohsuke.github.GHMyself; import org.kohsuke.github.GHOrganization; import org.kohsuke.github.GHTeam; +import org.kohsuke.github.GHUser; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.Header; import org.kohsuke.stapler.HttpRedirect; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.HttpResponses; import org.kohsuke.stapler.QueryParameter; -import org.kohsuke.stapler.StaplerRequest; -import org.springframework.dao.DataAccessException; -import org.springframework.dao.DataRetrievalFailureException; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; -import javax.servlet.http.HttpSession; +import org.kohsuke.stapler.StaplerRequest2; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; /** * @@ -103,7 +104,7 @@ of this software and associated documentation files (the "Software"), to deal * This is based on the MySQLSecurityRealm from the mysql-auth-plugin written by * Alex Ackerman. */ -public class GithubSecurityRealm extends AbstractPasswordBasedSecurityRealm implements UserDetailsService { +public class GithubSecurityRealm extends AbstractPasswordBasedSecurityRealm { private static final String DEFAULT_WEB_URI = "https://github.com"; private static final String DEFAULT_API_URI = "https://api.github.com"; private static final String DEFAULT_ENTERPRISE_API_SUFFIX = "/api/v3"; @@ -334,7 +335,7 @@ public String getOauthScopes() { return oauthScopes; } - public HttpResponse doCommenceLogin(StaplerRequest request, @QueryParameter String from, @Header("Referer") final String referer) + public HttpResponse doCommenceLogin(StaplerRequest2 request, @QueryParameter String from, @Header("Referer") final String referer) throws IOException { // https://tools.ietf.org/html/rfc6749#section-10.10 dictates that probability that an attacker guesses the string // SHOULD be less than or equal to 2^(-160) and our Strings consist of 65 chars. (65^27 ~= 2^160) @@ -357,7 +358,7 @@ public HttpResponse doCommenceLogin(StaplerRequest request, @QueryParameter Stri } String suffix=""; if (!scopes.isEmpty()) { - suffix = "&scope="+Util.join(scopes,",")+"&state="+state; + suffix = "&scope=" + String.join(",", scopes) + "&state=" + state; } else { // We need repo scope in order to access private repos // See https://developer.github.com/v3/oauth/#scopes @@ -372,7 +373,7 @@ public HttpResponse doCommenceLogin(StaplerRequest request, @QueryParameter Stri * This is where the user comes back to at the end of the OAuth redirect * ping-pong. */ - public HttpResponse doFinishLogin(StaplerRequest request) + public HttpResponse doFinishLogin(StaplerRequest2 request) throws IOException { String code = request.getParameter("code"); String state = request.getParameter(STATE_ATTRIBUTE); @@ -437,7 +438,7 @@ public HttpResponse doFinishLogin(StaplerRequest request) } } - SecurityListener.fireAuthenticated(new GithubOAuthUserDetails(self.getLogin(), auth.getAuthorities())); + SecurityListener.fireAuthenticated2(new GithubOAuthUserDetails(self.getLogin(), auth.getAuthorities())); // While LastGrantedAuthorities are triggered by that event, we cannot trigger it there // or modifications in organizations will be not reflected when using API Token, due to that caching @@ -563,7 +564,7 @@ public Authentication authenticate(Authentication authentication) GithubSecretStorage.put(user, token.getCredentials().toString()); } - SecurityListener.fireAuthenticated(new GithubOAuthUserDetails(token.getName(), github.getAuthorities())); + SecurityListener.fireAuthenticated2(new GithubOAuthUserDetails(token.getName(), github.getAuthorities())); return github; } catch (IOException e) { @@ -572,11 +573,11 @@ public Authentication authenticate(Authentication authentication) throw new BadCredentialsException( "Unexpected authentication type: " + authentication); } - }, GithubSecurityRealm.this::loadUserByUsername); + }, GithubSecurityRealm.this::loadUserByUsername2); } @Override - protected GithubOAuthUserDetails authenticate(String username, String password) throws AuthenticationException { + protected GithubOAuthUserDetails authenticate2(String username, String password) throws AuthenticationException { try { GithubAuthenticationToken github = new GithubAuthenticationToken(password, getGithubApiUri()); if(username.equals(github.getPrincipal())) { @@ -595,12 +596,12 @@ public String getLoginUrl() { } @Override - protected String getPostLogOutUrl(StaplerRequest req, Authentication auth) { + protected String getPostLogOutUrl2(StaplerRequest2 req, Authentication auth) { // if we just redirect to the root and anonymous does not have Overall read then we will start a login all over again. // we are actually anonymous here as the security context has been cleared Jenkins j = Jenkins.get(); if (j.hasPermission(Jenkins.READ)) { - return super.getPostLogOutUrl(req, auth); + return super.getPostLogOutUrl2(req, auth); } return req.getContextPath()+ "/" + GithubLogoutAction.POST_LOGOUT_URL; } @@ -656,8 +657,8 @@ public DescriptorImpl getDescriptor() { * @return userDetails */ @Override - public UserDetails loadUserByUsername(String username) - throws UsernameNotFoundException, DataAccessException { + public UserDetails loadUserByUsername2(String username) + throws UsernameNotFoundException { //username is in org*team format if(username.contains(GithubOAuthGroupDetails.ORG_TEAM_SEPARATOR)) { throw new UsernameNotFoundException("Using org*team format instead of username: " + username); @@ -674,7 +675,7 @@ public UserDetails loadUserByUsername(String username) } } catch(IOException | UsernameNotFoundException e) { if(e instanceof IOException) { - throw new UserMayOrMayNotExistException("Could not connect to GitHub API server, target URL = " + getGithubApiUri(), e); + throw new UserMayOrMayNotExistException2("Could not connect to GitHub API server, target URL = " + getGithubApiUri(), e); } else { // user not found so continuing normally re-using the current context holder LOGGER.log(Level.FINE, "Attempted to impersonate " + username + " but token in user property was invalid."); @@ -686,7 +687,7 @@ public UserDetails loadUserByUsername(String username) if (token instanceof GithubAuthenticationToken) { authToken = (GithubAuthenticationToken) token; } else { - throw new UserMayOrMayNotExistException("Unexpected authentication type: " + token); + throw new UserMayOrMayNotExistException2("Unexpected authentication type: " + token); } /** @@ -694,7 +695,19 @@ public UserDetails loadUserByUsername(String username) * Taken from hudson.security.HudsonPrivateSecurityRealm#loadUserByUsername(java.lang.String) */ if (localUser != null) { - return new GithubOAuthUserDetails(username, authToken); + GHUser user; + try { + user = authToken.loadUser(username); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + Collection authorities; + if (user != null) { + authorities = authToken.getAuthorities(); + } else { + authorities = List.of(); + } + return new GithubOAuthUserDetails(username, authorities); } try { @@ -711,7 +724,7 @@ public UserDetails loadUserByUsername(String username) return userDetails; } catch (IOException | Error e) { - throw new DataRetrievalFailureException("loadUserByUsername (username=" + username +")", e); + throw new AuthenticationServiceException("loadUserByUsername (username=" + username +")", e); } } @@ -751,8 +764,8 @@ public int hashCode() { * @return groupDetails */ @Override - public GroupDetails loadGroupByGroupname(String groupName) - throws UsernameNotFoundException, DataAccessException { + public GroupDetails loadGroupByGroupname2(String groupName, boolean fetchMembers) + throws UsernameNotFoundException { GithubAuthenticationToken authToken = (GithubAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); if(authToken == null) @@ -778,7 +791,7 @@ public GroupDetails loadGroupByGroupname(String groupName) return new GithubOAuthGroupDetails(ghOrg); } } catch (Error e) { - throw new DataRetrievalFailureException("loadGroupByGroupname (groupname=" + groupName + ")", e); + throw new AuthenticationServiceException("loadGroupByGroupname (groupname=" + groupName + ")", e); } } diff --git a/src/main/resources/org/jenkinsci/plugins/GithubAuthorizationStrategy/config.jelly b/src/main/resources/org/jenkinsci/plugins/GithubAuthorizationStrategy/config.jelly index 2455a404..abd722d1 100644 --- a/src/main/resources/org/jenkinsci/plugins/GithubAuthorizationStrategy/config.jelly +++ b/src/main/resources/org/jenkinsci/plugins/GithubAuthorizationStrategy/config.jelly @@ -8,6 +8,10 @@ + + + + diff --git a/src/main/webapp/help/auth/agent-user-name-help.html b/src/main/webapp/help/auth/agent-user-name-help.html new file mode 100644 index 00000000..eee865b0 --- /dev/null +++ b/src/main/webapp/help/auth/agent-user-name-help.html @@ -0,0 +1,3 @@ +
+If you are using inbound Jenkins agents, this is the user that is used for authenticating agents. This user will receive rights to create, connect and configure agents. +
\ No newline at end of file diff --git a/src/test/java/org/jenkinsci/plugins/GithubAccessTokenPropertySEC797Test.java b/src/test/java/org/jenkinsci/plugins/GithubAccessTokenPropertySEC797Test.java index a270482e..7875afe1 100644 --- a/src/test/java/org/jenkinsci/plugins/GithubAccessTokenPropertySEC797Test.java +++ b/src/test/java/org/jenkinsci/plugins/GithubAccessTokenPropertySEC797Test.java @@ -23,156 +23,145 @@ */ package org.jenkinsci.plugins; -import com.gargoylesoftware.htmlunit.Page; -import com.gargoylesoftware.htmlunit.WebRequest; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import edu.umd.cs.findbugs.annotations.CheckForNull; import hudson.model.UnprotectedRootAction; import hudson.util.HttpResponses; -import java.util.Collections; +import jakarta.servlet.http.HttpSession; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.servlet.DefaultServlet; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; +import org.eclipse.jetty.util.Fields; +import org.eclipse.jetty.util.UrlEncoded; +import org.htmlunit.Page; +import org.htmlunit.WebRequest; +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.StaplerRequest; +import org.kohsuke.stapler.StaplerRequest2; -import edu.umd.cs.findbugs.annotations.CheckForNull; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; import java.io.IOException; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.URI; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; +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(); - +@WithJenkins +class GithubAccessTokenPropertySEC797Test { + + private JenkinsRule j; private JenkinsRule.WebClient wc; - - private Server server; + + private HttpServer 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); - + + private void setupMockGithubServer() throws Exception { + server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0); servlet = new MockGithubServlet(j); - - ServletContextHandler context = new ServletContextHandler(); - ServletHolder servletHolder = new ServletHolder("default", servlet); - context.addServlet(servletHolder, "/*"); - server.setHandler(context); - + server.createContext("/", servlet); 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)); + + InetSocketAddress address = server.getAddress(); + serverUri = new URI(String.format("http://%s:%d/", address.getHostString(), address.getPort())); 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 static class MockGithubServlet implements HttpHandler { private String currentLogin; private List organizations; private List teams; - - private JenkinsRule jenkinsRule; + + 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()) { + + @Override + public void handle(HttpExchange he) throws IOException { + switch (he.getRequestURI().getPath()) { case "/user": - this.onUser(req, resp); + this.onUser(he); break; case "/users/_specific_login_": - this.onUser(req, resp); + this.onUser(he); break; case "/user/orgs": - this.onUserOrgs(req, resp); + this.onUserOrgs(he); break; case "/user/teams": - this.onUserTeams(req, resp); + this.onUserTeams(he); break; case "/orgs/org-a": - this.onOrgs(req, resp, "org-a"); + this.onOrgs(he, "org-a"); break; case "/orgs/org-a/teams": - this.onOrgsTeam(req, resp, "org-a"); + this.onOrgsTeam(he, "org-a"); break; case "/orgs/org-a/members/alice": - this.onOrgsMember(req, resp, "org-a", "alice"); + this.onOrgsMember(he, "org-a", "alice"); break; case "/teams/7/members/alice": - this.onTeamMember(req, resp, "team-b", "alice"); + this.onTeamMember(he, "team-b", "alice"); break; case "/orgs/org-c": - this.onOrgs(req, resp, "org-c"); + this.onOrgs(he, "org-c"); break; case "/orgs/org-c/teams": - this.onOrgsTeam(req, resp, "org-c"); + this.onOrgsTeam(he, "org-c"); break; case "/orgs/org-c/members/bob": - this.onOrgsMember(req, resp, "org-c", "bob"); + this.onOrgsMember(he, "org-c", "bob"); break; case "/teams/7/members/bob": - this.onTeamMember(req, resp, "team-d", "bob"); + this.onTeamMember(he, "team-d", "bob"); break; case "/login/oauth/authorize": - this.onLoginOAuthAuthorize(req, resp); + this.onLoginOAuthAuthorize(he); break; case "/login/oauth/access_token": - this.onLoginOAuthAccessToken(req, resp); + this.onLoginOAuthAccessToken(he); break; default: - throw new RuntimeException("Url not mapped yet: " + req.getRequestURI()); + throw new RuntimeException("Url not mapped yet: " + he.getRequestURI().getPath()); } + he.close(); } - - private void onUser(HttpServletRequest req, HttpServletResponse resp) throws IOException { - resp.getWriter().write(JSONObject.fromObject( + + private void onUser(HttpExchange he) throws IOException { + sendResponse(he, JSONObject.fromObject( new HashMap() {{ put("login", currentLogin); put("name", currentLogin + "_name"); @@ -182,42 +171,42 @@ private void onUser(HttpServletRequest req, HttpServletResponse resp) throws IOE }} ).toString()); } - - private void onUserOrgs(HttpServletRequest req, HttpServletResponse resp) throws IOException { + + private void onUserOrgs(HttpExchange he) throws IOException { List> responseBody = new ArrayList<>(); for (String orgName : organizations) { final String orgName_ = orgName; - responseBody.add(new HashMap() {{ + responseBody.add(new HashMap<>() {{ put("login", orgName_); }}); } - - resp.getWriter().write(JSONArray.fromObject(responseBody).toString()); + + sendResponse(he, JSONArray.fromObject(responseBody).toString()); } - - private void onOrgs(HttpServletRequest req, HttpServletResponse resp, final String orgName) throws IOException { + + private void onOrgs(HttpExchange he, final String orgName) throws IOException { Map responseBody = new HashMap() {{ put("login", orgName); }}; - - resp.getWriter().write(JSONObject.fromObject(responseBody).toString()); + + sendResponse(he, JSONObject.fromObject(responseBody).toString()); } - - private void onOrgsMember(HttpServletRequest req, HttpServletResponse resp, String orgName, String userName) { - resp.setStatus(HttpServletResponse.SC_NO_CONTENT); + + private void onOrgsMember(HttpExchange he, String orgName, String userName) throws IOException { + he.sendResponseHeaders(HttpURLConnection.HTTP_NO_CONTENT, -1); // 302 / 404 responses not implemented } - - private void onTeamMember(HttpServletRequest req, HttpServletResponse resp, String orgName, String userName) { - resp.setStatus(HttpServletResponse.SC_NO_CONTENT); + + private void onTeamMember(HttpExchange he, String orgName, String userName) throws IOException { + he.sendResponseHeaders(HttpURLConnection.HTTP_NO_CONTENT, -1); // 302 / 404 responses not implemented } - - private void onOrgsTeam(HttpServletRequest req, HttpServletResponse resp, final String orgName) throws IOException { + + private void onOrgsTeam(HttpExchange he, final String orgName) throws IOException { List> responseBody = new ArrayList<>(); for (String teamName : teams) { final String teamName_ = teamName; - responseBody.add(new HashMap() {{ + responseBody.add(new HashMap<>() {{ put("id", 7); put("login", teamName_ + "_login"); put("name", teamName_); @@ -226,15 +215,15 @@ private void onOrgsTeam(HttpServletRequest req, HttpServletResponse resp, final }}); }}); } - - resp.getWriter().write(JSONArray.fromObject(responseBody).toString()); + + sendResponse(he, JSONArray.fromObject(responseBody).toString()); } - - private void onUserTeams(HttpServletRequest req, HttpServletResponse resp) throws IOException { + + private void onUserTeams(HttpExchange he) throws IOException { List> responseBody = new ArrayList<>(); for (String teamName : teams) { final String teamName_ = teamName; - responseBody.add(new HashMap() {{ + responseBody.add(new HashMap<>() {{ put("login", teamName_ + "_login"); put("name", teamName_); put("organization", new HashMap() {{ @@ -242,35 +231,47 @@ private void onUserTeams(HttpServletRequest req, HttpServletResponse resp) throw }}); }}); } - - resp.getWriter().write(JSONArray.fromObject(responseBody).toString()); + + sendResponse(he, JSONArray.fromObject(responseBody).toString()); } - - private void onLoginOAuthAuthorize(HttpServletRequest req, HttpServletResponse resp) throws IOException { + + private void onLoginOAuthAuthorize(HttpExchange he) throws IOException { String code = "test"; - String state = req.getParameter("state"); - resp.sendRedirect(jenkinsRule.getURL() + "securityRealm/finishLogin?code=" + code + "&state=" + state); + Fields fields = new Fields(); + UrlEncoded.decodeUtf8To(he.getRequestURI().getQuery(), fields); + String state = fields.getValue("state"); + he.getResponseHeaders().set("Location", jenkinsRule.getURL() + "securityRealm/finishLogin?code=" + code + "&state=" + state); + he.sendResponseHeaders(302, -1); + } + + private void onLoginOAuthAccessToken(HttpExchange he) throws IOException { + sendResponse(he, "access_token=RANDOM_ACCESS_TOKEN"); } - - private void onLoginOAuthAccessToken(HttpServletRequest req, HttpServletResponse resp) throws IOException { - resp.getWriter().write("access_token=RANDOM_ACCESS_TOKEN"); + + private void sendResponse(HttpExchange he, String response) throws IOException { + byte[] body = response.getBytes(StandardCharsets.UTF_8); + he.sendResponseHeaders(HttpURLConnection.HTTP_OK, body.length); + try (OutputStream os = he.getResponseBody()) { + os.write(body); + } } } - - @Before - public void prepareRealmAndWebClient() throws Exception { + + @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, @@ -278,60 +279,55 @@ private void setupRealm() { clientSecret, oauthScopes ); - + j.jenkins.setSecurityRealm(githubSecurityRealm); } - - @After - public void stopEmbeddedJettyServer() { - try { - server.stop(); - } catch (Exception e) { - e.printStackTrace(); - } + + @AfterEach + void stopEmbeddedJettyServer() { + server.stop(1); } - + // all the code above is reused from GithubAccessTokenPropertyTest - + @Issue("SECURITY-797") @Test - public void preventSessionFixation() throws Exception { + 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.currentLogin = "alice"; 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); + 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(StaplerRequest request) { + + public HttpResponse doIndex(StaplerRequest2 request) { HttpSession session = request.getSession(false); if (session == null) { sessionId = null; @@ -341,20 +337,20 @@ public HttpResponse doIndex(StaplerRequest request) { 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"))); diff --git a/src/test/java/org/jenkinsci/plugins/GithubAccessTokenPropertyTest.java b/src/test/java/org/jenkinsci/plugins/GithubAccessTokenPropertyTest.java index 8054eb6e..a33886ac 100644 --- a/src/test/java/org/jenkinsci/plugins/GithubAccessTokenPropertyTest.java +++ b/src/test/java/org/jenkinsci/plugins/GithubAccessTokenPropertyTest.java @@ -23,31 +23,33 @@ */ package org.jenkinsci.plugins; -import com.gargoylesoftware.htmlunit.Page; -import com.gargoylesoftware.htmlunit.WebRequest; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; import hudson.model.User; import hudson.util.Scrambler; -import java.util.Collections; import jenkins.security.ApiTokenProperty; import net.sf.json.JSONArray; import net.sf.json.JSONObject; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.servlet.DefaultServlet; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; +import org.eclipse.jetty.util.Fields; +import org.eclipse.jetty.util.UrlEncoded; +import org.htmlunit.Page; +import org.htmlunit.WebRequest; +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 javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.URI; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -57,42 +59,27 @@ import java.util.Map; import java.util.Set; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -public class GithubAccessTokenPropertyTest { +@WithJenkins +class GithubAccessTokenPropertyTest { - @Rule - public JenkinsRule j = new JenkinsRule(); + private JenkinsRule j; private JenkinsRule.WebClient wc; - private Server server; + private HttpServer 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); - + private void setupMockGithubServer() throws Exception { + server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0); servlet = new MockGithubServlet(j); - - ServletContextHandler context = new ServletContextHandler(); - ServletHolder servletHolder = new ServletHolder("default", servlet); - context.addServlet(servletHolder, "/*"); - server.setHandler(context); - + server.createContext("/", servlet); 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)); + InetSocketAddress address = server.getAddress(); + serverUri = new URI(String.format("http://%s:%d/", address.getHostString(), address.getPort())); servlet.setServerUrl(serverUri); } @@ -102,12 +89,12 @@ public void setupMockGithubServer() throws Exception { * https://developer.github.com/v3/orgs/ * https://developer.github.com/v3/orgs/teams/ */ - private static class MockGithubServlet extends DefaultServlet { + private static class MockGithubServlet implements HttpHandler { private String currentLogin; private List organizations; private List> teams; - private JenkinsRule jenkinsRule; + private final JenkinsRule jenkinsRule; private URI serverUri; public MockGithubServlet(JenkinsRule jenkinsRule) { @@ -118,57 +105,59 @@ public void setServerUrl(URI serverUri) { this.serverUri = serverUri; } - @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { - switch (req.getRequestURI()) { + @Override + public void handle(HttpExchange he) throws IOException { + switch (he.getRequestURI().getPath()) { case "/user": - this.onUser(req, resp); + this.onUser(he); break; case "/users/_specific_login_": - this.onUser(req, resp); + this.onUser(he); break; case "/user/orgs": - this.onUserOrgs(req, resp); + this.onUserOrgs(he); break; case "/user/teams": - this.onUserTeams(req, resp); + this.onUserTeams(he); break; case "/orgs/org-a": - this.onOrgs(req, resp, "org-a"); + this.onOrgs(he, "org-a"); break; case "/orgs/org-a/teams": - this.onOrgsTeam(req, resp, "org-a"); + this.onOrgsTeam(he, "org-a"); break; case "/orgs/org-a/members/alice": - this.onOrgsMember(req, resp, "org-a", "alice"); + this.onOrgsMember(he, "org-a", "alice"); break; case "/teams/7/members/alice": - this.onTeamMember(req, resp, "team-b", "alice"); + this.onTeamMember(he, "team-b", "alice"); break; case "/orgs/org-c": - this.onOrgs(req, resp, "org-c"); + this.onOrgs(he, "org-c"); break; case "/orgs/org-c/teams": - this.onOrgsTeam(req, resp, "org-c"); + this.onOrgsTeam(he, "org-c"); break; case "/orgs/org-c/members/bob": - this.onOrgsMember(req, resp, "org-c", "bob"); + this.onOrgsMember(he, "org-c", "bob"); break; case "/teams/7/members/bob": - this.onTeamMember(req, resp, "team-d", "bob"); + this.onTeamMember(he, "team-d", "bob"); break; case "/login/oauth/authorize": - this.onLoginOAuthAuthorize(req, resp); + this.onLoginOAuthAuthorize(he); break; case "/login/oauth/access_token": - this.onLoginOAuthAccessToken(req, resp); + this.onLoginOAuthAccessToken(he); break; default: - throw new RuntimeException("Url not mapped yet: " + req.getRequestURI()); + throw new RuntimeException("Url not mapped yet: " + he.getRequestURI().getPath()); } + he.close(); } - private void onUser(HttpServletRequest req, HttpServletResponse resp) throws IOException { - resp.getWriter().write(JSONObject.fromObject( + private void onUser(HttpExchange he) throws IOException { + sendResponse(he, JSONObject.fromObject( new HashMap() {{ put("login", currentLogin); put("name", currentLogin + "_name"); @@ -179,42 +168,40 @@ private void onUser(HttpServletRequest req, HttpServletResponse resp) throws IOE ).toString()); } - private void onUserOrgs(HttpServletRequest req, HttpServletResponse resp) throws IOException { + private void onUserOrgs(HttpExchange he) throws IOException { List> responseBody = new ArrayList<>(); for (String orgName : organizations) { final String orgName_ = orgName; - responseBody.add(new HashMap() {{ + responseBody.add(new HashMap<>() {{ put("login", orgName_); }}); } - - resp.getWriter().write(JSONArray.fromObject(responseBody).toString()); + sendResponse(he, JSONArray.fromObject(responseBody).toString()); } - private void onOrgs(HttpServletRequest req, HttpServletResponse resp, final String orgName) throws IOException { + private void onOrgs(HttpExchange he, final String orgName) throws IOException { Map responseBody = new HashMap() {{ put("login", orgName); }}; - - resp.getWriter().write(JSONObject.fromObject(responseBody).toString()); + sendResponse(he, JSONObject.fromObject(responseBody).toString()); } - private void onOrgsMember(HttpServletRequest req, HttpServletResponse resp, String orgName, String userName) { - resp.setStatus(HttpServletResponse.SC_NO_CONTENT); + private void onOrgsMember(HttpExchange he, String orgName, String userName) throws IOException { + he.sendResponseHeaders(HttpURLConnection.HTTP_NO_CONTENT, -1); // 302 / 404 responses not implemented } - private void onTeamMember(HttpServletRequest req, HttpServletResponse resp, String orgName, String userName) { - resp.setStatus(HttpServletResponse.SC_NO_CONTENT); + private void onTeamMember(HttpExchange he, String orgName, String userName) throws IOException { + he.sendResponseHeaders(HttpURLConnection.HTTP_NO_CONTENT, -1); // 302 / 404 responses not implemented } - private void onOrgsTeam(HttpServletRequest req, HttpServletResponse resp, final String orgName) throws IOException { + private void onOrgsTeam(HttpExchange he, 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() {{ + responseBody.add(new HashMap<>() {{ put("id", 7); put("login", teamName_ + "_login"); put("name", teamName_); @@ -224,16 +211,15 @@ private void onOrgsTeam(HttpServletRequest req, HttpServletResponse resp, final }}); }}); } - - resp.getWriter().write(JSONArray.fromObject(responseBody).toString()); + sendResponse(he, JSONArray.fromObject(responseBody).toString()); } - private void onUserTeams(HttpServletRequest req, HttpServletResponse resp) throws IOException { + private void onUserTeams(HttpExchange he) 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() {{ + responseBody.add(new HashMap<>() {{ put("login", teamName_ + "_login"); put("name", teamName_); put("slug", slug); @@ -243,29 +229,41 @@ private void onUserTeams(HttpServletRequest req, HttpServletResponse resp) throw }}); } - resp.getWriter().write(JSONArray.fromObject(responseBody).toString()); + sendResponse(he, JSONArray.fromObject(responseBody).toString()); } - private void onLoginOAuthAuthorize(HttpServletRequest req, HttpServletResponse resp) throws IOException { + private void onLoginOAuthAuthorize(HttpExchange he) throws IOException { String code = "test"; - String state = req.getParameter("state"); - resp.sendRedirect(jenkinsRule.getURL() + "securityRealm/finishLogin?code=" + code + "&state=" + state); + Fields fields = new Fields(); + UrlEncoded.decodeUtf8To(he.getRequestURI().getQuery(), fields); + String state = fields.getValue("state"); + he.getResponseHeaders().set("Location", jenkinsRule.getURL() + "securityRealm/finishLogin?code=" + code + "&state=" + state); + he.sendResponseHeaders(302, -1); } - private void onLoginOAuthAccessToken(HttpServletRequest req, HttpServletResponse resp) throws IOException { - resp.getWriter().write("access_token=RANDOM_ACCESS_TOKEN"); + private void onLoginOAuthAccessToken(HttpExchange he) throws IOException { + sendResponse(he, "access_token=RANDOM_ACCESS_TOKEN"); + } + + private void sendResponse(HttpExchange he, String response) throws IOException { + byte[] body = response.getBytes(StandardCharsets.UTF_8); + he.sendResponseHeaders(HttpURLConnection.HTTP_OK, body.length); + try (OutputStream os = he.getResponseBody()) { + os.write(body); + } } } - @Before - public void prepareRealmAndWebClient() throws Exception { + @BeforeEach + void prepareRealmAndWebClient(JenkinsRule j) throws Exception { + this.j = j; this.setupMockGithubServer(); this.setupRealm(); wc = j.createWebClient(); GithubAuthenticationToken.clearCaches(); } - private void setupRealm(){ + private void setupRealm() { String githubWebUri = serverUri.toString(); String githubApiUri = serverUri.toString(); String clientID = "xxx"; @@ -283,18 +281,14 @@ private void setupRealm(){ j.jenkins.setSecurityRealm(githubSecurityRealm); } - @After - public void stopEmbeddedJettyServer() { - try { - server.stop(); - } catch (Exception e) { - e.printStackTrace(); - } + @AfterEach + void stopEmbeddedJettyServer() { + server.stop(1); } @Issue("JENKINS-47113") @Test - public void testUsingGithubToken() throws IOException { + void testUsingGithubToken() throws IOException { String aliceLogin = "alice"; servlet.currentLogin = aliceLogin; servlet.organizations = Collections.singletonList("org-a"); @@ -325,7 +319,7 @@ public void testUsingGithubToken() throws IOException { @Issue("JENKINS-47113") @Test - public void testUsingGithubLogin() throws IOException { + void testUsingGithubLogin() throws IOException { String bobLogin = "bob"; servlet.currentLogin = bobLogin; servlet.organizations = Collections.singletonList("org-c"); @@ -354,7 +348,7 @@ public void testUsingGithubLogin() throws IOException { @Issue("JENKINS-60200") @Test - public void testInvalidateAuthorizationCacheOnFreshLogin() throws IOException { + void testInvalidateAuthorizationCacheOnFreshLogin() throws IOException { String bobLogin = "bob"; servlet.currentLogin = bobLogin; servlet.organizations = Collections.singletonList("org-c"); @@ -383,7 +377,6 @@ public void testInvalidateAuthorizationCacheOnFreshLogin() throws IOException { 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); @@ -405,7 +398,7 @@ private void makeRequestUsingOAuth(String expectedLogin, List expectedAu assertResponse(p, expectedLogin, expectedAuthorities); } - private void assertResponse(Page p, String expectedLogin, List 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) { @@ -423,11 +416,11 @@ private void assertResponse(Page p, String expectedLogin, List expectedA Set expectedAuthoritiesSet = new HashSet<>(expectedAuthorities); - assertEquals(String.format("They do not have the same content, expected=%s, actual=%s", expectedAuthorities, actualAuthorities), expectedAuthoritiesSet, actualAuthorities); + assertEquals(expectedAuthoritiesSet, actualAuthorities, String.format("They do not have the same content, expected=%s, actual=%s", expectedAuthorities, actualAuthorities)); } } - private String encodeBasic(String login, String credentials) { + 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 69390900..1051e7b1 100644 --- a/src/test/java/org/jenkinsci/plugins/GithubAuthenticationTokenTest.java +++ b/src/test/java/org/jenkinsci/plugins/GithubAuthenticationTokenTest.java @@ -2,9 +2,9 @@ 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; @@ -13,27 +13,21 @@ import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; import java.io.IOException; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -public class GithubAuthenticationTokenTest { +@ExtendWith(MockitoExtension.class) +class GithubAuthenticationTokenTest { - @Mock + @Mock(strictness = Mock.Strictness.LENIENT) private GithubSecurityRealm securityRealm; - private AutoCloseable closeable; - - @Before - public void setUp() { - closeable = MockitoAnnotations.openMocks(this); - } - - @After - public void tearDown() throws Exception { - closeable.close(); + @AfterEach + void tearDown() { + GithubAuthenticationToken.clearCaches(); } private void mockJenkins(MockedStatic mockedJenkins) { @@ -44,7 +38,7 @@ private void mockJenkins(MockedStatic mockedJenkins) { } @Test - public void testTokenSerialization() throws IOException { + void testTokenSerialization() throws IOException { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { mockJenkins(mockedJenkins); @@ -59,12 +53,7 @@ public void testTokenSerialization() throws IOException { } } - @After - public void after() { - GithubAuthenticationToken.clearCaches(); - } - - private GHMyself mockGHMyselfAs(MockedStatic mockedGitHubBuilder, String username) throws IOException { + 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); @@ -78,5 +67,4 @@ private GHMyself mockGHMyselfAs(MockedStatic mockedGitHubBuilder, 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..7960cba9 100644 --- a/src/test/java/org/jenkinsci/plugins/GithubAuthorizationStrategyTest.java +++ b/src/test/java/org/jenkinsci/plugins/GithubAuthorizationStrategyTest.java @@ -24,20 +24,22 @@ of this software and associated documentation files (the "Software"), to deal package org.jenkinsci.plugins; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import org.junit.jupiter.api.Test; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +class GithubAuthorizationStrategyTest { -public class GithubAuthorizationStrategyTest { @Test - public void testEquals_true() { + 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() { + 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 6d83cf64..a5f42969 100644 --- a/src/test/java/org/jenkinsci/plugins/GithubLogoutActionTest.java +++ b/src/test/java/org/jenkinsci/plugins/GithubLogoutActionTest.java @@ -25,35 +25,23 @@ of this software and associated documentation files (the "Software"), to deal package org.jenkinsci.plugins; import jenkins.model.Jenkins; -import junit.framework.TestCase; -import org.jenkinsci.plugins.GithubSecurityRealm.DescriptorImpl; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +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.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; -public class GithubLogoutActionTest extends TestCase { +import static org.junit.jupiter.api.Assertions.assertEquals; + +@ExtendWith(MockitoExtension.class) +class GithubLogoutActionTest { @Mock private GithubSecurityRealm securityRealm; @Mock - private DescriptorImpl descriptor; - - private AutoCloseable closeable; - - @Before - public void setUp() { - closeable = MockitoAnnotations.openMocks(this); - } - - @After - public void tearDown() throws Exception { - closeable.close(); - } + private GithubSecurityRealm.DescriptorImpl descriptor; private void mockJenkins(MockedStatic mockedJenkins) { Jenkins jenkins = Mockito.mock(Jenkins.class); @@ -68,7 +56,7 @@ private void mockGithubSecurityRealmWebUriFor(String host) { } @Test - public void testGetGitHubText_gh() { + void testGetGitHubText_gh() { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class)) { mockJenkins(mockedJenkins); mockGithubSecurityRealmWebUriFor("https://github.com"); @@ -78,7 +66,7 @@ public void testGetGitHubText_gh() { } @Test - public void testGetGitHubText_ghe() { + void testGetGitHubText_ghe() { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class)) { mockJenkins(mockedJenkins); mockGithubSecurityRealmWebUriFor("https://ghe.example.com"); diff --git a/src/test/java/org/jenkinsci/plugins/GithubRequireOrganizationMembershipACLTest.java b/src/test/java/org/jenkinsci/plugins/GithubRequireOrganizationMembershipACLTest.java index c0e99bec..128e8a8e 100644 --- a/src/test/java/org/jenkinsci/plugins/GithubRequireOrganizationMembershipACLTest.java +++ b/src/test/java/org/jenkinsci/plugins/GithubRequireOrganizationMembershipACLTest.java @@ -26,20 +26,28 @@ of this software and associated documentation files (the "Software"), to deal */ package org.jenkinsci.plugins; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.acegisecurity.Authentication; -import org.acegisecurity.GrantedAuthority; -import org.acegisecurity.GrantedAuthorityImpl; -import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; +import hudson.model.Computer; +import hudson.model.Hudson; +import hudson.model.Item; +import hudson.model.Messages; +import hudson.model.Project; +import hudson.plugins.git.GitSCM; +import hudson.plugins.git.UserRemoteConfig; +import hudson.scm.NullSCM; +import hudson.security.Permission; +import hudson.security.PermissionScope; +import jenkins.branch.Branch; +import jenkins.branch.MultiBranchProject; +import jenkins.model.Jenkins; +import jenkins.scm.api.SCMSource; import org.jenkinsci.plugins.github_branch_source.GitHubSCMSource; 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; @@ -49,36 +57,31 @@ of this software and associated documentation files (the "Software"), to deal import org.kohsuke.github.RateLimitHandler; import org.kohsuke.github.extras.okhttp3.OkHttpGitHubConnector; import org.kohsuke.stapler.Stapler; -import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerRequest2; 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 hudson.model.Hudson; -import hudson.model.Item; -import hudson.model.Messages; -import hudson.model.Project; -import hudson.plugins.git.GitSCM; -import hudson.plugins.git.UserRemoteConfig; -import hudson.scm.NullSCM; -import hudson.security.Permission; -import hudson.security.PermissionScope; -import jenkins.branch.Branch; -import jenkins.branch.MultiBranchProject; -import jenkins.model.Jenkins; -import jenkins.scm.api.SCMSource; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** - * * @author alex */ -public class GithubRequireOrganizationMembershipACLTest { +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +class GithubRequireOrganizationMembershipACLTest { private GitHub gh; @@ -93,10 +96,8 @@ public class GithubRequireOrganizationMembershipACLTest { private boolean allowAnonymousWebhookPermission; private boolean allowAnonymousCCTrayPermission; - private AutoCloseable closeable; - - @Before - public void setUp() { + @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; @@ -106,8 +107,6 @@ public void setUp() { 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); @@ -125,12 +124,12 @@ private void mockJenkins(MockedStatic mockedJenkins) { Messages._Item_READ_description(), Permission.READ, PermissionScope.ITEM); - private final Authentication ANONYMOUS_USER = new AnonymousAuthenticationToken("anonymous", + private static final Authentication ANONYMOUS_USER = new AnonymousAuthenticationToken("anonymous", "anonymous", - new GrantedAuthority[]{new GrantedAuthorityImpl("anonymous")}); + List.of(new SimpleGrantedAuthority("anonymous"))); private GithubRequireOrganizationMembershipACL createACL() { - return new GithubRequireOrganizationMembershipACL( + GithubRequireOrganizationMembershipACL acl = new GithubRequireOrganizationMembershipACL( "admin", "myOrg", authenticatedUserReadPermission, @@ -140,6 +139,8 @@ private GithubRequireOrganizationMembershipACL createACL() { allowAnonymousCCTrayPermission, allowAnonymousReadPermission, allowAnonymousJobStatusPermission); + acl.setAgentUserName("agent"); + return acl; } private GithubRequireOrganizationMembershipACL aclForProject(Project project) { @@ -172,7 +173,7 @@ private GHMyself mockGHMyselfAs(MockedStatic mockedGitHubBuilder, // TODO: Add ability to set list of orgs user belongs to to check whitelisting! - private void mockReposFor(GHPerson person, List repositories) { + private static 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); @@ -193,7 +194,7 @@ private GHRepository mockPublicRepository(String repositoryName) throws IOExcept return mockRepository(repositoryName, true, false, false, false); } - private Project mockProject(String url) { + private static Project mockProject(String url) { Project project = Mockito.mock(Project.class); GitSCM gitSCM = Mockito.mock(GitSCM.class); UserRemoteConfig userRemoteConfig = Mockito.mock(UserRemoteConfig.class); @@ -204,7 +205,7 @@ private Project mockProject(String url) { return project; } - private WorkflowJob mockWorkflowJob(String url) { + private static WorkflowJob mockWorkflowJob(String url) { WorkflowJob project = Mockito.mock(WorkflowJob.class); GitSCM gitSCM = Mockito.mock(GitSCM.class); Branch branch = Mockito.mock(Branch.class); @@ -219,7 +220,7 @@ private WorkflowJob mockWorkflowJob(String url) { return project; } - private MultiBranchProject mockMultiBranchProject(String url) { + private static MultiBranchProject mockMultiBranchProject(String url) { WorkflowMultiBranchProject multiBranchProject = Mockito.mock(WorkflowMultiBranchProject.class); GitHubSCMSource gitHubSCM = Mockito.mock(GitHubSCMSource.class); ArrayList scmSources = new ArrayList<>(); @@ -229,15 +230,14 @@ private MultiBranchProject mockMultiBranchProject(String url) { return multiBranchProject; } - @After - public void tearDown() throws Exception { - closeable.close(); + @AfterEach + void tearDown() { gh = null; GithubAuthenticationToken.clearCaches(); } @Test - public void testCanReadAndBuildOneOfMyPrivateRepositories() throws IOException { + void testCanReadAndBuildOneOfMyPrivateRepositories() throws IOException { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { mockJenkins(mockedJenkins); @@ -253,20 +253,20 @@ public void testCanReadAndBuildOneOfMyPrivateRepositories() throws IOException { GithubRequireOrganizationMembershipACL projectAcl = aclForProject(mockProject); GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - assertTrue(projectAcl.hasPermission(authenticationToken, Item.READ)); - assertTrue(projectAcl.hasPermission(authenticationToken, Item.DISCOVER)); - assertTrue(projectAcl.hasPermission(authenticationToken, Item.BUILD)); - assertTrue(workflowJobAcl.hasPermission(authenticationToken, Item.READ)); - assertTrue(workflowJobAcl.hasPermission(authenticationToken, Item.DISCOVER)); - assertTrue(workflowJobAcl.hasPermission(authenticationToken, Item.BUILD)); - assertTrue(multiBranchProjectAcl.hasPermission(authenticationToken, Item.READ)); - assertTrue(multiBranchProjectAcl.hasPermission(authenticationToken, Item.DISCOVER)); - assertTrue(multiBranchProjectAcl.hasPermission(authenticationToken, Item.BUILD)); + 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 { + void testCanReadAndBuildAPublicRepository() throws IOException { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { mockJenkins(mockedJenkins); @@ -281,20 +281,20 @@ public void testCanReadAndBuildAPublicRepository() throws IOException { GithubRequireOrganizationMembershipACL projectAcl = aclForProject(mockProject); GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - assertTrue(projectAcl.hasPermission(authenticationToken, Item.READ)); - assertTrue(projectAcl.hasPermission(authenticationToken, Item.DISCOVER)); - assertTrue(projectAcl.hasPermission(authenticationToken, Item.BUILD)); - assertTrue(workflowJobAcl.hasPermission(authenticationToken, Item.READ)); - assertTrue(workflowJobAcl.hasPermission(authenticationToken, Item.DISCOVER)); - assertTrue(workflowJobAcl.hasPermission(authenticationToken, Item.BUILD)); - assertTrue(multiBranchProjectAcl.hasPermission(authenticationToken, Item.READ)); - assertTrue(multiBranchProjectAcl.hasPermission(authenticationToken, Item.DISCOVER)); - assertTrue(multiBranchProjectAcl.hasPermission(authenticationToken, Item.BUILD)); + 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 { + void testCanReadAndBuildPrivateRepositoryIHavePullRightsOn() throws IOException { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { mockJenkins(mockedJenkins); @@ -312,20 +312,20 @@ public void testCanReadAndBuildPrivateRepositoryIHavePullRightsOn() throws IOExc GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - assertTrue(projectAcl.hasPermission(authenticationToken, Item.READ)); - assertTrue(projectAcl.hasPermission(authenticationToken, Item.DISCOVER)); - assertTrue(projectAcl.hasPermission(authenticationToken, Item.BUILD)); - assertTrue(workflowJobAcl.hasPermission(authenticationToken, Item.READ)); - assertTrue(workflowJobAcl.hasPermission(authenticationToken, Item.DISCOVER)); - assertTrue(workflowJobAcl.hasPermission(authenticationToken, Item.BUILD)); - assertTrue(multiBranchProjectAcl.hasPermission(authenticationToken, Item.READ)); - assertTrue(multiBranchProjectAcl.hasPermission(authenticationToken, Item.DISCOVER)); - assertTrue(multiBranchProjectAcl.hasPermission(authenticationToken, Item.BUILD)); + 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 { + void testCanNotReadOrBuildRepositoryIDoNotCollaborateOn() throws IOException { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { mockJenkins(mockedJenkins); @@ -341,20 +341,20 @@ public void testCanNotReadOrBuildRepositoryIDoNotCollaborateOn() throws IOExcept GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - assertFalse(projectAcl.hasPermission(authenticationToken, Item.READ)); - assertFalse(projectAcl.hasPermission(authenticationToken, Item.DISCOVER)); - assertFalse(projectAcl.hasPermission(authenticationToken, Item.BUILD)); - assertFalse(multiBranchProjectAcl.hasPermission(authenticationToken, Item.READ)); - assertFalse(multiBranchProjectAcl.hasPermission(authenticationToken, Item.DISCOVER)); - assertFalse(multiBranchProjectAcl.hasPermission(authenticationToken, Item.BUILD)); - assertFalse(workflowJobAcl.hasPermission(authenticationToken, Item.READ)); - assertFalse(workflowJobAcl.hasPermission(authenticationToken, Item.DISCOVER)); - assertFalse(workflowJobAcl.hasPermission(authenticationToken, Item.BUILD)); + 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 { + void testNotGrantedBuildWhenNotUsingGitSCM() throws IOException { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { mockJenkins(mockedJenkins); @@ -366,13 +366,13 @@ public void testNotGrantedBuildWhenNotUsingGitSCM() throws IOException { GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - assertFalse(acl.hasPermission(authenticationToken, Item.READ)); - assertFalse(acl.hasPermission(authenticationToken, Item.DISCOVER)); + assertFalse(acl.hasPermission2(authenticationToken, Item.READ)); + assertFalse(acl.hasPermission2(authenticationToken, Item.DISCOVER)); } } @Test - public void testNotGrantedBuildWhenRepositoryIsEmpty() throws IOException { + void testNotGrantedBuildWhenRepositoryIsEmpty() throws IOException { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { mockJenkins(mockedJenkins); @@ -382,13 +382,13 @@ public void testNotGrantedBuildWhenRepositoryIsEmpty() throws IOException { GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - assertFalse(acl.hasPermission(authenticationToken, Item.READ)); - assertFalse(acl.hasPermission(authenticationToken, Item.DISCOVER)); + assertFalse(acl.hasPermission2(authenticationToken, Item.READ)); + assertFalse(acl.hasPermission2(authenticationToken, Item.DISCOVER)); } } @Test - public void testNotGrantedReadWhenRepositoryUrlIsEmpty() throws IOException { + void testNotGrantedReadWhenRepositoryUrlIsEmpty() throws IOException { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { mockJenkins(mockedJenkins); @@ -404,13 +404,13 @@ public void testNotGrantedReadWhenRepositoryUrlIsEmpty() throws IOException { GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - assertFalse(acl.hasPermission(authenticationToken, Item.READ)); - assertFalse(acl.hasPermission(authenticationToken, Item.DISCOVER)); + assertFalse(acl.hasPermission2(authenticationToken, Item.READ)); + assertFalse(acl.hasPermission2(authenticationToken, Item.DISCOVER)); } } @Test - public void testGlobalReadAvailableDueToAuthenticatedUserReadPermission() throws IOException { + void testGlobalReadAvailableDueToAuthenticatedUserReadPermission() throws IOException { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { mockJenkins(mockedJenkins); @@ -421,12 +421,12 @@ public void testGlobalReadAvailableDueToAuthenticatedUserReadPermission() throws GithubRequireOrganizationMembershipACL acl = createACL(); GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - assertTrue(acl.hasPermission(authenticationToken, Hudson.READ)); + assertTrue(acl.hasPermission2(authenticationToken, Hudson.READ)); } } @Test - public void testWithoutUseRepositoryPermissionsSetCanReadDueToAuthenticatedUserReadPermission() throws IOException { + void testWithoutUseRepositoryPermissionsSetCanReadDueToAuthenticatedUserReadPermission() throws IOException { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { mockJenkins(mockedJenkins); @@ -437,12 +437,12 @@ public void testWithoutUseRepositoryPermissionsSetCanReadDueToAuthenticatedUserR GithubRequireOrganizationMembershipACL acl = createACL(); GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - assertTrue(acl.hasPermission(authenticationToken, Item.READ)); + assertTrue(acl.hasPermission2(authenticationToken, Item.READ)); } } @Test - public void testWithoutUseRepositoryPermissionsSetCannotReadWithoutAuthenticatedUserReadPermission() throws IOException { + void testWithoutUseRepositoryPermissionsSetCannotReadWithoutAuthenticatedUserReadPermission() throws IOException { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { mockJenkins(mockedJenkins); @@ -453,12 +453,12 @@ public void testWithoutUseRepositoryPermissionsSetCannotReadWithoutAuthenticated GithubRequireOrganizationMembershipACL acl = createACL(); GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - assertFalse(acl.hasPermission(authenticationToken, Item.READ)); + assertFalse(acl.hasPermission2(authenticationToken, Item.READ)); } } @Test - public void testUsersCannotCreateWithoutConfigurationEnabledPermission() throws IOException { + void testUsersCannotCreateWithoutConfigurationEnabledPermission() throws IOException { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { mockJenkins(mockedJenkins); @@ -468,12 +468,12 @@ public void testUsersCannotCreateWithoutConfigurationEnabledPermission() throws GithubRequireOrganizationMembershipACL acl = createACL(); GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - assertFalse(acl.hasPermission(authenticationToken, Item.CREATE)); + assertFalse(acl.hasPermission2(authenticationToken, Item.CREATE)); } } @Test - public void testUsersCanCreateWithConfigurationEnabledPermission() throws IOException { + void testUsersCanCreateWithConfigurationEnabledPermission() throws IOException { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { mockJenkins(mockedJenkins); @@ -483,12 +483,12 @@ public void testUsersCanCreateWithConfigurationEnabledPermission() throws IOExce GithubRequireOrganizationMembershipACL acl = createACL(); GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - assertTrue(acl.hasPermission(authenticationToken, Item.CREATE)); + assertTrue(acl.hasPermission2(authenticationToken, Item.CREATE)); } } @Test - public void testCanReadAProjectWithAuthenticatedUserReadPermission() throws IOException { + void testCanReadAProjectWithAuthenticatedUserReadPermission() throws IOException { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { mockJenkins(mockedJenkins); @@ -501,19 +501,19 @@ public void testCanReadAProjectWithAuthenticatedUserReadPermission() throws IOEx GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); // Gives the user rights to see the project - assertTrue(acl.hasPermission(authenticationToken, Item.READ)); - assertTrue(acl.hasPermission(authenticationToken, Item.DISCOVER)); + assertTrue(acl.hasPermission2(authenticationToken, Item.READ)); + assertTrue(acl.hasPermission2(authenticationToken, Item.DISCOVER)); // but not to build, cancel, configure, view configuration, delete it - assertFalse(acl.hasPermission(authenticationToken, Item.BUILD)); - assertFalse(acl.hasPermission(authenticationToken, Item.CONFIGURE)); - assertFalse(acl.hasPermission(authenticationToken, Item.DELETE)); - assertFalse(acl.hasPermission(authenticationToken, Item.EXTENDED_READ)); - assertFalse(acl.hasPermission(authenticationToken, Item.CANCEL)); + 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 { + void testCannotReadAProjectWithoutAuthenticatedUserReadPermission() throws IOException { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { mockJenkins(mockedJenkins); @@ -525,18 +525,18 @@ public void testCannotReadAProjectWithoutAuthenticatedUserReadPermission() throw GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - assertFalse(acl.hasPermission(authenticationToken, Item.READ)); - assertFalse(acl.hasPermission(authenticationToken, Item.DISCOVER)); - assertFalse(acl.hasPermission(authenticationToken, Item.BUILD)); - assertFalse(acl.hasPermission(authenticationToken, Item.CONFIGURE)); - assertFalse(acl.hasPermission(authenticationToken, Item.DELETE)); - assertFalse(acl.hasPermission(authenticationToken, Item.EXTENDED_READ)); - assertFalse(acl.hasPermission(authenticationToken, Item.CANCEL)); + 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 { + void testCannotReadRepositoryWithInvalidRepoUrl() throws IOException { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); MockedStatic mockedGitHubBuilder = Mockito.mockStatic(GitHubBuilder.class)) { mockJenkins(mockedJenkins); @@ -550,112 +550,135 @@ public void testCannotReadRepositoryWithInvalidRepoUrl() throws IOException { GithubAuthenticationToken authenticationToken = new GithubAuthenticationToken("accessToken", "https://api.github.com"); - assertFalse(acl.hasPermission(authenticationToken, Item.READ)); + assertFalse(acl.hasPermission2(authenticationToken, Item.READ)); } } @Test - public void testAnonymousCanViewJobStatusWhenGranted() { + 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.hasPermission(ANONYMOUS_USER, VIEW_JOBSTATUS_PERMISSION)); + assertTrue(acl.hasPermission2(ANONYMOUS_USER, VIEW_JOBSTATUS_PERMISSION)); } @Test - public void testAnonymousCannotViewJobStatusWhenNotGranted() { + void testAnonymousCannotViewJobStatusWhenNotGranted() { this.allowAnonymousJobStatusPermission = false; Project mockProject = mockProject("https://github.com/some-org/a-public-repo.git"); GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); - assertFalse(acl.hasPermission(ANONYMOUS_USER, VIEW_JOBSTATUS_PERMISSION)); + assertFalse(acl.hasPermission2(ANONYMOUS_USER, VIEW_JOBSTATUS_PERMISSION)); } @Test - public void testAnonymousCanReachWebhookWhenGranted() { + void testAnonymousCanReachWebhookWhenGranted() { try (MockedStatic mockedJenkins = Mockito.mockStatic(Jenkins.class); MockedStatic mockedStapler = Mockito.mockStatic(Stapler.class)) { mockJenkins(mockedJenkins); this.allowAnonymousWebhookPermission = true; - StaplerRequest currentRequest = Mockito.mock(StaplerRequest.class); - mockedStapler.when(Stapler::getCurrentRequest).thenReturn(currentRequest); + 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.hasPermission(ANONYMOUS_USER, Item.READ)); + assertTrue(acl.hasPermission2(ANONYMOUS_USER, Item.READ)); } } @Test - public void testAnonymousCannotReachWebhookIfNotGranted() { + void testAnonymousCannotReachWebhookIfNotGranted() { try (MockedStatic mockedStapler = Mockito.mockStatic(Stapler.class)) { this.allowAnonymousWebhookPermission = false; - StaplerRequest currentRequest = Mockito.mock(StaplerRequest.class); - mockedStapler.when(Stapler::getCurrentRequest).thenReturn(currentRequest); + 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.hasPermission(ANONYMOUS_USER, Item.READ)); + assertFalse(acl.hasPermission2(ANONYMOUS_USER, Item.READ)); } } @Test - public void testAnonymousCanReadAndDiscoverWhenGranted() { + void testAnonymousCanReadAndDiscoverWhenGranted() { this.allowAnonymousReadPermission = true; Project mockProject = mockProject("https://github.com/some-org/a-public-repo.git"); GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); - assertTrue(acl.hasPermission(ANONYMOUS_USER, Item.READ)); - assertTrue(acl.hasPermission(ANONYMOUS_USER, Item.DISCOVER)); + assertTrue(acl.hasPermission2(ANONYMOUS_USER, Item.READ)); + assertTrue(acl.hasPermission2(ANONYMOUS_USER, Item.DISCOVER)); } @Test - public void testAnonymousCantReadAndDiscoverWhenNotGranted() { + void testAnonymousCantReadAndDiscoverWhenNotGranted() { this.allowAnonymousReadPermission = false; Project mockProject = mockProject("https://github.com/some-org/a-public-repo.git"); GithubRequireOrganizationMembershipACL acl = aclForProject(mockProject); - assertFalse(acl.hasPermission(ANONYMOUS_USER, Item.READ)); - assertFalse(acl.hasPermission(ANONYMOUS_USER, Item.DISCOVER)); + assertFalse(acl.hasPermission2(ANONYMOUS_USER, Item.READ)); + assertFalse(acl.hasPermission2(ANONYMOUS_USER, Item.DISCOVER)); } @Test - public void testAnonymousCanReachCCTrayWhenGranted() { + void testAnonymousCanReachCCTrayWhenGranted() { try (MockedStatic mockedStapler = Mockito.mockStatic(Stapler.class)) { this.allowAnonymousCCTrayPermission = true; - StaplerRequest currentRequest = Mockito.mock(StaplerRequest.class); - mockedStapler.when(Stapler::getCurrentRequest).thenReturn(currentRequest); + StaplerRequest2 currentRequest = Mockito.mock(StaplerRequest2.class); + mockedStapler.when(Stapler::getCurrentRequest2).thenReturn(currentRequest); Mockito.when(currentRequest.getOriginalRequestURI()).thenReturn("https://www.jenkins.org/cc.xml"); GithubRequireOrganizationMembershipACL acl = createACL(); - assertTrue(acl.hasPermission(ANONYMOUS_USER, Item.READ)); + assertTrue(acl.hasPermission2(ANONYMOUS_USER, Item.READ)); } } @Test - public void testAnonymousCannotReachCCTrayIfNotGranted() { + void testAnonymousCannotReachCCTrayIfNotGranted() { try (MockedStatic mockedStapler = Mockito.mockStatic(Stapler.class)) { this.allowAnonymousCCTrayPermission = false; - StaplerRequest currentRequest = Mockito.mock(StaplerRequest.class); - mockedStapler.when(Stapler::getCurrentRequest).thenReturn(currentRequest); + StaplerRequest2 currentRequest = Mockito.mock(StaplerRequest2.class); + mockedStapler.when(Stapler::getCurrentRequest2).thenReturn(currentRequest); Mockito.when(currentRequest.getOriginalRequestURI()).thenReturn("https://www.jenkins.org/cc.xml"); GithubRequireOrganizationMembershipACL acl = createACL(); - assertFalse(acl.hasPermission(ANONYMOUS_USER, Item.READ)); + 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..8d87d509 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); + 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() { + private static User retrieveUser() { return User.getById("alice", false); } - private User retrieveOtherUser() { + private static User retrieveOtherUser() { return User.getById("bob", false); } @Test - public void correctBehaviorEvenAfterRestart() throws Throwable { + 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 e6cd935a..60f9ff09 100644 --- a/src/test/java/org/jenkinsci/plugins/GithubSecurityRealmTest.java +++ b/src/test/java/org/jenkinsci/plugins/GithubSecurityRealmTest.java @@ -24,31 +24,27 @@ of this software and associated documentation files (the "Software"), to deal package org.jenkinsci.plugins; -import org.jenkinsci.plugins.GithubSecurityRealm.DescriptorImpl; -import org.junit.ClassRule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.junit.jupiter.WithJenkins; -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 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; - -public class GithubSecurityRealmTest { - - @ClassRule - public final static JenkinsRule rule = new JenkinsRule(); +@WithJenkins +class GithubSecurityRealmTest { @Test - public void testEquals_true() { + 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 - public void testEquals_false() { + 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); @@ -56,7 +52,7 @@ public void testEquals_false() { } @Test - public void testHasScope_true() { + 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")); @@ -64,26 +60,26 @@ public void testHasScope_true() { } @Test - public void testHasScope_false() { + 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 - public void testDescriptorImplGetDefaultGithubWebUri() { - DescriptorImpl descriptor = new DescriptorImpl(); + void testDescriptorImplGetDefaultGithubWebUri(JenkinsRule rule) { + GithubSecurityRealm.DescriptorImpl descriptor = new GithubSecurityRealm.DescriptorImpl(); assertEquals("https://github.com", descriptor.getDefaultGithubWebUri()); } @Test - public void testDescriptorImplGetDefaultGithubApiUri() { - DescriptorImpl descriptor = new DescriptorImpl(); + void testDescriptorImplGetDefaultGithubApiUri(JenkinsRule rule) { + GithubSecurityRealm.DescriptorImpl descriptor = new GithubSecurityRealm.DescriptorImpl(); assertEquals("https://api.github.com", descriptor.getDefaultGithubApiUri()); } @Test - public void testDescriptorImplGetDefaultOauthScopes() { - DescriptorImpl descriptor = new DescriptorImpl(); + 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 41822462..d783c2d1 100644 --- a/src/test/java/org/jenkinsci/plugins/JenkinsProxyAuthenticatorTest.java +++ b/src/test/java/org/jenkinsci/plugins/JenkinsProxyAuthenticatorTest.java @@ -5,15 +5,15 @@ import okhttp3.Protocol; import okhttp3.Request; import okhttp3.Response; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; -public class JenkinsProxyAuthenticatorTest { - +class JenkinsProxyAuthenticatorTest { @Test - public void refusesChallengeIfAuthenticationAlreadyFailed() { + void refusesChallengeIfAuthenticationAlreadyFailed() { Request previousRequest = new Request.Builder() .url("https://example.com") @@ -28,11 +28,11 @@ public void refusesChallengeIfAuthenticationAlreadyFailed() { .message("Unauthorized") .build(); - Assert.assertNull(new JenkinsProxyAuthenticator(null).authenticate(null, response)); + assertNull(new JenkinsProxyAuthenticator(null).authenticate(null, response)); } @Test - public void refusesPreemptiveOkHttpChallenge() { + void refusesPreemptiveOkHttpChallenge() { Request previousRequest = new Request.Builder().url("https://example.com").build(); Response response = @@ -44,11 +44,11 @@ public void refusesPreemptiveOkHttpChallenge() { .message("Unauthorized") .build(); - Assert.assertNull(new JenkinsProxyAuthenticator(null).authenticate(null, response)); + assertNull(new JenkinsProxyAuthenticator(null).authenticate(null, response)); } @Test - public void acceptsBasicChallenge() { + void acceptsBasicChallenge() { Request previousRequest = new Request.Builder().url("https://example.com").build(); Response response = @@ -66,11 +66,11 @@ public void acceptsBasicChallenge() { Request requestWithBasicAuth = new JenkinsProxyAuthenticator(proxyConfiguration).authenticate(null, response); - Assert.assertEquals(requestWithBasicAuth.header("Proxy-Authorization"), credentials); + assertEquals(requestWithBasicAuth.header("Proxy-Authorization"), credentials); } @Test - public void refusesAnyChallengeWhichIsNotBasicAuthentication() { + void refusesAnyChallengeWhichIsNotBasicAuthentication() { Request previousRequest = new Request.Builder().url("https://example.com").build(); Response response = @@ -82,7 +82,6 @@ public void refusesAnyChallengeWhichIsNotBasicAuthentication() { .message("Unauthorized") .build(); - Assert.assertNull(new JenkinsProxyAuthenticator(null).authenticate(null, response)); + 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/GithubAPITest.java similarity index 81% rename from src/test/java/org/jenkinsci/plugins/api/GihubAPITest.java rename to src/test/java/org/jenkinsci/plugins/api/GithubAPITest.java index 508c23dd..e76d3374 100644 --- a/src/test/java/org/jenkinsci/plugins/api/GihubAPITest.java +++ b/src/test/java/org/jenkinsci/plugins/api/GithubAPITest.java @@ -26,52 +26,41 @@ of this software and associated documentation files (the "Software"), to deal */ 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 - } +@Disabled("we ignore this test when running the automated tests") +class GithubAPITest { private static final String LOGIN = System.getProperty("github.login"); private static final String API_TOKEN = System.getProperty("github.api"); - // I would sugest with the repo level of permission. + // I would suggest with the repo level of permission. private static final String OAUTH_TOKEN = System.getProperty("github.oauth"); // the name of the organization to which the login is a participant. - private static final String PARTICPATING_ORG = System.getProperty("github.org"); - - public GihubAPITest(String name) { - super(name); - // TODO Auto-generated constructor stub - } + private static final String PARTICIPATING_ORG = System.getProperty("github.org"); - public void testWithUserAPIToken() throws IOException { + @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(); @@ -94,7 +83,8 @@ public void testWithUserAPIToken() throws IOException { assertTrue(found); } - public void testOrganizationMembership () throws IOException { + @Test + void testOrganizationMembership() throws IOException { GitHub gh = GitHub.connectUsingOAuth(OAUTH_TOKEN); Map orgs = gh.getMyOrganizations(); @@ -122,7 +112,8 @@ public void testOrganizationMembership () throws IOException { assertTrue(true); } - public void testOrganizationMembershipAPI () throws IOException { + @Test + void testOrganizationMembershipAPI() throws IOException { GitHub gh = GitHub.connect(LOGIN, API_TOKEN); Map orgs = gh.getMyOrganizations(); @@ -137,12 +128,13 @@ public void testOrganizationMembershipAPI () throws IOException { } // /organizations - public void testWithOAuthToken() throws IOException { + @Test + void testWithOAuthToken() throws IOException { GitHub gh = GitHub.connectUsingOAuth(OAUTH_TOKEN); GHUser me = gh.getMyself(); - GHOrganization org = gh.getOrganization(PARTICPATING_ORG); + GHOrganization org = gh.getOrganization(PARTICIPATING_ORG); Map teams = org.getTeams();