Try autodiscover with bare name if we get 401 with address

* Some autodiscover servers appear to require the bare user name
  for authentication rather than the user's email address.  This
  is apparently common for complex organizations maintaining a
  group of email domains
* If we get a 401 when trying to connect to an autodiscover server
  using the email address, we try again using just the bare name

Bug: 2682833
Change-Id: Ia07ca336e189069d4f3539e2245b3d53c82e3324
This commit is contained in:
Andrew Stadler 2010-05-14 00:05:28 -07:00
parent 8100a2dc55
commit 3778b3ed7e
1 changed files with 45 additions and 18 deletions

View File

@ -467,15 +467,17 @@ public class EasSyncService extends AbstractSyncService {
/**
* Send the POST command to the autodiscover server, handling a redirect, if necessary, and
* return the HttpResponse
* return the HttpResponse. If we get a 401 (unauthorized) error and we're using the
* full email address, try the bare user name instead (e.g. foo instead of foo@bar.com)
*
* @param client the HttpClient to be used for the request
* @param post the HttpPost we're going to send
* @param canRetry whether we can retry using the bare name on an authentication failure (401)
* @return an HttpResponse from the original or redirect server
* @throws IOException on any IOException within the HttpClient code
* @throws MessagingException
*/
private HttpResponse postAutodiscover(HttpClient client, HttpPost post)
private HttpResponse postAutodiscover(HttpClient client, HttpPost post, boolean canRetry)
throws IOException, MessagingException {
userLog("Posting autodiscover to: " + post.getURI());
HttpResponse resp = executePostWithTimeout(client, post, COMMAND_TIMEOUT);
@ -487,10 +489,21 @@ public class EasSyncService extends AbstractSyncService {
userLog("Posting autodiscover to redirect: " + post.getURI());
return executePostWithTimeout(client, post, COMMAND_TIMEOUT);
}
// 401 (Unauthorized) is for true auth errors when used in Autodiscover
} else if (code == HttpStatus.SC_UNAUTHORIZED) {
// 401 (Unauthorized) is for true auth errors when used in Autodiscover
// 403 (and others) we'll just punt on
if (canRetry && mUserName.contains("@")) {
// Try again using the bare user name
int atSignIndex = mUserName.indexOf('@');
mUserName = mUserName.substring(0, atSignIndex);
cacheAuthAndCmdString();
userLog("401 received; trying username: ", mUserName);
// Recreate the basic authentication string and reset the header
post.removeHeaders("Authorization");
post.setHeader("Authorization", mAuthString);
return postAutodiscover(client, post, false);
}
throw new MessagingException(MessagingException.AUTHENTICATION_FAILED);
// 403 (and others) we'll just punt on
} else if (code != HttpStatus.SC_OK) {
// We'll try the next address if this doesn't work
userLog("Code: " + code + ", throwing IOException");
@ -533,8 +546,8 @@ public class EasSyncService extends AbstractSyncService {
// Initialize the user name and password
mUserName = userName;
mPassword = password;
// Make sure the authentication string is created (mAuthString)
makeUriString("foo", null);
// Make sure the authentication string is recreated and cached
cacheAuthAndCmdString();
// Split out the domain name
int amp = userName.indexOf('@');
@ -546,6 +559,9 @@ public class EasSyncService extends AbstractSyncService {
// There are up to four attempts here; the two URLs that we're supposed to try per the
// specification, and up to one redirect for each (handled in postAutodiscover)
// Note: The expectation is that, of these four attempts, only a single server will
// actually be identified as the autodiscover server. For the identified server,
// we may also try a 2nd connection with a different format (bare name).
// Try the domain first and see if we can get a response
HttpPost post = new HttpPost("https://" + domain + AUTO_DISCOVER_PAGE);
@ -555,14 +571,14 @@ public class EasSyncService extends AbstractSyncService {
HttpClient client = getHttpClient(COMMAND_TIMEOUT);
HttpResponse resp;
try {
resp = postAutodiscover(client, post);
resp = postAutodiscover(client, post, true /*canRetry*/);
} catch (IOException e1) {
userLog("IOException in autodiscover; trying alternate address");
// We catch the IOException here because we have an alternate address to try
post.setURI(URI.create("https://autodiscover." + domain + AUTO_DISCOVER_PAGE));
// If we fail here, we're out of options, so we let the outer try catch the
// IOException and return null
resp = postAutodiscover(client, post);
resp = postAutodiscover(client, post, true /*canRetry*/);
}
// Get the "final" code; if it's not 200, just return null
@ -588,9 +604,12 @@ public class EasSyncService extends AbstractSyncService {
hostAuth = new HostAuth();
parseAutodiscover(parser, hostAuth);
// On success, we'll have a server address and login
if (hostAuth.mAddress != null && hostAuth.mLogin != null) {
if (hostAuth.mAddress != null) {
// Fill in the rest of the HostAuth
hostAuth.mPassword = password;
// We use the user name and password that were successful during
// the autodiscover process
hostAuth.mLogin = mUserName;
hostAuth.mPassword = mPassword;
hostAuth.mPort = 443;
hostAuth.mProtocol = "eas";
hostAuth.mFlags =
@ -699,8 +718,7 @@ public class EasSyncService extends AbstractSyncService {
String name = parser.getName();
if (name.equals("EMailAddress")) {
String addr = parser.nextText();
hostAuth.mLogin = addr;
userLog("Autodiscover, login: " + addr);
userLog("Autodiscover, email: " + addr);
} else if (name.equals("DisplayName")) {
String dn = parser.nextText();
userLog("Autodiscover, user: " + dn);
@ -1041,15 +1059,24 @@ public class EasSyncService extends AbstractSyncService {
}
}
/**
* Using mUserName and mPassword, create and cache mAuthString and mCacheString, which are used
* in all HttpPost commands. This should be called if these strings are null, or if mUserName
* and/or mPassword are changed
*/
@SuppressWarnings("deprecation")
private void cacheAuthAndCmdString() {
String safeUserName = URLEncoder.encode(mUserName);
String cs = mUserName + ':' + mPassword;
mAuthString = "Basic " + Base64.encodeToString(cs.getBytes(), Base64.NO_WRAP);
mCmdString = "&User=" + safeUserName + "&DeviceId=" + mDeviceId +
"&DeviceType=" + mDeviceType;
}
private String makeUriString(String cmd, String extra) throws IOException {
// Cache the authentication string and the command string
String safeUserName = URLEncoder.encode(mUserName);
if (mAuthString == null) {
String cs = mUserName + ':' + mPassword;
mAuthString = "Basic " + Base64.encodeToString(cs.getBytes(), Base64.NO_WRAP);
mCmdString = "&User=" + safeUserName + "&DeviceId=" + mDeviceId + "&DeviceType="
+ mDeviceType;
if (mAuthString == null || mCmdString == null) {
cacheAuthAndCmdString();
}
String us = (mSsl ? (mTrustSsl ? "httpts" : "https") : "http") + "://" + mHostAddress +
"/Microsoft-Server-ActiveSync";