com.samsung.android.remindme.jsonrpc.AuthenticatedJsonRpcJavaClient.java Source code

Java tutorial

Introduction

Here is the source code for com.samsung.android.remindme.jsonrpc.AuthenticatedJsonRpcJavaClient.java

Source

/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.samsung.android.remindme.jsonrpc;

import com.samsung.android.remindme.Config;

import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.cookie.BasicClientCookie;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;

/**
 * An Android/App Engine JSON-RPC client, complete with AccountManager-based auth.
 */
public class AuthenticatedJsonRpcJavaClient extends JsonRpcJavaClient {
    public static final String APPENGINE_SERVICE_NAME = "ah";

    public static final int NEED_AUTH_NOTIFICATION = 1;
    public static final int NEED_AUTH_INTENT = 2;

    private Context mContext;

    private TokenStoreHelper mTokenStoreHelper;

    private final String mAuthUrlTemplate;

    public AuthenticatedJsonRpcJavaClient(Context context, String authUrlTemplate, String rpcUrl) {
        super(rpcUrl);
        mContext = context;
        mAuthUrlTemplate = authUrlTemplate;
        mTokenStoreHelper = new TokenStoreHelper(context);
    }

    @SuppressWarnings("serial")
    public static class RequestedUserAuthenticationException extends Exception {
    }

    @SuppressWarnings("serial")
    public static class InvalidAuthTokenException extends Exception {
        public InvalidAuthTokenException() {
            super();
        }

        public InvalidAuthTokenException(String message) {
            super(message);
        }
    }

    public void blockingAuthenticateAccount(final Account account, final int needAuthAction,
            boolean forceReauthenticate) throws AuthenticationException, OperationCanceledException,
            RequestedUserAuthenticationException, InvalidAuthTokenException {

        String existingToken = mTokenStoreHelper.getToken(account);
        if (!forceReauthenticate && existingToken != null) {
            BasicClientCookie c = new BasicClientCookie("ACSID", existingToken);
            try {
                c.setDomain(new URI(Config.SERVER_BASE_URL).getHost());
                mHttpClient.getCookieStore().addCookie(c);
                return;
            } catch (URISyntaxException e) {
            }
        }

        // Get an auth token for this account.
        AccountManager am = AccountManager.get(mContext);
        Bundle authBundle = null;
        String authToken = null;

        // Block on getting the auth token result.
        try {
            authBundle = am.getAuthToken(account, APPENGINE_SERVICE_NAME, needAuthAction == NEED_AUTH_NOTIFICATION,
                    null, null).getResult();
        } catch (IOException e) {
            throw new AuthenticationException("IOException while getting auth token.", e);
        } catch (AuthenticatorException e) {
            throw new AuthenticationException("AuthenticatorException while getting auth token.", e);
        }

        if (authBundle.containsKey(AccountManager.KEY_INTENT) && needAuthAction == NEED_AUTH_INTENT) {
            Intent authRequestIntent = (Intent) authBundle.get(AccountManager.KEY_INTENT);
            mContext.startActivity(authRequestIntent);
            throw new RequestedUserAuthenticationException();
        } else if (authBundle.containsKey(AccountManager.KEY_AUTHTOKEN)) {
            authToken = authBundle.getString(AccountManager.KEY_AUTHTOKEN);
            System.out.println(authToken);
            System.out.println(AccountManager.KEY_AUTHTOKEN);
        }

        if (authToken == null) {
            throw new AuthenticationException("Retrieved auth token was null.");
        }

        try {
            blockingAuthenticateWithToken(account, authToken);
        } catch (InvalidAuthTokenException e) {
            am.invalidateAuthToken(account.type, authToken);
            throw e;
        }
    }

