From 59e33daaf80d467db5ef03b2bb568521440125cd Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sun, 3 Nov 2024 20:11:07 -0800 Subject: [PATCH 1/7] Upgrade to pac4j 6.x, require Java 17 or newer, and migrate to EE 9 --- Jenkinsfile | 1 - pom.xml | 76 ++++++++++----- .../oic/AnythingGoesTokenValidator.java | 29 ++++-- .../plugins/oic/CustomOidcConfiguration.java | 2 +- .../oic/EscapeHatchCrumbExclusion.java | 8 +- .../oic/FailedCheckOfTokenException.java | 8 +- .../jenkinsci/plugins/oic/OicCredentials.java | 4 +- .../plugins/oic/OicCrumbExclusion.java | 10 +- .../plugins/oic/OicSecurityRealm.java | 96 ++++++++++++------- .../oic/EscapeHatchCrumbExclusionTest.java | 10 +- .../plugins/oic/MockHttpServletRequest.java | 26 ++--- .../plugins/oic/OicCrumbExclusionTest.java | 6 +- .../org/jenkinsci/plugins/oic/PluginTest.java | 10 +- .../org/jenkinsci/plugins/oic/TestRealm.java | 13 ++- 14 files changed, 181 insertions(+), 118 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index ebe4cbaa..bc891518 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -4,6 +4,5 @@ buildPlugin(useContainerAgent: true, artifactCachingProxyEnabled: true, configurations: [ [platform: 'linux', jdk: 21], - [platform: 'linux', jdk: 11], [platform: 'windows', jdk: 17], ]) diff --git a/pom.xml b/pom.xml index 3651ad2b..e46c9204 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.jenkins-ci.plugins plugin - 4.87 + 5.2 @@ -45,22 +45,20 @@ 4 999999-SNAPSHOT jenkinsci/${project.artifactId}-plugin - - 2.426.3 + 2.479 + ${jenkins.baseline}.1 false Max - 1836.vccda_4a_122a_a_e 4.383 - - 5.7.7 + 6.0.6 io.jenkins.tools.bom - bom-2.426.x - 3208.vb_21177d4b_cd9 + bom-${jenkins.baseline}.x + 3482.vc10d4f6da_28a_ pom import @@ -93,6 +91,10 @@ io.jenkins.plugins asm-api + + io.jenkins.plugins + commons-text-api + org.jenkins-ci.plugins jackson2-api @@ -104,18 +106,18 @@ org.pac4j - - pac4j-javaee + pac4j-jakartaee ${pac4jVersion} - com.google.guava - guava + + com.fasterxml.jackson.core + jackson-databind - - org.ow2.asm - asm + + commons-text + commons-text @@ -128,7 +130,6 @@ org.pac4j pac4j-oidc ${pac4jVersion} - @@ -136,17 +137,27 @@ jackson-databind + com.google.guava guava + + + commons-text + commons-text + + + + org.slf4j + slf4j-api + + + + org.springframework + spring-core + - - com.google.code.gson - gson - 2.11.0 - test - com.google.oauth-client google-oauth-client @@ -154,9 +165,20 @@ test + com.google.guava guava + + + org.apache.httpcomponents + httpclient + + + + org.apache.httpcomponents + httpcore + @@ -165,9 +187,13 @@ test - org.apache.httpcomponents - httpclient - 4.5.14 + io.jenkins.plugins + gson-api + test + + + org.jenkins-ci.plugins + apache-httpcomponents-client-4-api test diff --git a/src/main/java/org/jenkinsci/plugins/oic/AnythingGoesTokenValidator.java b/src/main/java/org/jenkinsci/plugins/oic/AnythingGoesTokenValidator.java index 2bdb56ca..d740552d 100644 --- a/src/main/java/org/jenkinsci/plugins/oic/AnythingGoesTokenValidator.java +++ b/src/main/java/org/jenkinsci/plugins/oic/AnythingGoesTokenValidator.java @@ -17,6 +17,8 @@ import java.util.logging.Logger; import org.pac4j.core.exception.TechnicalException; import org.pac4j.oidc.config.OidcConfiguration; +import org.pac4j.oidc.metadata.OidcOpMetadataResolver; +import org.pac4j.oidc.metadata.StaticOidcOpMetadataResolver; import org.pac4j.oidc.profile.creator.TokenValidator; public class AnythingGoesTokenValidator extends TokenValidator { @@ -24,7 +26,11 @@ public class AnythingGoesTokenValidator extends TokenValidator { public static final Logger LOGGER = Logger.getLogger(AnythingGoesTokenValidator.class.getName()); public AnythingGoesTokenValidator() { - super(createFakeOidcConfiguration()); + this(createFakeOidcProviderMetadata()); + } + + public AnythingGoesTokenValidator(OIDCProviderMetadata metadata) { + super(createFakeOidcConfiguration(metadata), metadata); } @Override @@ -50,18 +56,23 @@ public IDTokenClaimsSet validate(final JWT idToken, final Nonce expectedNonce) { * which if we are not validating we may not actually have (e.g. jwks_url). * So we need a configuration with this set just so the validator can say "this is valid". */ - private static OidcConfiguration createFakeOidcConfiguration() { + private static OidcConfiguration createFakeOidcConfiguration(OIDCProviderMetadata metadata) { + OidcConfiguration config = new OidcConfiguration(); + config.setClientId("ignored"); + config.setSecret("ignored"); + OidcOpMetadataResolver opMetadataResolver = new StaticOidcOpMetadataResolver(config, metadata); + config.setOpMetadataResolver(opMetadataResolver); + config.setPreferredJwsAlgorithm(JWSAlgorithm.HS256); + config.setClientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); + return config; + } + + private static OIDCProviderMetadata createFakeOidcProviderMetadata() { try { - OidcConfiguration config = new OidcConfiguration(); - config.setClientId("ignored"); - config.setSecret("ignored"); OIDCProviderMetadata providerMetadata = new OIDCProviderMetadata( new Issuer("http://ignored"), List.of(SubjectType.PUBLIC), new URI("http://ignored.and.invalid./")); providerMetadata.setIDTokenJWSAlgs(List.of(JWSAlgorithm.HS256)); - config.setProviderMetadata(providerMetadata); - config.setPreferredJwsAlgorithm(JWSAlgorithm.HS256); - config.setClientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); - return config; + return providerMetadata; } catch (URISyntaxException e) { // should never happen the urls we are using are valid throw new IllegalStateException(e); diff --git a/src/main/java/org/jenkinsci/plugins/oic/CustomOidcConfiguration.java b/src/main/java/org/jenkinsci/plugins/oic/CustomOidcConfiguration.java index 5f1878b4..fc58784d 100644 --- a/src/main/java/org/jenkinsci/plugins/oic/CustomOidcConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/oic/CustomOidcConfiguration.java @@ -30,6 +30,7 @@ class CustomOidcConfiguration extends OidcConfiguration { @Override public void configureHttpRequest(HTTPRequest request) { + super.configureHttpRequest(request); Proxy proxy = null; Jenkins jenkins = Jenkins.getInstanceOrNull(); if (jenkins != null) { // unit tests @@ -47,6 +48,5 @@ public void configureHttpRequest(HTTPRequest request) { throw new IllegalStateException("could not configure the SSLFactory, this should not be possible", e); } } - super.configureHttpRequest(request); } } diff --git a/src/main/java/org/jenkinsci/plugins/oic/EscapeHatchCrumbExclusion.java b/src/main/java/org/jenkinsci/plugins/oic/EscapeHatchCrumbExclusion.java index c888ed12..65ba3f8e 100644 --- a/src/main/java/org/jenkinsci/plugins/oic/EscapeHatchCrumbExclusion.java +++ b/src/main/java/org/jenkinsci/plugins/oic/EscapeHatchCrumbExclusion.java @@ -2,11 +2,11 @@ import hudson.Extension; import hudson.security.csrf.CrumbExclusion; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; /** * Excluding the escapeHatch login from CSRF protection as the crumb is calculated based on the authentication diff --git a/src/main/java/org/jenkinsci/plugins/oic/FailedCheckOfTokenException.java b/src/main/java/org/jenkinsci/plugins/oic/FailedCheckOfTokenException.java index 11dce4b4..4e4e65d0 100644 --- a/src/main/java/org/jenkinsci/plugins/oic/FailedCheckOfTokenException.java +++ b/src/main/java/org/jenkinsci/plugins/oic/FailedCheckOfTokenException.java @@ -1,11 +1,11 @@ package org.jenkinsci.plugins.oic; import edu.umd.cs.findbugs.annotations.CheckForNull; +import jakarta.servlet.ServletException; import java.io.IOException; -import javax.servlet.ServletException; import org.kohsuke.stapler.HttpResponse; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.StaplerRequest2; +import org.kohsuke.stapler.StaplerResponse2; /** * Exception to be thrown when the received ID Token did not pass the expected check. @@ -26,7 +26,7 @@ public String getIdpLogoutUrl() { } @Override - public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) + public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node) throws IOException, ServletException { req.getView(this, "error").forward(req, rsp); } diff --git a/src/main/java/org/jenkinsci/plugins/oic/OicCredentials.java b/src/main/java/org/jenkinsci/plugins/oic/OicCredentials.java index 843dea50..fefc070e 100644 --- a/src/main/java/org/jenkinsci/plugins/oic/OicCredentials.java +++ b/src/main/java/org/jenkinsci/plugins/oic/OicCredentials.java @@ -10,7 +10,7 @@ import java.io.Serializable; import net.sf.json.JSONObject; import org.jenkinsci.Symbol; -import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerRequest2; public class OicCredentials extends UserProperty implements Serializable { static final String PROPERTY_NAME = "oicCredentials"; @@ -23,7 +23,7 @@ public class OicCredentials extends UserProperty implements Serializable { private final Long expiresAtMillis; @Override - public UserProperty reconfigure(StaplerRequest req, JSONObject form) throws Descriptor.FormException { + public UserProperty reconfigure(StaplerRequest2 req, JSONObject form) throws Descriptor.FormException { req.bindJSON(this, form); return this; } diff --git a/src/main/java/org/jenkinsci/plugins/oic/OicCrumbExclusion.java b/src/main/java/org/jenkinsci/plugins/oic/OicCrumbExclusion.java index d7e0141e..fa33334c 100644 --- a/src/main/java/org/jenkinsci/plugins/oic/OicCrumbExclusion.java +++ b/src/main/java/org/jenkinsci/plugins/oic/OicCrumbExclusion.java @@ -3,15 +3,15 @@ import hudson.Extension; import hudson.security.SecurityRealm; import hudson.security.csrf.CrumbExclusion; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import jenkins.model.Jenkins; /** - * Crumb exclusion to allow POSTing to {@link OicSecurityRealm#doFinishLogin(org.kohsuke.stapler.StaplerRequest, org.kohsuke.stapler.StaplerResponse)} + * Crumb exclusion to allow POSTing to {@link OicSecurityRealm#doFinishLogin(org.kohsuke.stapler.StaplerRequest2, org.kohsuke.stapler.StaplerResponse2)} */ @Extension public class OicCrumbExclusion extends CrumbExclusion { diff --git a/src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java b/src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java index 0d2382ff..7b4467ff 100644 --- a/src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java +++ b/src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java @@ -46,7 +46,7 @@ import hudson.model.Descriptor.FormException; import hudson.model.Failure; import hudson.model.User; -import hudson.security.ChainedServletFilter; +import hudson.security.ChainedServletFilter2; import hudson.security.SecurityRealm; import hudson.tasks.Mailer; import hudson.util.FormValidation; @@ -55,6 +55,15 @@ import io.burt.jmespath.JmesPath; import io.burt.jmespath.RuntimeConfiguration; import io.burt.jmespath.jcf.JcfRuntime; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectStreamException; @@ -79,15 +88,6 @@ import java.util.logging.Logger; import java.util.regex.Pattern; import javax.annotation.PostConstruct; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; import jenkins.model.Jenkins; import jenkins.security.ApiTokenProperty; import jenkins.security.FIPS140; @@ -102,9 +102,11 @@ import org.kohsuke.stapler.Header; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.Stapler; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.StaplerRequest2; +import org.kohsuke.stapler.StaplerResponse2; import org.kohsuke.stapler.interceptor.RequirePOST; +import org.pac4j.core.context.CallContext; +import org.pac4j.core.context.FrameworkParameters; import org.pac4j.core.context.WebContext; import org.pac4j.core.context.session.SessionStore; import org.pac4j.core.credentials.Credentials; @@ -114,12 +116,16 @@ import org.pac4j.core.http.callback.NoParameterCallbackUrlResolver; import org.pac4j.core.profile.creator.ProfileCreator; import org.pac4j.jee.context.JEEContextFactory; +import org.pac4j.jee.context.JEEFrameworkParameters; import org.pac4j.jee.context.session.JEESessionStoreFactory; import org.pac4j.jee.http.adapter.JEEHttpActionAdapter; import org.pac4j.oidc.client.OidcClient; import org.pac4j.oidc.config.OidcConfiguration; import org.pac4j.oidc.credentials.authenticator.OidcAuthenticator; +import org.pac4j.oidc.metadata.OidcOpMetadataResolver; +import org.pac4j.oidc.metadata.StaticOidcOpMetadataResolver; import org.pac4j.oidc.profile.OidcProfile; +import org.pac4j.oidc.profile.creator.TokenValidator; import org.pac4j.oidc.redirect.OidcRedirectionActionBuilder; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.AuthenticationManager; @@ -517,11 +523,19 @@ private OidcConfiguration buildOidcConfiguration() { OIDCProviderMetadata oidcProviderMetadata = serverConfiguration.toProviderMetadata(); filterNonFIPS140CompliantAlgorithms(oidcProviderMetadata); + OidcOpMetadataResolver opMetadataResolver; if (this.isDisableTokenVerification()) { conf.setAllowUnsignedIdTokens(true); - conf.setTokenValidator(new AnythingGoesTokenValidator()); + opMetadataResolver = new StaticOidcOpMetadataResolver(conf, oidcProviderMetadata) { + @Override + protected TokenValidator createTokenValidator() { + return new AnythingGoesTokenValidator(); + } + }; + } else { + opMetadataResolver = new StaticOidcOpMetadataResolver(conf, oidcProviderMetadata); } - conf.setProviderMetadata(oidcProviderMetadata); + conf.setOpMetadataResolver(opMetadataResolver); if (oidcProviderMetadata.getScopes() != null) { // auto configuration does not need to supply scopes conf.setScope(oidcProviderMetadata.getScopes().toString()); @@ -534,6 +548,7 @@ private OidcConfiguration buildOidcConfiguration() { if (this.isPkceEnabled()) { conf.setPkceMethod(CodeChallengeMethod.S256); } + opMetadataResolver.init(); return conf; } @@ -853,7 +868,7 @@ public String getAuthenticationGatewayUrl() { @Override public Filter createFilter(FilterConfig filterConfig) { - return new ChainedServletFilter(super.createFilter(filterConfig), new Filter() { + return new ChainedServletFilter2(super.createFilter(filterConfig), new Filter() { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { @@ -947,11 +962,12 @@ public void doCommenceLogin(@QueryParameter String from, @Header("Referer") fina final String redirectOnFinish = getValidRedirectUrl(from != null ? from : referer); OidcRedirectionActionBuilder builder = new OidcRedirectionActionBuilder(client); - WebContext webContext = - JEEContextFactory.INSTANCE.newContext(Stapler.getCurrentRequest(), Stapler.getCurrentResponse()); - SessionStore sessionStore = JEESessionStoreFactory.INSTANCE.newSessionStore(); - RedirectionAction redirectionAction = - builder.getRedirectionAction(webContext, sessionStore).orElseThrow(); + FrameworkParameters parameters = + new JEEFrameworkParameters(Stapler.getCurrentRequest2(), Stapler.getCurrentResponse2()); + WebContext webContext = JEEContextFactory.INSTANCE.newContext(parameters); + SessionStore sessionStore = JEESessionStoreFactory.INSTANCE.newSessionStore(parameters); + CallContext ctx = new CallContext(webContext, sessionStore); + RedirectionAction redirectionAction = builder.getRedirectionAction(ctx).orElseThrow(); // store the redirect url for after the login. sessionStore.set(webContext, SESSION_POST_LOGIN_REDIRECT_URL_KEY, redirectOnFinish); @@ -1149,7 +1165,7 @@ private List ensureString(Object field) { } @Restricted(DoNotUse.class) // stapler only - public void doLogout(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { + public void doLogout(StaplerRequest2 req, StaplerResponse2 rsp) throws IOException, ServletException { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); User user = User.get2(authentication); @@ -1172,7 +1188,7 @@ public void doLogout(StaplerRequest req, StaplerResponse rsp) throws IOException } @Override - public String getPostLogOutUrl2(StaplerRequest req, Authentication auth) { + public String getPostLogOutUrl2(StaplerRequest2 req, Authentication auth) { Object idToken = req.getAttribute(ID_TOKEN_REQUEST_ATTRIBUTE); Object state = getStateAttribute(req.getSession()); var openidLogoutEndpoint = maybeOpenIdLogoutEndpoint( @@ -1187,12 +1203,14 @@ public String getPostLogOutUrl2(StaplerRequest req, Authentication auth) { Object getStateAttribute(HttpSession session) { // return null; OidcClient client = buildOidcClient(); - WebContext webContext = - JEEContextFactory.INSTANCE.newContext(Stapler.getCurrentRequest(), Stapler.getCurrentResponse()); - SessionStore sessionStore = JEESessionStoreFactory.INSTANCE.newSessionStore(); + FrameworkParameters parameters = + new JEEFrameworkParameters(Stapler.getCurrentRequest2(), Stapler.getCurrentResponse2()); + WebContext webContext = JEEContextFactory.INSTANCE.newContext(parameters); + SessionStore sessionStore = JEESessionStoreFactory.INSTANCE.newSessionStore(parameters); + CallContext ctx = new CallContext(webContext, sessionStore); return client.getConfiguration() .getValueRetriever() - .retrieve(client.getStateSessionAttributeName(), client, webContext, sessionStore) + .retrieve(ctx, client.getStateSessionAttributeName(), client) .orElse(null); } @@ -1219,7 +1237,7 @@ private String maybeOpenIdLogoutEndpoint(String idToken, String state, String po return null; } - private String getFinalLogoutUrl(StaplerRequest req, Authentication auth) { + private String getFinalLogoutUrl(StaplerRequest2 req, Authentication auth) { if (Jenkins.get().hasPermission(Jenkins.READ)) { return super.getPostLogOutUrl2(req, auth); } @@ -1256,11 +1274,12 @@ private String buildOAuthRedirectUrl() throws NullPointerException { * @param request The user's request * @throws ParseException if the JWT (or other response) could not be parsed. */ - public void doFinishLogin(StaplerRequest request, StaplerResponse response) throws IOException, ParseException { + public void doFinishLogin(StaplerRequest2 request, StaplerResponse2 response) throws IOException, ParseException { OidcClient client = buildOidcClient(); - WebContext webContext = JEEContextFactory.INSTANCE.newContext(request, response); - SessionStore sessionStore = JEESessionStoreFactory.INSTANCE.newSessionStore(); + FrameworkParameters parameters = new JEEFrameworkParameters(request, response); + WebContext webContext = JEEContextFactory.INSTANCE.newContext(parameters); + SessionStore sessionStore = JEESessionStoreFactory.INSTANCE.newSessionStore(parameters); try { // NB: TODO this also handles back channel logout if "logoutendpoint" parameter is set @@ -1269,14 +1288,17 @@ public void doFinishLogin(StaplerRequest request, StaplerResponse response) thro // Jenkins stuff correctly // also should have its own URL to make the code easier to follow :) - Credentials credentials = client.getCredentials(webContext, sessionStore) + CallContext ctx = new CallContext(webContext, sessionStore); + Credentials credentials = client.getCredentials(ctx) .orElseThrow(() -> new Failure("Could not extract credentials from request")); + credentials = client.validateCredentials(ctx, credentials) + .orElseThrow(() -> new Failure("Could not validate credentials from request")); ProfileCreator profileCreator = client.getProfileCreator(); // creating the profile performs validation of the token OidcProfile profile = (OidcProfile) profileCreator - .create(credentials, webContext, sessionStore) + .create(ctx, credentials) .orElseThrow(() -> new Failure("Could not build user profile")); AccessToken accessToken = profile.getAccessToken(); @@ -1393,8 +1415,9 @@ private boolean refreshExpiredToken( HttpServletResponse httpResponse) throws IOException { - WebContext webContext = JEEContextFactory.INSTANCE.newContext(httpRequest, httpResponse); - SessionStore sessionStore = JEESessionStoreFactory.INSTANCE.newSessionStore(); + FrameworkParameters parameters = new JEEFrameworkParameters(httpRequest, httpResponse); + WebContext webContext = JEEContextFactory.INSTANCE.newContext(parameters); + SessionStore sessionStore = JEESessionStoreFactory.INSTANCE.newSessionStore(parameters); OidcClient client = buildOidcClient(); // PAC4J maintains the nonce even though servers should not respond with an id token containing the nonce // https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse @@ -1410,7 +1433,8 @@ private boolean refreshExpiredToken( profile.setIdTokenString(credentials.getIdToken()); profile.setRefreshToken(new RefreshToken(credentials.getRefreshToken())); - profile = (OidcProfile) client.renewUserProfile(profile, webContext, sessionStore) + CallContext ctx = new CallContext(webContext, sessionStore); + profile = (OidcProfile) client.renewUserProfile(ctx, profile) .orElseThrow(() -> new IllegalStateException("Could not renew user profile")); // During refresh the IDToken may or may not be present. @@ -1471,7 +1495,7 @@ private boolean refreshExpiredToken( return false; } LOGGER.log(Level.FINE, "Failed to refresh expired token", e); - redirectToLoginUrl(Stapler.getCurrentRequest(), Stapler.getCurrentResponse()); + redirectToLoginUrl(Stapler.getCurrentRequest2(), Stapler.getCurrentResponse2()); return false; } LOGGER.log(Level.WARNING, "Failed to refresh expired token", e); diff --git a/src/test/java/org/jenkinsci/plugins/oic/EscapeHatchCrumbExclusionTest.java b/src/test/java/org/jenkinsci/plugins/oic/EscapeHatchCrumbExclusionTest.java index e3155ef5..14b91baa 100644 --- a/src/test/java/org/jenkinsci/plugins/oic/EscapeHatchCrumbExclusionTest.java +++ b/src/test/java/org/jenkinsci/plugins/oic/EscapeHatchCrumbExclusionTest.java @@ -1,11 +1,11 @@ package org.jenkinsci.plugins.oic; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletResponse; import org.junit.Test; import static org.junit.Assert.assertFalse; diff --git a/src/test/java/org/jenkinsci/plugins/oic/MockHttpServletRequest.java b/src/test/java/org/jenkinsci/plugins/oic/MockHttpServletRequest.java index 5bb2836a..578f0554 100644 --- a/src/test/java/org/jenkinsci/plugins/oic/MockHttpServletRequest.java +++ b/src/test/java/org/jenkinsci/plugins/oic/MockHttpServletRequest.java @@ -1,24 +1,24 @@ package org.jenkinsci.plugins.oic; +import jakarta.servlet.AsyncContext; +import jakarta.servlet.DispatcherType; +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import jakarta.servlet.http.HttpUpgradeHandler; +import jakarta.servlet.http.Part; import java.io.BufferedReader; import java.security.Principal; import java.util.Collection; import java.util.Enumeration; import java.util.Locale; import java.util.Map; -import javax.servlet.AsyncContext; -import javax.servlet.DispatcherType; -import javax.servlet.RequestDispatcher; -import javax.servlet.ServletContext; -import javax.servlet.ServletInputStream; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import javax.servlet.http.HttpUpgradeHandler; -import javax.servlet.http.Part; public class MockHttpServletRequest implements HttpServletRequest { diff --git a/src/test/java/org/jenkinsci/plugins/oic/OicCrumbExclusionTest.java b/src/test/java/org/jenkinsci/plugins/oic/OicCrumbExclusionTest.java index a6f0ad72..22ed2133 100644 --- a/src/test/java/org/jenkinsci/plugins/oic/OicCrumbExclusionTest.java +++ b/src/test/java/org/jenkinsci/plugins/oic/OicCrumbExclusionTest.java @@ -1,8 +1,8 @@ package org.jenkinsci.plugins.oic; -import javax.servlet.FilterChain; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.FilterChain; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import jenkins.model.Jenkins; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/jenkinsci/plugins/oic/PluginTest.java b/src/test/java/org/jenkinsci/plugins/oic/PluginTest.java index 2a6aeb99..791d5c3d 100644 --- a/src/test/java/org/jenkinsci/plugins/oic/PluginTest.java +++ b/src/test/java/org/jenkinsci/plugins/oic/PluginTest.java @@ -18,6 +18,7 @@ import hudson.model.User; import hudson.tasks.Mailer; import hudson.util.VersionNumber; +import jakarta.servlet.http.HttpSession; import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; @@ -41,7 +42,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.net.ssl.SSLException; -import javax.servlet.http.HttpSession; import jenkins.model.Jenkins; import jenkins.security.ApiTokenProperty; import jenkins.security.LastGrantedAuthoritiesProperty; @@ -143,7 +143,7 @@ public void testLoginWithDefaults() throws Exception { .withQueryParam("nonce", matching(".+"))); verify(postRequestedFor(urlPathEqualTo("/token")).withRequestBody(notMatching(".*&scope=.*"))); webClient.executeOnServer(() -> { - HttpSession session = Stapler.getCurrentRequest().getSession(); + HttpSession session = Stapler.getCurrentRequest2().getSession(); assertNotNull(((OicSecurityRealm) Jenkins.get().getSecurityRealm()).getStateAttribute(session)); return null; }); @@ -933,7 +933,7 @@ public void testLogoutShouldBeJenkinsOnlyWhenNoProviderLogoutConfigured() throws String[] logoutURL = new String[1]; jenkinsRule.executeOnServer(() -> { - logoutURL[0] = oicsr.getPostLogOutUrl2(Stapler.getCurrentRequest(), Jenkins.ANONYMOUS2); + logoutURL[0] = oicsr.getPostLogOutUrl2(Stapler.getCurrentRequest2(), Jenkins.ANONYMOUS2); return null; }); assertEquals("/jenkins/", logoutURL[0]); @@ -947,7 +947,7 @@ public void testLogoutShouldBeProviderURLWhenProviderLogoutConfigured() throws E String[] logoutURL = new String[1]; jenkinsRule.executeOnServer(() -> { - logoutURL[0] = oicsr.getPostLogOutUrl2(Stapler.getCurrentRequest(), Jenkins.ANONYMOUS2); + logoutURL[0] = oicsr.getPostLogOutUrl2(Stapler.getCurrentRequest2(), Jenkins.ANONYMOUS2); return null; }); assertEquals("http://provider/logout?state=null", logoutURL[0]); @@ -964,7 +964,7 @@ public void testLogoutShouldBeProviderURLWithRedirectWhenProviderLogoutConfigure String[] logoutURL = new String[1]; jenkinsRule.executeOnServer(() -> { - logoutURL[0] = oicsr.getPostLogOutUrl2(Stapler.getCurrentRequest(), Jenkins.ANONYMOUS2); + logoutURL[0] = oicsr.getPostLogOutUrl2(Stapler.getCurrentRequest2(), Jenkins.ANONYMOUS2); return null; }); assertEquals( diff --git a/src/test/java/org/jenkinsci/plugins/oic/TestRealm.java b/src/test/java/org/jenkinsci/plugins/oic/TestRealm.java index eec1d58e..58e01908 100644 --- a/src/test/java/org/jenkinsci/plugins/oic/TestRealm.java +++ b/src/test/java/org/jenkinsci/plugins/oic/TestRealm.java @@ -8,11 +8,13 @@ import java.io.IOException; import java.io.ObjectStreamException; import java.text.ParseException; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.StaplerRequest2; +import org.kohsuke.stapler.StaplerResponse2; +import org.pac4j.core.context.FrameworkParameters; import org.pac4j.core.context.WebContext; import org.pac4j.core.context.session.SessionStore; import org.pac4j.jee.context.JEEContextFactory; +import org.pac4j.jee.context.JEEFrameworkParameters; import org.pac4j.jee.context.session.JEESessionStoreFactory; import org.pac4j.oidc.client.OidcClient; @@ -259,14 +261,15 @@ public Descriptor getDescriptor() { } @Override - public void doFinishLogin(StaplerRequest request, StaplerResponse response) throws IOException, ParseException { + public void doFinishLogin(StaplerRequest2 request, StaplerResponse2 response) throws IOException, ParseException { /* * PluginTest uses a hardCoded nonce "nonce" */ if (!isNonceDisabled()) { // only hack the nonce if the nonce is enabled - WebContext webContext = JEEContextFactory.INSTANCE.newContext(request, response); - SessionStore sessionStore = JEESessionStoreFactory.INSTANCE.newSessionStore(); + FrameworkParameters parameters = new JEEFrameworkParameters(request, response); + WebContext webContext = JEEContextFactory.INSTANCE.newContext(parameters); + SessionStore sessionStore = JEESessionStoreFactory.INSTANCE.newSessionStore(parameters); OidcClient oidcClient = buildOidcClient(); sessionStore.set(webContext, oidcClient.getNonceSessionAttributeName(), "nonce"); } From 1b25d1e6dba296da05ccbf177aadca4a4d06d5e7 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sun, 3 Nov 2024 20:28:35 -0800 Subject: [PATCH 2/7] Build with a recent version of Maven --- .github/workflows/ci.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 26befbe3..a75b368a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,21 @@ jobs: java-version: '17' check-latest: true cache: 'maven' + # https://github.com/jenkins-infra/github-reusable-workflows/issues/36 + - name: Set up Maven + run: | + wget --no-verbose https://downloads.apache.org/maven/maven-3/$MAVEN_VERSION/binaries/apache-maven-$MAVEN_VERSION-bin.tar.gz + echo $CHECKSUM apache-maven-$MAVEN_VERSION-bin.tar.gz | sha512sum --check + tar xzf apache-maven-$MAVEN_VERSION-bin.tar.gz + rm apache-maven-$MAVEN_VERSION-bin.tar.gz + sudo mv apache-maven-$MAVEN_VERSION /opt/maven + sudo rm -f /usr/bin/mvn + sudo ln -s /opt/maven/bin/mvn /usr/bin/mvn + mvn --version + env: + MAVEN_VERSION: 3.9.9 + # https://downloads.apache.org/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz.sha512 + CHECKSUM: a555254d6b53d267965a3404ecb14e53c3827c09c3b94b5678835887ab404556bfaf78dcfe03ba76fa2508649dca8531c74bca4d5846513522404d48e8c4ac8b - name: Generate coverage with JaCoCo run: mvn --batch-mode clean verify jacoco:prepare-agent test integration-test jacoco:report From bb116fd444740112bc538da8bec1eef58727d346 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Thu, 2 Jan 2025 12:15:01 -0800 Subject: [PATCH 3/7] Fix TODO --- pom.xml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pom.xml b/pom.xml index f816c90e..0ad8f663 100644 --- a/pom.xml +++ b/pom.xml @@ -62,16 +62,6 @@ pom import - - - com.nimbusds - nimbus-jose-jwt - 9.48 - - com.github.stephenc.jcip From 9d4d158eca4be22dc55335303e4b5ef7d66a7584 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Fri, 3 Jan 2025 06:55:53 -0800 Subject: [PATCH 4/7] Remove unnecessary change to minimize diff --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index 0ad8f663..c224d3de 100644 --- a/pom.xml +++ b/pom.xml @@ -165,6 +165,11 @@ commons-text commons-text + + + org.ow2.asm + asm + org.slf4j From b8348b083ae3e94f0ef51a8bad9d0855d8519fc8 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Fri, 3 Jan 2025 06:58:48 -0800 Subject: [PATCH 5/7] Fix up to last commit --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index c224d3de..f8cc9bb9 100644 --- a/pom.xml +++ b/pom.xml @@ -133,6 +133,11 @@ org.apache.commons commons-text + + + org.ow2.asm + asm + org.slf4j @@ -165,11 +170,6 @@ commons-text commons-text - - - org.ow2.asm - asm - org.slf4j From 312f7bc63b516532ef5baaa40c5776d591b907cc Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Fri, 10 Jan 2025 07:06:00 -0800 Subject: [PATCH 6/7] Update pom.xml Co-authored-by: Francisco Javier Fernandez <31063239+fcojfernandez@users.noreply.github.com> --- pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pom.xml b/pom.xml index 88d76b28..a9e7c02c 100644 --- a/pom.xml +++ b/pom.xml @@ -108,11 +108,6 @@ com.fasterxml.jackson.core jackson-annotations - - - com.fasterxml.jackson.core - jackson-annotations - com.fasterxml.jackson.core From 82d7e6f765b0f78f0bec61782ac1cb6a326e3a2d Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Fri, 10 Jan 2025 07:10:26 -0800 Subject: [PATCH 7/7] https://github.com/jenkinsci/oic-auth-plugin/pull/455#discussion_r1910505996 --- pom.xml | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/pom.xml b/pom.xml index a9e7c02c..0f83bc5f 100644 --- a/pom.xml +++ b/pom.xml @@ -172,6 +172,12 @@ + + com.google.code.gson + gson + 2.11.0 + test + com.google.oauth-client google-oauth-client @@ -183,16 +189,6 @@ com.google.guava guava - - - org.apache.httpcomponents - httpclient - - - - org.apache.httpcomponents - httpcore - @@ -201,13 +197,9 @@ test - io.jenkins.plugins - gson-api - test - - - org.jenkins-ci.plugins - apache-httpcomponents-client-4-api + org.apache.httpcomponents + httpclient + 4.5.14 test