Don't try fallback authentication for non-authentication error responses

This commit is contained in:
cketti 2017-05-02 03:57:25 +02:00
parent 231b46f278
commit c54b95b2c6
5 changed files with 109 additions and 10 deletions

View file

@ -376,6 +376,7 @@ class ImapConnection {
try {
attemptXOAuth2();
} catch (NegativeImapResponseException e) {
//TODO: Check response code so we don't needlessly invalidate the token.
oauthTokenProvider.invalidateToken(settings.getUsername());
if (!retryXoauth2WithNewToken) {
@ -456,7 +457,7 @@ class ImapConnection {
try {
extractCapabilities(responseParser.readStatusResponse(tag, command, getLogId(), null));
} catch (NegativeImapResponseException e) {
throw new AuthenticationFailedException(e.getMessage());
handleAuthenticationFailure(e);
}
}
@ -489,11 +490,7 @@ class ImapConnection {
try {
extractCapabilities(responseParser.readStatusResponse(tag, command, getLogId(), null));
} catch (NegativeImapResponseException e) {
if (e.wasByeResponseReceived()) {
close();
}
throw new AuthenticationFailedException(e.getMessage());
handleAuthenticationFailure(e);
}
}
@ -514,7 +511,7 @@ class ImapConnection {
String command = String.format(Commands.LOGIN + " \"%s\" \"%s\"", username, password);
extractCapabilities(executeSimpleCommand(command, true));
} catch (NegativeImapResponseException e) {
throw new AuthenticationFailedException(e.getMessage());
handleAuthenticationFailure(e);
}
}
@ -534,6 +531,23 @@ class ImapConnection {
}
}
private void handleAuthenticationFailure(NegativeImapResponseException e) throws MessagingException {
ImapResponse lastResponse = e.getLastResponse();
String responseCode = ResponseCodeExtractor.getResponseCode(lastResponse);
// If there's no response code we simply assume it was an authentication failure.
if (responseCode == null || responseCode.equals(ResponseCodeExtractor.AUTHENTICATION_FAILED)) {
if (e.wasByeResponseReceived()) {
close();
}
throw new AuthenticationFailedException(e.getMessage());
} else {
close();
throw e;
}
}
private void enableCompressionIfRequested() throws IOException, MessagingException {
if (hasCapability(Capabilities.COMPRESS_DEFLATE) && shouldEnableCompression()) {
enableCompression();

View file

@ -20,20 +20,24 @@ class NegativeImapResponseException extends MessagingException {
public String getAlertText() {
if (alertText == null) {
ImapResponse lastResponse = responses.get(responses.size() - 1);
ImapResponse lastResponse = getLastResponse();
alertText = AlertResponse.getAlertText(lastResponse);
}
return alertText;
}
public boolean wasByeResponseReceived() {
for (ImapResponse response : responses) {
if (response.getTag() == null && response.size() >= 1 && equalsIgnoreCase(response.get(0), Responses.BYE)) {
return true;
}
}
return false;
}
public ImapResponse getLastResponse() {
return responses.get(responses.size() - 1);
}
}

View file

@ -0,0 +1,19 @@
package com.fsck.k9.mail.store.imap;
class ResponseCodeExtractor {
public static final String AUTHENTICATION_FAILED = "AUTHENTICATIONFAILED";
private ResponseCodeExtractor() {
}
public static String getResponseCode(ImapResponse response) {
if (response.size() < 2 || !response.isList(1)) {
return null;
}
ImapList responseTextCode = response.getList(1);
return responseTextCode.size() != 1 ? null : responseTextCode.getString(0);
}
}

View file

@ -197,6 +197,30 @@ public class ImapConnectionTest {
server.verifyInteractionCompleted();
}
@Test
public void open_authPlainFailureAndDisconnect_shouldThrow() throws Exception {
settings.setAuthType(AuthType.PLAIN);
MockImapServer server = new MockImapServer();
preAuthenticationDialog(server, "AUTH=PLAIN");
server.expect("2 AUTHENTICATE PLAIN");
server.output("+");
server.expect(ByteString.encodeUtf8("\000" + USERNAME + "\000" + PASSWORD).base64());
server.output("2 NO [UNAVAILABLE] Maximum number of connections from user+IP exceeded");
server.closeConnection();
ImapConnection imapConnection = startServerAndCreateImapConnection(server);
try {
imapConnection.open();
fail("Expected exception");
} catch (NegativeImapResponseException e) {
assertThat(e.getMessage(), containsString("Maximum number of connections from user+IP exceeded"));
}
assertFalse(imapConnection.isConnected());
server.verifyConnectionClosed();
server.verifyInteractionCompleted();
}
@Test
public void open_authPlainWithByeResponseAndConnectionClose_shouldThrowAuthenticationFailedException()
throws Exception {

View file

@ -0,0 +1,38 @@
package com.fsck.k9.mail.store.imap;
import org.junit.Test;
import static com.fsck.k9.mail.store.imap.ImapResponseHelper.createImapResponse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
public class ResponseCodeExtractorTest {
@Test
public void getResponseCode_withResponseCode() throws Exception {
ImapResponse imapResponse = createImapResponse("x NO [AUTHENTICATIONFAILED] No sir");
String result = ResponseCodeExtractor.getResponseCode(imapResponse);
assertEquals("AUTHENTICATIONFAILED", result);
}
@Test
public void getResponseCode_withoutResponseCode() throws Exception {
ImapResponse imapResponse = createImapResponse("x NO Authentication failed");
String result = ResponseCodeExtractor.getResponseCode(imapResponse);
assertNull(result);
}
@Test
public void getResponseCode_withoutSingleItemResponse() throws Exception {
ImapResponse imapResponse = createImapResponse("x NO");
String result = ResponseCodeExtractor.getResponseCode(imapResponse);
assertNull(result);
}
}