    private void blockingAuthenticateWithToken(Account account, String authToken)
            throws AuthenticationException, InvalidAuthTokenException {
        // Promote the given auth token into an App Engine ACSID token.
        HttpGet httpGet = new HttpGet(String.format(mAuthUrlTemplate, authToken));
        String acsidToken = null;

        try {
            HttpResponse response = mHttpClient.execute(httpGet);
            if (response.getStatusLine().getStatusCode() == 403) {
                throw new InvalidAuthTokenException();
            }

            List<Cookie> cookies = mHttpClient.getCookieStore().getCookies();
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals("ACSID")) {
                    acsidToken = cookie.getValue();
                    break;
                }
            }

            if (acsidToken == null && response.getStatusLine().getStatusCode() == 500) {
                // If no ACSID cookie was passed, it usually means the auth token was invalid;
                throw new InvalidAuthTokenException("ACSID cookie not found in HTTP response: "
                        + response.getStatusLine().toString() + "; assuming invalid auth token.");
            }

            mTokenStoreHelper.putToken(account, acsidToken);
        } catch (ClientProtocolException e) {
            throw new AuthenticationException("HTTP Protocol error authenticating to App Engine", e);
        } catch (IOException e) {
            throw new AuthenticationException("IOException authenticating to App Engine", e);
        }
    }

    public static void ensureHasTokenWithUI(Activity activity, Account account,
            final EnsureHasTokenWithUICallback callback) {
        AccountManager am = AccountManager.get(activity);
        am.getAuthToken(account, APPENGINE_SERVICE_NAME, null, activity, new AccountManagerCallback<Bundle>() {
            public void run(AccountManagerFuture<Bundle> authBundleFuture) {
                Bundle authBundle = null;
                try {
                    authBundle = authBundleFuture.getResult();
                } catch (OperationCanceledException e) {
                    callback.onAuthDenied();
                    return;
                } catch (AuthenticatorException e) {
                    callback.onError(e);
                    return;
                } catch (IOException e) {
                    callback.onError(e);
                    return;
                }

                if (authBundle.containsKey(AccountManager.KEY_AUTHTOKEN)) {
                    callback.onHasToken((String) authBundle.get(AccountManager.KEY_AUTHTOKEN));
                } else {
                    callback.onError(
                            new IllegalStateException("No auth token available, but operation not canceled."));
                }
            }
        }, null);
    }

    public void invalidateAccountAcsidToken(Account account) {
        mTokenStoreHelper.invalidateToken(account);
    }

    public static interface EnsureHasTokenWithUICallback {
        public void onAuthDenied();

        public void onHasToken(String authToken);

        public void onError(Throwable e);
    }

    /**
     * This class helps manage stored ACSID tokens.
     * TODO: use a persistent cookie store instead of this intermediate structure
     */
    private static class TokenStoreHelper extends SQLiteOpenHelper {
        private static final String TABLE_NAME = "tokens";
        private static final String[] ALL_COLUMNS = new String[] { "account", "token" };

        TokenStoreHelper(Context context) {
            super(context, "tokens.db", null, 1);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("CREATE TABLE " + TABLE_NAME + " (account TEXT UNIQUE, token TEXT);");
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
            onCreate(db);
        }

        public void putToken(Account account, String token) {
            SQLiteDatabase db = getWritableDatabase();
            ContentValues values = new ContentValues();
            values.put("account", account.name);
            values.put("token", token);
            db.insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE);
            db.close();
        }

        public String getToken(Account account) {
            SQLiteDatabase db = getReadableDatabase();
            Cursor c = db.query(TABLE_NAME, ALL_COLUMNS, "account = ?", new String[] { account.name }, null, null,
                    null);
            if (!c.moveToNext()) {
                c.close();
                db.close();
                return null;
            }
            String token = c.getString(1);
            c.close();
            db.close();
            return token;
        }

        public void invalidateToken(Account account) {
            SQLiteDatabase db = getWritableDatabase();
            db.delete(TABLE_NAME, "account = ?", new String[] { account.name });
            db.close();
        }
    }
}