Improved auth flow for generic app.
authorkenstir <kenstir@gmail.com>
Wed, 18 Nov 2015 15:52:24 +0000 (10:52 -0500)
committerkenstir <kenstir@gmail.com>
Wed, 18 Nov 2015 15:52:24 +0000 (10:52 -0500)
* factored AccountManager interaction out into AccountUtils
* switching back and forth between c/w mars accounts works, but not switching
  to admin@demo.evergreencatalog.com or admin@mlnc4.mvlcstaff.org.
  Will debug further.

Open-ILS/src/Android/core/src/org/evergreen_ils/accountAccess/AccountAccess.java
Open-ILS/src/Android/core/src/org/evergreen_ils/accountAccess/AccountUtils.java [new file with mode: 0644]
Open-ILS/src/Android/core/src/org/evergreen_ils/auth/AuthenticatorActivity.java
Open-ILS/src/Android/core/src/org/evergreen_ils/auth/Const.java
Open-ILS/src/Android/core/src/org/evergreen_ils/auth/EvergreenAuthenticator.java
Open-ILS/src/Android/core/src/org/evergreen_ils/globals/GlobalConfigs.java
Open-ILS/src/Android/core/src/org/evergreen_ils/globals/Utils.java
Open-ILS/src/Android/core/src/org/evergreen_ils/views/splashscreen/LoadingTask.java
Open-ILS/src/Android/core/src/org/evergreen_ils/views/splashscreen/SplashActivity.java

index 61dfa22..81f0aa0 100644 (file)
  */
 package org.evergreen_ils.accountAccess;
 
-import java.io.IOException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.*;
-
-import android.accounts.*;
 import android.app.Activity;
-import android.os.Bundle;
-import android.os.Looper;
 import android.text.TextUtils;
 import android.util.Log;
-import org.evergreen_ils.R;
 import org.evergreen_ils.accountAccess.bookbags.BookBag;
 import org.evergreen_ils.accountAccess.bookbags.BookBagItem;
 import org.evergreen_ils.accountAccess.checkout.CircRecord;
 import org.evergreen_ils.accountAccess.fines.FinesRecord;
 import org.evergreen_ils.accountAccess.holds.HoldRecord;
+import org.evergreen_ils.auth.Const;
 import org.evergreen_ils.globals.GlobalConfigs;
 import org.evergreen_ils.globals.Utils;
 import org.evergreen_ils.searchCatalog.RecordInfo;
-import org.evergreen_ils.auth.Const;
 import org.opensrf.net.http.HttpConnection;
 import org.opensrf.util.OSRFObject;
 
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+
 /**
  * The Class AuthenticateUser. Singleton class
  */
