diff --git a/changelog.html b/changelog.html
index 1487f139cb0ac8d47a2a9269d71b049a8147b16d..e732f5ba65f9d47127080c68a57c91b4577e23bc 100644
--- a/changelog.html
+++ b/changelog.html
@@ -63,6 +63,9 @@ Upcoming changes
+ -
+ Jenkins should recover gracefully from a failure to process "remember me" cookie
+ (issue 11643)
-
Fixed Up link in matrix projects
(issue 21773)
diff --git a/core/src/main/java/hudson/security/TokenBasedRememberMeServices2.java b/core/src/main/java/hudson/security/TokenBasedRememberMeServices2.java
index 7234f9a88aba3f1e68e73fb9aafd791da93d31e1..7edf311e18c90d1e70d5571a38210a5017c489f5 100644
--- a/core/src/main/java/hudson/security/TokenBasedRememberMeServices2.java
+++ b/core/src/main/java/hudson/security/TokenBasedRememberMeServices2.java
@@ -28,6 +28,7 @@ import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import hudson.Functions;
import jenkins.model.Jenkins;
import jenkins.security.HMACConfidentialKey;
import org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices;
@@ -100,6 +101,16 @@ public class TokenBasedRememberMeServices2 extends TokenBasedRememberMeServices
}
}
+ @Override
+ public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
+ try {
+ return super.autoLogin(request, response);
+ } catch (Exception e) {
+ cancelCookie(request, response, "Failed to handle remember-me cookie: "+Functions.printThrowable(e));
+ return null;
+ }
+ }
+
/**
* Used to compute the token signature securely.
*/
diff --git a/test/src/test/groovy/hudson/security/TokenBasedRememberMeServices2Test.groovy b/test/src/test/groovy/hudson/security/TokenBasedRememberMeServices2Test.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..13dacab83ee6dbd4998fdc40d1130f991d2e1168
--- /dev/null
+++ b/test/src/test/groovy/hudson/security/TokenBasedRememberMeServices2Test.groovy
@@ -0,0 +1,131 @@
+package hudson.security
+
+import com.gargoylesoftware.htmlunit.html.HtmlForm
+import com.gargoylesoftware.htmlunit.html.HtmlPage
+import org.acegisecurity.AuthenticationException
+import org.acegisecurity.BadCredentialsException
+import org.acegisecurity.GrantedAuthority
+import org.acegisecurity.GrantedAuthorityImpl
+import org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices
+import org.acegisecurity.userdetails.User
+import org.acegisecurity.userdetails.UserDetails
+import org.acegisecurity.userdetails.UsernameNotFoundException
+import org.apache.commons.httpclient.Cookie
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.jvnet.hudson.test.JenkinsRule
+import org.springframework.dao.DataAccessException
+
+import java.util.logging.Handler
+import java.util.logging.Level
+import java.util.logging.LogRecord
+import java.util.logging.Logger
+
+import static java.util.logging.Level.FINEST
+
+/**
+ *
+ *
+ * @author Kohsuke Kawaguchi
+ */
+class TokenBasedRememberMeServices2Test {
+ @Rule
+ public JenkinsRule j = new JenkinsRule();
+
+ private boolean failureInduced;
+
+ private Logger logger = Logger.getLogger(TokenBasedRememberMeServices.class.name)
+
+ private List logs = []
+ private Handler loghandler
+
+ @Before
+ public void setUp() {
+ loghandler = new Handler() {
+ @Override
+ void publish(LogRecord record) {
+ logs.add(record);
+ }
+
+ @Override
+ void flush() {
+ }
+
+ @Override
+ void close() throws SecurityException {
+ }
+ }
+ loghandler.level = FINEST
+ logger.addHandler(loghandler)
+ logger.level = FINEST
+ }
+
+ @After
+ public void tearDown() {
+ logger.removeHandler(loghandler);
+ logger.level = null
+ }
+
+ @Test
+ public void bogusTokenWillNotClearItself() {
+ j.jenkins.securityRealm = new BogusSecurityRealm()
+
+ def wc = j.createWebClient()
+ loginWithRememberMe(wc)
+
+ // we should see a remember me cookie
+ def c = getRememberMeCookie(wc)
+ assert c!=null
+
+ // start a new session and attempt to access Jenkins,
+ // which should cause autoLogin failures
+ wc = j.createWebClient()
+ wc.cookieManager.addCookie(c);
+
+ // even if SecurityRealm chokes, it shouldn't kill the page
+ logs.clear()
+ wc.goTo("")
+
+ // make sure that the server recorded this failure
+ assert failureInduced
+ assert logs.find { it.message.contains("intentionally not working")}!=null
+ // and the problematic cookie should have been removed
+ assert getRememberMeCookie(wc)==null
+ }
+
+ private Cookie getRememberMeCookie(JenkinsRule.WebClient wc) {
+ wc.cookieManager.getCookie(TokenBasedRememberMeServices2.ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY)
+ }
+
+ private void loginWithRememberMe(JenkinsRule.WebClient wc) {
+ HtmlPage page = wc.goTo("login");
+
+ HtmlForm form = page.getFormByName("login");
+ form.getInputByName("j_username").valueAttribute = "alice"
+ form.getInputByName("j_password").valueAttribute = "alice"
+ form.getInputByName("remember_me").checked = true
+ form.submit(null);
+ }
+
+ private class BogusSecurityRealm extends AbstractPasswordBasedSecurityRealm {
+ @Override
+ protected UserDetails authenticate(String username, String password) throws AuthenticationException {
+ if (username==password)
+ return new User(username,password,true,[new GrantedAuthorityImpl("myteam")] as GrantedAuthority[])
+ throw new BadCredentialsException(username);
+ }
+
+ @Override
+ GroupDetails loadGroupByGroupname(String groupname) throws UsernameNotFoundException, DataAccessException {
+ throw new UnsupportedOperationException()
+ }
+
+ @Override
+ UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
+ failureInduced = true
+ throw new IllegalArgumentException("intentionally not working");
+ }
+ }
+}