Added an android authenticator based on Apache 2.0 licensed code from https://github...
authorkenstir <kenstir@gmail.com>
Sat, 16 Nov 2013 02:25:02 +0000 (21:25 -0500)
committerkenstir <kenstir@gmail.com>
Sat, 16 Nov 2013 02:25:02 +0000 (21:25 -0500)
The main problem in the EG app in the android-minor-cleanup branch is that when the auth token expires or you rotate the device at the wrong time you get repeated crashes.  My hypothesis is that using native android.accounts facilities for getting access to auth tokens will clean up the auth handling and also make authentication accessible to Services.

27 files changed:
.gitignore
Open-ILS/src/Android/.gitignore
Open-ILS/src/AndroidAuthenticator/.classpath [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/.gitignore [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/.project [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/AndroidManifest.xml [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/libs/android-support-v4.jar [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/libs/org.opensrf2_serialized_reg.jar [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/project.properties [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/res/drawable-hdpi/evergreen_launcher_icon.png [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/res/drawable-hdpi/ic_launcher.png [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/res/drawable-ldpi/evergreen_launcher_icon.png [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/res/drawable-mdpi/evergreen_launcher_icon.png [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/res/drawable-mdpi/ic_launcher.png [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/res/drawable-xhdpi/evergreen_launcher_icon.png [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/res/drawable-xhdpi/ic_launcher.png [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/res/layout/activity_login.xml [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/res/values-v11/styles.xml [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/res/values-v14/styles.xml [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/res/values/strings.xml [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/res/values/styles.xml [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/res/xml/authenticator.xml [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/src/org/evergreen_ils/auth/Authenticator.java [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/src/org/evergreen_ils/auth/AuthenticatorActivity.java [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/src/org/evergreen_ils/auth/AuthenticatorService.java [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/src/org/evergreen_ils/auth/Const.java [new file with mode: 0644]
Open-ILS/src/AndroidAuthenticator/src/org/evergreen_ils/auth/EvergreenAuthenticate.java [new file with mode: 0644]

index 8cd128a..9c7a34d 100644 (file)
@@ -333,6 +333,3 @@ Open-ILS/examples/apache/eg.conf
 Open-ILS/examples/apache/eg_startup
 Open-ILS/examples/apache/eg_vhost.conf
 Open-ILS/src/support-scripts/eg_db_config
-Open-ILS/src/Android/.settings/org.eclipse.jdt.core.prefs
-Open-ILS/src/Android/local.properties
-Open-ILS/src/Android/proguard-project.txt
index 414f145..a284b92 100644 (file)
@@ -3,4 +3,6 @@
 *.dex
 bin/
 gen/
-
+.settings
+local.properties
+proguard-project.txt
diff --git a/Open-ILS/src/AndroidAuthenticator/.classpath b/Open-ILS/src/AndroidAuthenticator/.classpath
new file mode 100644 (file)
index 0000000..7bc01d9
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="src" path="gen"/>
+       <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+       <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+       <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
+       <classpathentry kind="output" path="bin/classes"/>
+</classpath>
diff --git a/Open-ILS/src/AndroidAuthenticator/.gitignore b/Open-ILS/src/AndroidAuthenticator/.gitignore
new file mode 100644 (file)
index 0000000..a284b92
--- /dev/null
@@ -0,0 +1,8 @@
+#ignore class objects
+*.class
+*.dex
+bin/
+gen/
+.settings
+local.properties
+proguard-project.txt
diff --git a/Open-ILS/src/AndroidAuthenticator/.project b/Open-ILS/src/AndroidAuthenticator/.project
new file mode 100644 (file)
index 0000000..a496b5e
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>EvergreenAuthenticator</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>com.android.ide.eclipse.adt.ApkBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/Open-ILS/src/AndroidAuthenticator/AndroidManifest.xml b/Open-ILS/src/AndroidAuthenticator/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..33341c4
--- /dev/null
@@ -0,0 +1,31 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="org.evergreen_ils.auth"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk
+        android:minSdkVersion="8"
+        android:targetSdkVersion="18" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/evergreen_launcher_icon"
+        android:label="@string/app_label"
+        android:theme="@style/AppTheme" >
+
+        <activity
+            android:name=".AuthenticatorActivity"
+            android:label="@string/title_sign_in" >
+        </activity>
+
+        <service android:name=".AuthenticatorService">
+            <intent-filter>
+                <action android:name="android.accounts.AccountAuthenticator" />
+            </intent-filter>
+            <meta-data android:name="android.accounts.AccountAuthenticator"
+                       android:resource="@xml/authenticator" />
+        </service>
+
+    </application>
+    
+</manifest>
diff --git a/Open-ILS/src/AndroidAuthenticator/libs/android-support-v4.jar b/Open-ILS/src/AndroidAuthenticator/libs/android-support-v4.jar
new file mode 100644 (file)
index 0000000..9056828
Binary files /dev/null and b/Open-ILS/src/AndroidAuthenticator/libs/android-support-v4.jar differ
diff --git a/Open-ILS/src/AndroidAuthenticator/libs/org.opensrf2_serialized_reg.jar b/Open-ILS/src/AndroidAuthenticator/libs/org.opensrf2_serialized_reg.jar
new file mode 100644 (file)
index 0000000..84c9b94
Binary files /dev/null and b/Open-ILS/src/AndroidAuthenticator/libs/org.opensrf2_serialized_reg.jar differ
diff --git a/Open-ILS/src/AndroidAuthenticator/project.properties b/Open-ILS/src/AndroidAuthenticator/project.properties
new file mode 100644 (file)
index 0000000..91d2b02
--- /dev/null
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
+android.library=true
diff --git a/Open-ILS/src/AndroidAuthenticator/res/drawable-hdpi/evergreen_launcher_icon.png b/Open-ILS/src/AndroidAuthenticator/res/drawable-hdpi/evergreen_launcher_icon.png
new file mode 100644 (file)
index 0000000..1f2baa4
Binary files /dev/null and b/Open-ILS/src/AndroidAuthenticator/res/drawable-hdpi/evergreen_launcher_icon.png differ
diff --git a/Open-ILS/src/AndroidAuthenticator/res/drawable-hdpi/ic_launcher.png b/Open-ILS/src/AndroidAuthenticator/res/drawable-hdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..96a442e
Binary files /dev/null and b/Open-ILS/src/AndroidAuthenticator/res/drawable-hdpi/ic_launcher.png differ
diff --git a/Open-ILS/src/AndroidAuthenticator/res/drawable-ldpi/evergreen_launcher_icon.png b/Open-ILS/src/AndroidAuthenticator/res/drawable-ldpi/evergreen_launcher_icon.png
new file mode 100644 (file)
index 0000000..a8da28a
Binary files /dev/null and b/Open-ILS/src/AndroidAuthenticator/res/drawable-ldpi/evergreen_launcher_icon.png differ
diff --git a/Open-ILS/src/AndroidAuthenticator/res/drawable-mdpi/evergreen_launcher_icon.png b/Open-ILS/src/AndroidAuthenticator/res/drawable-mdpi/evergreen_launcher_icon.png
new file mode 100644 (file)
index 0000000..63adfad
Binary files /dev/null and b/Open-ILS/src/AndroidAuthenticator/res/drawable-mdpi/evergreen_launcher_icon.png differ
diff --git a/Open-ILS/src/AndroidAuthenticator/res/drawable-mdpi/ic_launcher.png b/Open-ILS/src/AndroidAuthenticator/res/drawable-mdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..359047d
Binary files /dev/null and b/Open-ILS/src/AndroidAuthenticator/res/drawable-mdpi/ic_launcher.png differ
diff --git a/Open-ILS/src/AndroidAuthenticator/res/drawable-xhdpi/evergreen_launcher_icon.png b/Open-ILS/src/AndroidAuthenticator/res/drawable-xhdpi/evergreen_launcher_icon.png
new file mode 100644 (file)
index 0000000..2c2adb1
Binary files /dev/null and b/Open-ILS/src/AndroidAuthenticator/res/drawable-xhdpi/evergreen_launcher_icon.png differ
diff --git a/Open-ILS/src/AndroidAuthenticator/res/drawable-xhdpi/ic_launcher.png b/Open-ILS/src/AndroidAuthenticator/res/drawable-xhdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..71c6d76
Binary files /dev/null and b/Open-ILS/src/AndroidAuthenticator/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/Open-ILS/src/AndroidAuthenticator/res/layout/activity_login.xml b/Open-ILS/src/AndroidAuthenticator/res/layout/activity_login.xml
new file mode 100644 (file)
index 0000000..18c2302
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="fill_parent"
+              android:layout_height="fill_parent"
+              android:paddingLeft="17dp"
+              android:paddingRight="17dp"
+        >
+
+    <EditText android:id="@+id/accountName"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:layout_marginTop="20dp"
+              android:hint="@string/prompt_username"
+              android:inputType="textEmailAddress"
+              android:padding="10dp"
+            />
+
+    <EditText android:id="@+id/accountPassword"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:layout_marginTop="10dp"
+              android:inputType="textPassword"
+              android:hint="@string/prompt_password"
+              android:padding="10dp"
+            />
+
+    <Button android:id="@+id/submit"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:layout_gravity="right"
+              android:layout_marginTop="16dp"
+              android:text="@string/action_sign_in"
+              android:paddingLeft="32dp"
+              android:paddingRight="32dp"/>
+
+</LinearLayout>
+
diff --git a/Open-ILS/src/AndroidAuthenticator/res/values-v11/styles.xml b/Open-ILS/src/AndroidAuthenticator/res/values-v11/styles.xml
new file mode 100644 (file)
index 0000000..3c02242
--- /dev/null
@@ -0,0 +1,11 @@
+<resources>
+
+    <!--
+        Base application theme for API 11+. This theme completely replaces
+        AppBaseTheme from res/values/styles.xml on API 11+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+        <!-- API 11 theme customizations can go here. -->
+    </style>
+
+</resources>
diff --git a/Open-ILS/src/AndroidAuthenticator/res/values-v14/styles.xml b/Open-ILS/src/AndroidAuthenticator/res/values-v14/styles.xml
new file mode 100644 (file)
index 0000000..a91fd03
--- /dev/null
@@ -0,0 +1,12 @@
+<resources>
+
+    <!--
+        Base application theme for API 14+. This theme completely replaces
+        AppBaseTheme from BOTH res/values/styles.xml and
+        res/values-v11/styles.xml on API 14+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+        <!-- API 14 theme customizations can go here. -->
+    </style>
+
+</resources>
diff --git a/Open-ILS/src/AndroidAuthenticator/res/values/strings.xml b/Open-ILS/src/AndroidAuthenticator/res/values/strings.xml
new file mode 100644 (file)
index 0000000..150f3b1
--- /dev/null
@@ -0,0 +1,11 @@
+<resources>
+
+    <string name="app_name">EvergreenAuthenticator</string>
+    <string name="app_label">Evergreen</string>
+    <string name="gateway_url">http://bark.cwmars.org/osrf-gateway-v1</string>
+    <string name="action_sign_in">Sign in</string>
+    <string name="title_sign_in">Sign in</string>
+    <string name="prompt_password">Password</string>
+    <string name="prompt_username">Username</string>
+    
+</resources>
diff --git a/Open-ILS/src/AndroidAuthenticator/res/values/styles.xml b/Open-ILS/src/AndroidAuthenticator/res/values/styles.xml
new file mode 100644 (file)
index 0000000..6ce89c7
--- /dev/null
@@ -0,0 +1,20 @@
+<resources>
+
+    <!--
+        Base application theme, dependent on API level. This theme is replaced
+        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Light">
+        <!--
+            Theme customizations available in newer API levels can go in
+            res/values-vXX/styles.xml, while customizations related to
+            backward-compatibility can go here.
+        -->
+    </style>
+
+    <!-- Application theme. -->
+    <style name="AppTheme" parent="AppBaseTheme">
+        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+    </style>
+
+</resources>
diff --git a/Open-ILS/src/AndroidAuthenticator/res/xml/authenticator.xml b/Open-ILS/src/AndroidAuthenticator/res/xml/authenticator.xml
new file mode 100644 (file)
index 0000000..fd4c591
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
+                       android:accountType="org.evergreen_ils.opac"
+                       android:icon="@drawable/evergreen_launcher_icon"
+                       android:smallIcon="@drawable/evergreen_launcher_icon"
+                       android:label="@string/app_label"/>
diff --git a/Open-ILS/src/AndroidAuthenticator/src/org/evergreen_ils/auth/Authenticator.java b/Open-ILS/src/AndroidAuthenticator/src/org/evergreen_ils/auth/Authenticator.java
new file mode 100644 (file)
index 0000000..db0b242
--- /dev/null
@@ -0,0 +1,114 @@
+package org.evergreen_ils.auth;
+
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.accounts.NetworkErrorException;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+
+public class Authenticator extends AbstractAccountAuthenticator {
+    
+    private final String TAG = "eg.auth";
+    private Context context;
+
+    public Authenticator(Context context) {
+        super(context);
+        this.context = context;
+    }
+
+    @Override
+    public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
+        Log.d(TAG, "addaccount "+accountType+" "+authTokenType);
+        final Intent intent = new Intent(context, AuthenticatorActivity.class);
+        intent.putExtra(Const.AUTHTOKEN_TYPE, authTokenType);
+        intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
+        
+        Bundle result = new Bundle();
+        result.putParcelable(AccountManager.KEY_INTENT, intent);
+        return result;
+    }
+
+    @Override
+    public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
+        Log.d(TAG, "getAuthToken "+account.name);
+
+        // If the caller requested an authToken type we don't support, then
+        // return an error
+        if (!authTokenType.equals(Const.AUTHTOKEN_TYPE)) {
+            final Bundle result = new Bundle();
+            result.putString(AccountManager.KEY_ERROR_MESSAGE, "invalid authTokenType");
+            return result;
+        }
+
+        final AccountManager am = AccountManager.get(context);
+        String authToken = am.peekAuthToken(account, authTokenType);
+        Log.d(TAG, "peekAuthToken returned " + authToken);
+        if (TextUtils.isEmpty(authToken)) {
+            final String password = am.getPassword(account);
+            if (password != null) {
+                try {
+                    Log.d(TAG, "attempting to sign in with existing password");
+                    authToken = EvergreenAuthenticate.signIn(context, account.name, password);
+                } catch (Exception e) {
+                    Log.d(TAG, "caught exception "+e.getMessage());
+                    final Bundle result = new Bundle();
+                    result.putString(AccountManager.KEY_ERROR_MESSAGE, e.getMessage());
+                    return result;
+                }
+            }
+        }
+
+        // If we get an authToken - we return it
+        if (!TextUtils.isEmpty(authToken)) {
+            final Bundle result = new Bundle();
+            result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
+            result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
+            result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
+            return result;
+        }
+
+        // If we get here, then we couldn't access the user's password - so we
+        // need to re-prompt them for their credentials. We do that by creating
+        // an intent to display our AuthenticatorActivity.
+        final Intent intent = new Intent(context, AuthenticatorActivity.class);
+        intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
+        intent.putExtra(AuthenticatorActivity.ARG_ACCOUNT_TYPE, account.type);
+        intent.putExtra(AuthenticatorActivity.ARG_AUTH_TYPE, authTokenType);
+        intent.putExtra(AuthenticatorActivity.ARG_ACCOUNT_NAME, account.name);
+        final Bundle bundle = new Bundle();
+        bundle.putParcelable(AccountManager.KEY_INTENT, intent);
+        return bundle;
+    }
+
+    @Override
+    public String getAuthTokenLabel(String authTokenType) {
+        return Const.AUTHTOKEN_TYPE_LABEL;
+    }
+
+    @Override
+    public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
+        final Bundle result = new Bundle();
+        result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
+        return result;
+    }
+
+    @Override
+    public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
+        return null;
+    }
+
+    @Override
+    public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
+        return null;
+    }
+
+    @Override
+    public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
+        return null;
+    }
+}
diff --git a/Open-ILS/src/AndroidAuthenticator/src/org/evergreen_ils/auth/AuthenticatorActivity.java b/Open-ILS/src/AndroidAuthenticator/src/org/evergreen_ils/auth/AuthenticatorActivity.java
new file mode 100644 (file)
index 0000000..1ad73ef
--- /dev/null
@@ -0,0 +1,156 @@
+package org.evergreen_ils.auth;
+
+import android.accounts.AccountAuthenticatorActivity;
+import android.accounts.AccountManager;
+import android.accounts.Account;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class AuthenticatorActivity extends AccountAuthenticatorActivity {
+
+    private final String TAG = "eg.auth";
+
+    public final static String ARG_ACCOUNT_TYPE = "ACCOUNT_TYPE";
+    public final static String ARG_AUTH_TYPE = "AUTH_TYPE";
+    public final static String ARG_ACCOUNT_NAME = "ACCOUNT_NAME";
+    public final static String ARG_IS_ADDING_NEW_ACCOUNT = "IS_ADDING_ACCOUNT";
+
+    public static final String KEY_ERROR_MESSAGE = "ERR_MSG";
+
+    public final static String PARAM_USER_PASS = "USER_PASS";
+
+    private final int REQ_SIGNUP = 1;
+
+    private AccountManager accountManager;
+    private String authTokenType;
+
+    /**
+     * Called when the activity is first created.
+     */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_login);
+        accountManager = AccountManager.get(getBaseContext());
+
+        String accountName = getIntent().getStringExtra(ARG_ACCOUNT_NAME);
+        authTokenType = getIntent().getStringExtra(ARG_AUTH_TYPE);
+        if (authTokenType == null)
+            authTokenType = Const.AUTHTOKEN_TYPE;
+
+        if (accountName != null) {
+            ((TextView) findViewById(R.id.accountName)).setText(accountName);
+        }
+
+        findViewById(R.id.submit).setOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        submit();
+                    }
+                });
+        /*
+         * findViewById(R.id.signUp).setOnClickListener(new
+         * View.OnClickListener() {
+         * 
+         * @Override public void onClick(View v) { // Since there can only be
+         * one AuthenticatorActivity, we call the sign up activity, get his
+         * results, // and return them in setAccountAuthenticatorResult(). See
+         * finishLogin(). Intent signup = new Intent(getBaseContext(),
+         * SignUpActivity.class); signup.putExtras(getIntent().getExtras());
+         * startActivityForResult(signup, REQ_SIGNUP); } });
+         */
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+
+        // The sign up activity returned that the user has successfully created
+        // an account
+        if (requestCode == REQ_SIGNUP && resultCode == RESULT_OK) {
+            finishLogin(data);
+        } else
+            super.onActivityResult(requestCode, resultCode, data);
+    }
+
+    public void submit() {
+
+        final String username = ((TextView) findViewById(R.id.accountName)).getText().toString();
+        final String password = ((TextView) findViewById(R.id.accountPassword)).getText().toString();
+        //final String account_type = getIntent().getStringExtra(ARG_ACCOUNT_TYPE);
+
+        new AsyncTask<String, Void, Intent>() {
+
+            @Override
+            protected Intent doInBackground(String... params) {
+
+                Log.d("udinic", TAG + "> Started authenticating");
+
+                String authtoken = null;
+                Bundle data = new Bundle();
+                try {
+                    authtoken = EvergreenAuthenticate.signIn(AuthenticatorActivity.this, username, password);
+
+                    data.putString(AccountManager.KEY_ACCOUNT_NAME, username);
+                    data.putString(AccountManager.KEY_ACCOUNT_TYPE, Const.ACCOUNT_TYPE);
+                    data.putString(AccountManager.KEY_AUTHTOKEN, authtoken);
+                    data.putString(PARAM_USER_PASS, password);
+
+                } catch (Exception e) {
+                    data.putString(KEY_ERROR_MESSAGE, e.getMessage());
+                }
+
+                final Intent res = new Intent();
+                res.putExtras(data);
+                return res;
+            }
+
+            @Override
+            protected void onPostExecute(Intent intent) {
+                if (intent.hasExtra(KEY_ERROR_MESSAGE)) {
+                    Toast.makeText(getBaseContext(),
+                            intent.getStringExtra(KEY_ERROR_MESSAGE),
+                            Toast.LENGTH_SHORT).show();
+                } else {
+                    finishLogin(intent);
+                }
+            }
+        }.execute();
+    }
+
+    private void finishLogin(Intent intent) {
+        Log.d(TAG, "> finishLogin");
+
+        String accountName = intent
+                .getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
+        String accountPassword = intent.getStringExtra(PARAM_USER_PASS);
+        final Account account = new Account(accountName,
+                intent.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE));
+
+        if (getIntent().getBooleanExtra(ARG_IS_ADDING_NEW_ACCOUNT, false)) {
+            Log.d("udinic", TAG + "> finishLogin > addAccountExplicitly");
+            String authtoken = intent
+                    .getStringExtra(AccountManager.KEY_AUTHTOKEN);
+            String authtokenType = authTokenType;
+
+            // Creating the account on the device and setting the auth token we
+            // got
+            // (Not setting the auth token will cause another call to the server
+            // to authenticate the user)
+            accountManager.addAccountExplicitly(account, accountPassword, null);
+            accountManager.setAuthToken(account, authtokenType, authtoken);
+        } else {
+            Log.d(TAG, "finishLogin > setPassword");
+            accountManager.setPassword(account, accountPassword);
+        }
+
+        setAccountAuthenticatorResult(intent.getExtras());
+        setResult(RESULT_OK, intent);
+        finish();
+    }
+}
diff --git a/Open-ILS/src/AndroidAuthenticator/src/org/evergreen_ils/auth/AuthenticatorService.java b/Open-ILS/src/AndroidAuthenticator/src/org/evergreen_ils/auth/AuthenticatorService.java
new file mode 100644 (file)
index 0000000..3a54fe8
--- /dev/null
@@ -0,0 +1,14 @@
+package org.evergreen_ils.auth;
+
+import org.evergreen_ils.auth.Authenticator;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class AuthenticatorService extends Service {
+    @Override
+    public IBinder onBind(Intent arg0) {
+        return new Authenticator(this).getIBinder();
+    }
+}
diff --git a/Open-ILS/src/AndroidAuthenticator/src/org/evergreen_ils/auth/Const.java b/Open-ILS/src/AndroidAuthenticator/src/org/evergreen_ils/auth/Const.java
new file mode 100644 (file)
index 0000000..0f114a6
--- /dev/null
@@ -0,0 +1,7 @@
+package org.evergreen_ils.auth;
+
+public class Const {
+    public static final String ACCOUNT_TYPE = "org.evergreen-ils.opac";
+    public static final String AUTHTOKEN_TYPE = "opac";
+    public static final String AUTHTOKEN_TYPE_LABEL = "Online Public Access Catalog";
+}
diff --git a/Open-ILS/src/AndroidAuthenticator/src/org/evergreen_ils/auth/EvergreenAuthenticate.java b/Open-ILS/src/AndroidAuthenticator/src/org/evergreen_ils/auth/EvergreenAuthenticate.java
new file mode 100644 (file)
index 0000000..3831d57
--- /dev/null
@@ -0,0 +1,112 @@
+package org.evergreen_ils.auth;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+
+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;
+
+public class EvergreenAuthenticate {
+    private final static String TAG = "eg.auth";
+    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";
+
+    private static String md5(String s) {
+        try {
+            MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
+            digest.update(s.getBytes());
+            byte messageDigest[] = digest.digest();
+
+            // Create Hex String
+            StringBuffer hexString = new StringBuffer();
+            for (int i = 0; i < messageDigest.length; i++) {
+                String hex = Integer.toHexString(0xFF & messageDigest[i]);
+                if (hex.length() == 1) {
+                    // could use a for loop, but we're only dealing with a
+                    // single byte
+                    hexString.append('0');
+                }
+                hexString.append(hex);
+            }
+            return hexString.toString();
+
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        }
+
+        return "";
+    }
+
+    public static Object doRequest(HttpConnection conn, String service, String methodName, Object[] params) throws Exception {
+        Method method = new Method(methodName);
+
+        Log.d(TAG, "doRequest Method :" + methodName + ":");
+        for (int i = 0; i < params.length; i++) {
+            method.addParam(params[i]);
+            Log.d(TAG, "Param " + i + ": " + params[i]);
+        }
+
+        // sync request
+        HttpRequest req = new GatewayRequest(conn, service, method).send();
+        Object resp;
+
+        while ((resp = req.recv()) != null) {
+            Log.d(TAG, "Sync Response: " + resp);
+            Object response = (Object) resp;
+            return response;
+        }
+        return null;
+    }
+    
+    @SuppressWarnings("unchecked")
+    public static String signIn(Context context, String username, String password) throws Exception {
+        Log.d(TAG, "signIn "+username);
+
+        HttpConnection conn = new HttpConnection(context.getString(R.string.gateway_url));
+
+        // step 1: get seed
+        Object resp = doRequest(conn, SERVICE_AUTH, METHOD_AUTH_INIT, new Object[] { username });
+        if (resp == null)
+            throw new Exception("Unable to contact login service");
+        String seed = resp.toString();
+
+        // step 2: complete auth with seed + password
+        HashMap<String, String> complexParam = new HashMap<String, String>();
+        complexParam.put("type", "opac");
+        complexParam.put("username", username);
+        complexParam.put("password", md5(seed + md5(password)));
+        resp = doRequest(conn, SERVICE_AUTH, METHOD_AUTH_COMPLETE, new Object[] { complexParam });
+        if (resp == null)
+            throw new Exception("Unable to complete login");
+        
+        // parse response
+        String textcode = ((Map<String, String>) resp).get("textcode");
+        System.out.println("textcode: " + textcode);
+        if (textcode.equals("SUCCESS")) {
+            Object payload = ((Map<String, String>) resp).get("payload");
+            System.out.println("payload: " + payload);
+            String authtoken = ((Map<String, String>) payload).get("authtoken");
+            System.out.println("authtoken: " + authtoken);
+            Integer authtime = ((Map<String, Integer>) payload).get("authtime");
+            System.out.println("authtime: " + authtime);
+            return authtoken;
+        } else if (textcode.equals("LOGIN_FAILED")) {
+            String desc = ((Map<String, String>) resp).get("desc");
+            System.out.println("desc: "+desc);
+            if (!TextUtils.isEmpty(desc)) {
+                throw new Exception(desc);
+            }
+        }
+        
+        throw new Exception("Login failed");
+    }
+}