@@ -156,8 +151,6 @@ public class AccountAccess {
     /** home library ID. */
     private Integer homeLibraryID = null;
 
-    private boolean haveSession;
-
     /** The user name. */
     public static String userName = null;
     
@@ -236,29 +229,35 @@ public class AccountAccess {
      * Retrieve session.
      * @throws SessionNotFoundException
      */
-    public boolean retrieveSession(String auth_token, boolean force) throws SessionNotFoundException {
-
-        if (!force && this.haveSession && this.authToken.equals(auth_token))
-            return true;
-        this.haveSession = false;
+    public boolean retrieveSession(String auth_token) throws SessionNotFoundException {
+        Log.d(Const.AUTH_TAG, "retrieveSession " + auth_token);
+        clearSession();
         this.authToken = auth_token;
-        
+
         Object resp = Utils.doRequest(conn(), SERVICE_AUTH,
-                METHOD_AUTH_SESSION_RETRV, authToken, new Object[] {
-                        authToken});
+                METHOD_AUTH_SESSION_RETRV, auth_token, new Object[]{
+                        auth_token});
         if (resp != null) {
             OSRFObject au = (OSRFObject) resp;
             userID = au.getInt("id");
             homeLibraryID = au.getInt("home_ou");
             userName = au.getString("usrname");
             //email = au.getString("email");
-            this.haveSession = true;
+
+            return true;
         }
-        return this.haveSession;
+        throw new SessionNotFoundException();
     }
 
-    public static boolean runningOnUIThread() {
-        return (Looper.myLooper() == Looper.getMainLooper());
+    private void clearSession() {
+        userID = null;
+        homeLibraryID = null;
+        userName = null;
+        authToken = null;
+    }
+
+    public boolean reauthenticate(Activity activity) throws SessionNotFoundException {
+        return reauthenticate(activity, userName);
     }
 
     /** invalidate current auth token and get a new one
@@ -266,38 +265,20 @@ public class AccountAccess {
      * @param activity
      * @return true if auth successful
      */
-    public boolean reauthenticate(Activity activity) throws SessionNotFoundException, AuthenticatorException, OperationCanceledException, IOException {
-        boolean ok = false;
-        final AccountManager am = AccountManager.get(activity);
-        final String accountType = activity.getString(R.string.ou_account_type);
-        final Account account = new Account(userName, accountType);
-        am.invalidateAuthToken(accountType, authToken);
-        haveSession = false;
-        authToken = null;
-        if (runningOnUIThread())
-            return false;
-        Bundle b = am.getAuthToken(account, Const.AUTHTOKEN_TYPE, null, activity, null, null).getResult();
-        final String new_authToken = b.getString(AccountManager.KEY_AUTHTOKEN);
-        if (TextUtils.isEmpty(new_authToken))
-            return false;
-        return retrieveSession(new_authToken, true);
-    }
+    public boolean reauthenticate(Activity activity, String user_name) throws SessionNotFoundException {
+        Log.d(Const.AUTH_TAG, "reauthenticate " + user_name);
+        AccountUtils.invalidateAuthToken(activity, authToken);
+        clearSession();
 
-    public static String getLibraryUrl(Activity activity, String account_name, String account_type) {
-        final AccountManager am = AccountManager.get(activity);
-        Account account = new Account(account_name, account_type);
-        String library_url = am.getUserData(account, Const.KEY_LIBRARY_URL);
-
-        // compatibility with specific apps like cwmars_app.  If no library_url exists as userdata on the account,
-        // get it from the resources.
-        if (TextUtils.isEmpty(library_url)) {
-            library_url = activity.getString(R.string.ou_library_url);
-            if (!TextUtils.isEmpty(library_url)) {
-                am.setUserData(account, Const.KEY_LIBRARY_URL, library_url);
-            }
+        try {
+            String auth_token = AccountUtils.getAuthTokenForAccount(activity, user_name);
+            if (TextUtils.isEmpty(auth_token))
+                return false;
+            return retrieveSession(auth_token);
+        } catch (Exception e) {
+            Log.i(Const.AUTH_TAG, "reauth exception", e);
+            return false;
         }
-
-        return library_url;
     }
 
 //    public void getOrgHiddentDepth() {
diff --git a/Open-ILS/src/Android/core/src/org/evergreen_ils/accountAccess/AccountUtils.java b/Open-ILS/src/Android/core/src/org/evergreen_ils/accountAccess/AccountUtils.java
new file mode 100644 (file)
index 0000000..83971ff
--- /dev/null
@@ -0,0 +1,74 @@
+package org.evergreen_ils.accountAccess;
+
+import android.accounts.*;
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Looper;
+import android.text.TextUtils;
+import android.util.Log;
+import org.evergreen_ils.R;
+import org.evergreen_ils.auth.Const;
+import org.w3c.dom.Text;
+
+import java.io.IOException;
+
+/**
+ * Created by kenstir on 11/17/2015.
+ */
+public class AccountUtils {
+
+    public static String getLibraryUrl(Activity activity, String account_name, String account_type) {
+        final AccountManager am = AccountManager.get(activity);
+        Account account = new Account(account_name, account_type);
+        String library_url = am.getUserData(account, Const.KEY_LIBRARY_URL);
+
+        // compatibility with specific apps like cwmars_app.  If no library_url exists as userdata on the account,
+        // get it from the resources.
+        if (TextUtils.isEmpty(library_url)) {
+            library_url = activity.getString(R.string.ou_library_url);
+            if (!TextUtils.isEmpty(library_url)) {
+                am.setUserData(account, Const.KEY_LIBRARY_URL, library_url);
+            }
+        }
+
+        return library_url;
+    }
+
+    public static void invalidateAuthToken(Activity activity, String auth_token) {
+        Log.i(Const.AUTH_TAG, "invalidateAuthToken "+auth_token);
+        final AccountManager am = AccountManager.get(activity);
+        final String accountType = activity.getString(R.string.ou_account_type);
+        am.invalidateAuthToken(accountType, auth_token);
+    }
+
+    public static String getAuthTokenForAccount(Activity activity, String account_name) throws AuthenticatorException, OperationCanceledException, IOException {
+        Log.i(Const.AUTH_TAG, "getAuthTokenForAccount "+account_name);
+        if (runningOnUIThread() || TextUtils.isEmpty(account_name)) {
+            Log.i(Const.AUTH_TAG, "getAuthTokenForAccount returns null");
+            return null;
+        }
+        final AccountManager am = AccountManager.get(activity);
+        final String accountType = activity.getString(R.string.ou_account_type);
+        final Account account = new Account(account_name, accountType);
+        Bundle b = am.getAuthToken(account, Const.AUTHTOKEN_TYPE, null, activity, null, null).getResult();
+        final String auth_token = b.getString(AccountManager.KEY_AUTHTOKEN);
+        Log.i(Const.AUTH_TAG, "getAuthTokenForAccount " + account_name + " returns " + auth_token);
+        return auth_token;
+    }
+
+    public static Bundle getAuthToken(Activity activity) throws AuthenticatorException, OperationCanceledException, IOException {
+        Log.i(Const.AUTH_TAG, "getAuthToken");
+        if (runningOnUIThread())
+            return new Bundle();
+        final AccountManager am = AccountManager.get(activity);
+        final String accountType = activity.getString(R.string.ou_account_type);
+        AccountManagerFuture<Bundle> future = am.getAuthTokenByFeatures(accountType, Const.AUTHTOKEN_TYPE, null, activity, null, null, null, null);
+        Bundle bnd = future.getResult();
+        Log.i(Const.AUTH_TAG, "getAuthToken returns "+bnd);
+        return bnd;
+    }
+
+    public static boolean runningOnUIThread() {
+        return (Looper.myLooper() == Looper.getMainLooper());
+    }
+}
index 6400fa2..a67a0bf 100644 (file)
@@ -285,9 +285,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity {
     private void parseLibrariesJSON(String json) {
         libraries.clear();
 
-        boolean isDebuggable =  ( 0 != ( getApplicationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE ) );
-        if (isDebuggable) {
-            Library library = new Library("https://demo.evergreencatalog.com", "Example Consortium", "00 (Example Consortium)");
+        if (isDebuggable()) {
+            //Library library = new Library("https://demo.evergreencatalog.com", "Example Consortium", "00 (evergreencatalog.com Example Consortium)");
+            Library library = new Library("https://mlnc4.mvlcstaff.org", "MVLC Demo", "00 (MVLC Demo)");
             libraries.add(library);
         }
 
@@ -319,4 +319,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity {
             }
         }
     }
+
+    public boolean isDebuggable() {
+        return ( 0 != ( getApplicationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE ) );
+    }
 }
index 78e4862..4f6f698 100644 (file)
@@ -5,4 +5,5 @@ public class Const {
     public static final String AUTHTOKEN_TYPE = "opac";
     public static final String AUTHTOKEN_TYPE_LABEL = "Online Public Access Catalog";
     public static final String KEY_LIBRARY_URL = "library_url";
+    public final static String AUTH_TAG = "eg_auth:";
 }
index 4009f8d..146c8eb 100644 (file)
@@ -6,20 +6,16 @@ import java.security.NoSuchAlgorithmException;
 import java.util.HashMap;
 import java.util.Map;
 
-import android.preference.PreferenceManager;
-import org.evergreen_ils.globals.AppPrefs;
 import org.opensrf.Method;
 import org.opensrf.net.http.GatewayRequest;
 import org.opensrf.net.http.HttpConnection;
 import org.opensrf.net.http.HttpRequest;
 
-import android.content.Context;
 import android.text.TextUtils;
 import android.util.Log;
-import org.evergreen_ils.R;
 
 public class EvergreenAuthenticator {
-    private final static String TAG = "eg.auth";
+    private final static String TAG = EvergreenAuthenticator.class.getSimpleName();
     public final static String SERVICE_AUTH = "open-ils.auth";
     public final static String METHOD_AUTH_INIT = "open-ils.auth.authenticate.init";
     public final static String METHOD_AUTH_COMPLETE = "open-ils.auth.authenticate.complete";
@@ -44,7 +40,7 @@ public class EvergreenAuthenticator {
             return hexString.toString();
 
         } catch (NoSuchAlgorithmException e) {
-            e.printStackTrace();
+            Log.d(TAG, "no MD5", e);
         }
 
         return "";
index f22a4a2..d749092 100644 (file)
@@ -220,7 +220,7 @@ public class GlobalConfigs {
             });
 
             long duration_ms = System.currentTimeMillis() - start_ms;
-            Log.d("init", "getOrg took "+duration_ms+"ms");
+            Log.d(TAG, "getOrg took "+duration_ms+"ms");
             loadedOrgTree = true;
         }
     }
index 46b9fc2..0e71784 100644 (file)
@@ -36,6 +36,7 @@ import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.impl.client.DefaultHttpClient;
 import org.evergreen_ils.accountAccess.SessionNotFoundException;
+import org.evergreen_ils.auth.Const;
 import org.opensrf.Method;
 import org.opensrf.net.http.GatewayRequest;
 import org.opensrf.net.http.HttpConnection;
@@ -159,7 +160,7 @@ public class Utils {
 
             String textcode = getResponseTextcode(resp);
             if (TextUtils.equals(textcode, "NO_SESSION")) {
-                Log.d(TAG, textcode);
+                Log.d(Const.AUTH_TAG, textcode);
                 throw new SessionNotFoundException();
             }
 
index 2c92cbf..36c41d4 100644 (file)
@@ -22,18 +22,17 @@ package org.evergreen_ils.views.splashscreen;
 import android.text.TextUtils;
 import org.evergreen_ils.R;
 import org.evergreen_ils.accountAccess.AccountAccess;
+import org.evergreen_ils.accountAccess.AccountUtils;
 import org.evergreen_ils.accountAccess.SessionNotFoundException;
 import org.evergreen_ils.globals.AppPrefs;
 import org.evergreen_ils.globals.GlobalConfigs;
 import org.evergreen_ils.auth.Const;
 
-import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.AccountManagerFuture;
 import android.app.Activity;
 import android.os.Bundle;
 import android.util.Log;
-import org.w3c.dom.Text;
 
 /** This is basically the same as an AsyncTask<String,String,String>, except that it uses
  * a Thread.  Starting with HONEYCOMB, tasks are executed on a single thread and the 2nd
@@ -44,7 +43,7 @@ import org.w3c.dom.Text;
  */
 public class LoadingTask {
     private final String TAG = LoadingTask.class.getSimpleName();
-    
+
     public static final String TASK_OK = "OK";
 
     public interface LoadingTaskListener {
@@ -56,12 +55,10 @@ public class LoadingTask {
     // This is the listener that will be told when this task is finished
     private final LoadingTaskListener mListener;
     private Activity mCallingActivity;
-    private AccountManager mAccountManager;
 
     public LoadingTask(LoadingTaskListener listener, Activity callingActivity) {
         this.mListener = listener;
         this.mCallingActivity = callingActivity;
-        mAccountManager = AccountManager.get(callingActivity);
     }
 
     public void execute() {
@@ -95,19 +92,17 @@ public class LoadingTask {
     protected String doInBackground() {
         final String tag ="doInBackground> ";
         final String accountType = mCallingActivity.getString(R.string.ou_account_type);
-        Log.d(TAG, tag);
         try {
             Log.d(TAG, tag+"Signing in");
             publishProgress("Signing in");
 
-            AccountManagerFuture<Bundle> future = mAccountManager.getAuthTokenByFeatures(accountType, Const.AUTHTOKEN_TYPE, null, mCallingActivity, null, null, null, null);
-            Bundle bnd = future.getResult();
+            Bundle bnd = AccountUtils.getAuthToken(mCallingActivity);
             String auth_token = bnd.getString(AccountManager.KEY_AUTHTOKEN);
             String account_name = bnd.getString(AccountManager.KEY_ACCOUNT_NAME);
-            if (account_name == null)
+            if (TextUtils.isEmpty(auth_token) || TextUtils.isEmpty(account_name))
                 return "no account";
 
-            String library_url = AccountAccess.getLibraryUrl(mCallingActivity, account_name, accountType);
+            String library_url = AccountUtils.getLibraryUrl(mCallingActivity, account_name, accountType);
             AppPrefs.setString(AppPrefs.LIBRARY_URL, library_url);
 
             Log.d(TAG, tag+"Loading resources from "+library_url);
@@ -120,36 +115,13 @@ public class LoadingTask {
 
             // auth token zen: try once and if it fails, invalidate the token and try again
             boolean haveSession = false;
-            boolean retry = false;
             try {
-                haveSession = ac.retrieveSession(auth_token, true);
+                haveSession = ac.retrieveSession(auth_token);
             } catch (SessionNotFoundException e) {
-                mAccountManager.invalidateAuthToken(accountType, auth_token);
-                retry = true;
-            }
-            if (retry) {
-                // todo: replace with AccountAccess.reauthenticate?
-                try {
-                    haveSession = ac.reauthenticate(mCallingActivity);
-                } catch (Exception e) {
-                    Log.d(TAG, tag+"failed a 2nd time", e);
-                }
-                /*
-                final Account account = new Account(account_name, accountType);
-                future = mAccountManager.getAuthToken(account, Const.AUTHTOKEN_TYPE, null, mCallingActivity, null, null);
-                bnd = future.getResult();
-                Log.d(TAG, tag+"bnd="+bnd);
-                auth_token = bnd.getString(AccountManager.KEY_AUTHTOKEN);
-                account_name = bnd.getString(AccountManager.KEY_ACCOUNT_NAME);
-                Log.d(TAG, tag+"account_name="+account_name+" token="+auth_token);
-                if (account_name == null)
-                    return "no account";
                 try {
-                    haveSession = ac.retrieveSession(auth_token, true);
-                } catch (SessionNotFoundException e) {
-                    Log.d(TAG, tag+"failed a 2nd time", e);
+                    haveSession = ac.reauthenticate(mCallingActivity, account_name);
+                } catch (SessionNotFoundException e2) {
                 }
-                */
             }
             if (!haveSession)
                 return "no session";
index acad756..fa25ae9 100644 (file)
@@ -81,7 +81,6 @@ public class SplashActivity extends Activity implements LoadingTaskListener {
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
-        Log.d(TAG, "kcxxx: oncreate");
         super.onCreate(savedInstanceState);
 
         this.mContext = this;
@@ -111,7 +110,7 @@ public class SplashActivity extends Activity implements LoadingTaskListener {
     @Override
     protected void onStart() {
         super.onStart();
-        Log.d(TAG, "kcxxx: onstart");
+        Log.d(TAG, "onstart");
         if (!restarted) {
             startTask();
         }
@@ -121,19 +120,19 @@ public class SplashActivity extends Activity implements LoadingTaskListener {
     protected void onRestart() {
         super.onRestart();
         restarted = true;
-        Log.d(TAG, "kcxxx: onrestart");
+        Log.d(TAG, "onrestart");
     }
 
     @Override
     protected void onResume() {
         super.onResume();
-        Log.d(TAG, "kcxxx: onresume");
+        Log.d(TAG, "onresume");
     }
 
     @Override
     protected void onStop() {
         super.onStop();
-        Log.d(TAG, "kcxxx: onstop");
+        Log.d(TAG, "onstop");
         if (mAlertDialog != null) {
             mAlertDialog.dismiss();
         }
@@ -141,7 +140,7 @@ public class SplashActivity extends Activity implements LoadingTaskListener {
 
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-        Log.d(TAG, "kcxxx: onactivityresult: " + requestCode + " " + resultCode);
+        Log.d(TAG, "onactivityresult: " + requestCode + " " + resultCode);
     }
 
     private void startApp() {
@@ -159,7 +158,6 @@ public class SplashActivity extends Activity implements LoadingTaskListener {
 
     @Override
     public void onProgressUpdate(String value) {
-        Log.d(TAG, "onProgressUpdate> " + value);
         mProgressText.setText(value);
     }
 
@@ -167,10 +165,8 @@ public class SplashActivity extends Activity implements LoadingTaskListener {
     public void onPostExecute(String result) {
         Log.d(TAG, "onPostExecute> " + result);
         mTask = null;
-        Log.d(TAG, "progressbar...gone");
         mProgressBar.setVisibility(View.GONE);
         if (TextUtils.equals(result, LoadingTask.TASK_OK)) {
-            Log.d(TAG, "startApp");
             startApp();
         } else {
             String extra_text;
@@ -180,7 +176,6 @@ public class SplashActivity extends Activity implements LoadingTaskListener {
                 extra_text = "...Cancelled";
             Log.d(TAG, "progresstext += " + extra_text);
             mProgressText.setText(mProgressText.getText() + extra_text);
-            Log.d(TAG, "retrybutton...visible");
             mRetryButton.setVisibility(View.VISIBLE);
         }
     }