diff options
Diffstat (limited to 'src/com/android/email/mail/internet')
-rw-r--r-- | src/com/android/email/mail/internet/AuthenticationCache.java | 162 | ||||
-rw-r--r-- | src/com/android/email/mail/internet/OAuthAuthenticator.java | 191 |
2 files changed, 0 insertions, 353 deletions
diff --git a/src/com/android/email/mail/internet/AuthenticationCache.java b/src/com/android/email/mail/internet/AuthenticationCache.java deleted file mode 100644 index 21508af35..000000000 --- a/src/com/android/email/mail/internet/AuthenticationCache.java +++ /dev/null @@ -1,162 +0,0 @@ -package com.android.email.mail.internet; - -import android.content.Context; -import android.text.format.DateUtils; - -import com.android.email.mail.internet.OAuthAuthenticator.AuthenticationResult; -import com.android.emailcommon.Logging; -import com.android.emailcommon.mail.AuthenticationFailedException; -import com.android.emailcommon.mail.MessagingException; -import com.android.emailcommon.provider.Account; -import com.android.emailcommon.provider.Credential; -import com.android.emailcommon.provider.HostAuth; -import com.android.mail.utils.LogUtils; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -public class AuthenticationCache { - private static AuthenticationCache sCache; - - // Threshold for refreshing a token. If the token is expected to expire within this amount of - // time, we won't even bother attempting to use it and will simply force a refresh. - private static final long EXPIRATION_THRESHOLD = 5 * DateUtils.MINUTE_IN_MILLIS; - - private final Map<Long, CacheEntry> mCache; - private final OAuthAuthenticator mAuthenticator; - - private class CacheEntry { - CacheEntry(long accountId, String providerId, String accessToken, String refreshToken, - long expirationTime) { - mAccountId = accountId; - mProviderId = providerId; - mAccessToken = accessToken; - mRefreshToken = refreshToken; - mExpirationTime = expirationTime; - } - - final long mAccountId; - String mProviderId; - String mAccessToken; - String mRefreshToken; - long mExpirationTime; - } - - public static AuthenticationCache getInstance() { - synchronized (AuthenticationCache.class) { - if (sCache == null) { - sCache = new AuthenticationCache(); - } - return sCache; - } - } - - private AuthenticationCache() { - mCache = new HashMap<Long, CacheEntry>(); - mAuthenticator = new OAuthAuthenticator(); - } - - // Gets an access token for the given account. This may be whatever is currently cached, or - // it may query the server to get a new one if the old one is expired or nearly expired. - public String retrieveAccessToken(Context context, Account account) throws - MessagingException, IOException { - // Currently, we always use the same OAuth info for both sending and receiving. - // If we start to allow different credential objects for sending and receiving, this - // will need to be updated. - CacheEntry entry = null; - synchronized (mCache) { - entry = getEntry(context, account); - } - synchronized (entry) { - final long actualExpiration = entry.mExpirationTime - EXPIRATION_THRESHOLD; - if (System.currentTimeMillis() > actualExpiration) { - // This access token is pretty close to end of life. Don't bother trying to use it, - // it might just time out while we're trying to sync. Go ahead and refresh it - // immediately. - refreshEntry(context, entry); - } - return entry.mAccessToken; - } - } - - public String refreshAccessToken(Context context, Account account) throws - MessagingException, IOException { - CacheEntry entry = getEntry(context, account); - synchronized (entry) { - refreshEntry(context, entry); - return entry.mAccessToken; - } - } - - private CacheEntry getEntry(Context context, Account account) { - CacheEntry entry; - if (account.isSaved() && !account.isTemporary()) { - entry = mCache.get(account.mId); - if (entry == null) { - LogUtils.d(Logging.LOG_TAG, "initializing entry from database"); - final HostAuth hostAuth = account.getOrCreateHostAuthRecv(context); - final Credential credential = hostAuth.getOrCreateCredential(context); - entry = new CacheEntry(account.mId, credential.mProviderId, credential.mAccessToken, - credential.mRefreshToken, credential.mExpiration); - mCache.put(account.mId, entry); - } - } else { - // This account is temporary, just create a temporary entry. Don't store - // it in the cache, it won't be findable because we don't yet have an account Id. - final HostAuth hostAuth = account.getOrCreateHostAuthRecv(context); - final Credential credential = hostAuth.getCredential(context); - entry = new CacheEntry(account.mId, credential.mProviderId, credential.mAccessToken, - credential.mRefreshToken, credential.mExpiration); - } - return entry; - } - - private void refreshEntry(Context context, CacheEntry entry) throws - IOException, MessagingException { - LogUtils.d(Logging.LOG_TAG, "AuthenticationCache refreshEntry %d", entry.mAccountId); - try { - final AuthenticationResult result = mAuthenticator.requestRefresh(context, - entry.mProviderId, entry.mRefreshToken); - // Don't set the refresh token here, it's not returned by the refresh response, - // so setting it here would make it blank. - entry.mAccessToken = result.mAccessToken; - entry.mExpirationTime = result.mExpiresInSeconds * DateUtils.SECOND_IN_MILLIS + - System.currentTimeMillis(); - saveEntry(context, entry); - } catch (AuthenticationFailedException e) { - // This is fatal. Clear the tokens and rethrow the exception. - LogUtils.d(Logging.LOG_TAG, "authentication failed, clearning"); - clearEntry(context, entry); - throw e; - } catch (MessagingException e) { - LogUtils.d(Logging.LOG_TAG, "messaging exception"); - throw e; - } catch (IOException e) { - LogUtils.d(Logging.LOG_TAG, "IO exception"); - throw e; - } - } - - private void saveEntry(Context context, CacheEntry entry) { - LogUtils.d(Logging.LOG_TAG, "saveEntry"); - - final Account account = Account.restoreAccountWithId(context, entry.mAccountId); - final HostAuth hostAuth = account.getOrCreateHostAuthRecv(context); - final Credential cred = hostAuth.getOrCreateCredential(context); - cred.mProviderId = entry.mProviderId; - cred.mAccessToken = entry.mAccessToken; - cred.mRefreshToken = entry.mRefreshToken; - cred.mExpiration = entry.mExpirationTime; - cred.update(context, cred.toContentValues()); - } - - private void clearEntry(Context context, CacheEntry entry) { - LogUtils.d(Logging.LOG_TAG, "clearEntry"); - entry.mAccessToken = ""; - entry.mRefreshToken = ""; - entry.mExpirationTime = 0; - saveEntry(context, entry); - mCache.remove(entry.mAccountId); - } -} diff --git a/src/com/android/email/mail/internet/OAuthAuthenticator.java b/src/com/android/email/mail/internet/OAuthAuthenticator.java deleted file mode 100644 index c3e255b5c..000000000 --- a/src/com/android/email/mail/internet/OAuthAuthenticator.java +++ /dev/null @@ -1,191 +0,0 @@ -package com.android.email.mail.internet; - -import android.content.Context; -import android.text.format.DateUtils; - -import com.android.email.activity.setup.AccountSettingsUtils; -import com.android.emailcommon.Logging; -import com.android.emailcommon.VendorPolicyLoader.OAuthProvider; -import com.android.emailcommon.mail.AuthenticationFailedException; -import com.android.emailcommon.mail.MessagingException; -import com.android.mail.utils.LogUtils; - -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.client.HttpClient; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.List; - -public class OAuthAuthenticator { - private static final String TAG = Logging.LOG_TAG; - - public static final String OAUTH_REQUEST_CODE = "code"; - public static final String OAUTH_REQUEST_REFRESH_TOKEN = "refresh_token"; - public static final String OAUTH_REQUEST_CLIENT_ID = "client_id"; - public static final String OAUTH_REQUEST_CLIENT_SECRET = "client_secret"; - public static final String OAUTH_REQUEST_REDIRECT_URI = "redirect_uri"; - public static final String OAUTH_REQUEST_GRANT_TYPE = "grant_type"; - - public static final String JSON_ACCESS_TOKEN = "access_token"; - public static final String JSON_REFRESH_TOKEN = "refresh_token"; - public static final String JSON_EXPIRES_IN = "expires_in"; - - - private static final long CONNECTION_TIMEOUT = 20 * DateUtils.SECOND_IN_MILLIS; - private static final long COMMAND_TIMEOUT = 30 * DateUtils.SECOND_IN_MILLIS; - - final HttpClient mClient; - - public static class AuthenticationResult { - public AuthenticationResult(final String accessToken, final String refreshToken, - final int expiresInSeconds) { - mAccessToken = accessToken; - mRefreshToken = refreshToken; - mExpiresInSeconds = expiresInSeconds; - } - - @Override - public String toString() { - return "result access " + (mAccessToken==null?"null":"[REDACTED]") + - " refresh " + (mRefreshToken==null?"null":"[REDACTED]") + - " expiresInSeconds " + mExpiresInSeconds; - } - - public final String mAccessToken; - public final String mRefreshToken; - public final int mExpiresInSeconds; - } - - public OAuthAuthenticator() { - final HttpParams params = new BasicHttpParams(); - HttpConnectionParams.setConnectionTimeout(params, (int)(CONNECTION_TIMEOUT)); - HttpConnectionParams.setSoTimeout(params, (int)(COMMAND_TIMEOUT)); - HttpConnectionParams.setSocketBufferSize(params, 8192); - mClient = new DefaultHttpClient(params); - } - - public AuthenticationResult requestAccess(final Context context, final String providerId, - final String code) throws MessagingException, IOException { - final OAuthProvider provider = AccountSettingsUtils.findOAuthProvider(context, providerId); - if (provider == null) { - LogUtils.e(TAG, "invalid provider %s", providerId); - // This shouldn't happen, but if it does, it's a fatal. Throw an authentication failed - // exception, this will at least give the user a heads up to set up their account again. - throw new AuthenticationFailedException("Invalid provider" + providerId); - } - - final HttpPost post = new HttpPost(provider.tokenEndpoint); - post.setHeader("Content-Type", "application/x-www-form-urlencoded"); - final List<BasicNameValuePair> nvp = new ArrayList<BasicNameValuePair>(); - nvp.add(new BasicNameValuePair(OAUTH_REQUEST_CODE, code)); - nvp.add(new BasicNameValuePair(OAUTH_REQUEST_CLIENT_ID, provider.clientId)); - nvp.add(new BasicNameValuePair(OAUTH_REQUEST_CLIENT_SECRET, provider.clientSecret)); - nvp.add(new BasicNameValuePair(OAUTH_REQUEST_REDIRECT_URI, provider.redirectUri)); - nvp.add(new BasicNameValuePair(OAUTH_REQUEST_GRANT_TYPE, "authorization_code")); - try { - post.setEntity(new UrlEncodedFormEntity(nvp)); - } catch (UnsupportedEncodingException e) { - LogUtils.e(TAG, e, "unsupported encoding"); - // This shouldn't happen, but if it does, it's a fatal. Throw an authentication failed - // exception, this will at least give the user a heads up to set up their account again. - throw new AuthenticationFailedException("Unsupported encoding", e); - } - - return doRequest(post); - } - - public AuthenticationResult requestRefresh(final Context context, final String providerId, - final String refreshToken) throws MessagingException, IOException { - final OAuthProvider provider = AccountSettingsUtils.findOAuthProvider(context, providerId); - if (provider == null) { - LogUtils.e(TAG, "invalid provider %s", providerId); - // This shouldn't happen, but if it does, it's a fatal. Throw an authentication failed - // exception, this will at least give the user a heads up to set up their account again. - throw new AuthenticationFailedException("Invalid provider" + providerId); - } - final HttpPost post = new HttpPost(provider.refreshEndpoint); - post.setHeader("Content-Type", "application/x-www-form-urlencoded"); - final List<BasicNameValuePair> nvp = new ArrayList<BasicNameValuePair>(); - nvp.add(new BasicNameValuePair(OAUTH_REQUEST_REFRESH_TOKEN, refreshToken)); - nvp.add(new BasicNameValuePair(OAUTH_REQUEST_CLIENT_ID, provider.clientId)); - nvp.add(new BasicNameValuePair(OAUTH_REQUEST_CLIENT_SECRET, provider.clientSecret)); - nvp.add(new BasicNameValuePair(OAUTH_REQUEST_GRANT_TYPE, "refresh_token")); - try { - post.setEntity(new UrlEncodedFormEntity(nvp)); - } catch (UnsupportedEncodingException e) { - LogUtils.e(TAG, e, "unsupported encoding"); - // This shouldn't happen, but if it does, it's a fatal. Throw an authentication failed - // exception, this will at least give the user a heads up to set up their account again. - throw new AuthenticationFailedException("Unsuported encoding", e); - } - - return doRequest(post); - } - - private AuthenticationResult doRequest(HttpPost post) throws MessagingException, - IOException { - final HttpResponse response; - response = mClient.execute(post); - final int status = response.getStatusLine().getStatusCode(); - if (status == HttpStatus.SC_OK) { - return parseResponse(response); - } else if (status == HttpStatus.SC_FORBIDDEN || status == HttpStatus.SC_UNAUTHORIZED || - status == HttpStatus.SC_BAD_REQUEST) { - LogUtils.e(TAG, "HTTP Authentication error getting oauth tokens %d", status); - // This is fatal, and we probably should clear our tokens after this. - throw new AuthenticationFailedException("Auth error getting auth token"); - } else { - LogUtils.e(TAG, "HTTP Error %d getting oauth tokens", status); - // This is probably a transient error, we can try again later. - throw new MessagingException("HTTPError " + status + " getting oauth token"); - } - } - - private AuthenticationResult parseResponse(HttpResponse response) throws IOException, - MessagingException { - final BufferedReader reader = new BufferedReader(new InputStreamReader( - response.getEntity().getContent(), "UTF-8")); - final StringBuilder builder = new StringBuilder(); - for (String line = null; (line = reader.readLine()) != null;) { - builder.append(line).append("\n"); - } - try { - final JSONObject jsonResult = new JSONObject(builder.toString()); - final String accessToken = jsonResult.getString(JSON_ACCESS_TOKEN); - final String expiresIn = jsonResult.getString(JSON_EXPIRES_IN); - final String refreshToken; - if (jsonResult.has(JSON_REFRESH_TOKEN)) { - refreshToken = jsonResult.getString(JSON_REFRESH_TOKEN); - } else { - refreshToken = null; - } - try { - int expiresInSeconds = Integer.valueOf(expiresIn); - return new AuthenticationResult(accessToken, refreshToken, expiresInSeconds); - } catch (NumberFormatException e) { - LogUtils.e(TAG, e, "Invalid expiration %s", expiresIn); - // This indicates a server error, we can try again later. - throw new MessagingException("Invalid number format", e); - } - } catch (JSONException e) { - LogUtils.e(TAG, e, "Invalid JSON"); - // This indicates a server error, we can try again later. - throw new MessagingException("Invalid JSON", e); - } - } -} - |