From 914cc6c81903100d68c0c4b95006c9a58b0fe712 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Mon, 23 Oct 2017 11:30:18 +0200 Subject: [PATCH] restore PluginSDK --- src/java/Keepass2AndroidPluginSDK/.classpath | 9 + src/java/Keepass2AndroidPluginSDK/.project | 33 ++ .../.settings/org.eclipse.jdt.core.prefs | 4 + .../org.eclipse.ltk.core.refactoring.prefs | 2 + .../AndroidManifest.xml | 18 ++ .../bin/keepass2androidpluginsdk.jar | Bin 0 -> 20359 bytes src/java/Keepass2AndroidPluginSDK/lint.xml | 3 + .../proguard-project.txt | 20 ++ .../project.properties | 15 + .../res/drawable-hdpi/ic_launcher.png | Bin 0 -> 9397 bytes .../res/drawable-mdpi/ic_launcher.png | Bin 0 -> 5237 bytes .../res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 14383 bytes .../res/values-v11/styles.xml | 11 + .../res/values-v14/styles.xml | 12 + .../res/values-w820dp/dimens.xml | 10 + .../res/values/dimens.xml | 7 + .../res/values/strings.xml | 6 + .../res/values/styles.xml | 20 ++ .../pluginsdk/AccessManager.java | 201 ++++++++++++ .../pluginsdk/KeepassDefs.java | 48 +++ .../pluginsdk/Kp2aControl.java | 111 +++++++ .../PluginAccessBroadcastReceiver.java | 101 ++++++ .../pluginsdk/PluginAccessException.java | 21 ++ .../PluginActionBroadcastReceiver.java | 303 ++++++++++++++++++ .../keepass2android/pluginsdk/Strings.java | 190 +++++++++++ 25 files changed, 1145 insertions(+) create mode 100644 src/java/Keepass2AndroidPluginSDK/.classpath create mode 100644 src/java/Keepass2AndroidPluginSDK/.project create mode 100644 src/java/Keepass2AndroidPluginSDK/.settings/org.eclipse.jdt.core.prefs create mode 100644 src/java/Keepass2AndroidPluginSDK/.settings/org.eclipse.ltk.core.refactoring.prefs create mode 100644 src/java/Keepass2AndroidPluginSDK/AndroidManifest.xml create mode 100644 src/java/Keepass2AndroidPluginSDK/bin/keepass2androidpluginsdk.jar create mode 100644 src/java/Keepass2AndroidPluginSDK/lint.xml create mode 100644 src/java/Keepass2AndroidPluginSDK/proguard-project.txt create mode 100644 src/java/Keepass2AndroidPluginSDK/project.properties create mode 100644 src/java/Keepass2AndroidPluginSDK/res/drawable-hdpi/ic_launcher.png create mode 100644 src/java/Keepass2AndroidPluginSDK/res/drawable-mdpi/ic_launcher.png create mode 100644 src/java/Keepass2AndroidPluginSDK/res/drawable-xhdpi/ic_launcher.png create mode 100644 src/java/Keepass2AndroidPluginSDK/res/values-v11/styles.xml create mode 100644 src/java/Keepass2AndroidPluginSDK/res/values-v14/styles.xml create mode 100644 src/java/Keepass2AndroidPluginSDK/res/values-w820dp/dimens.xml create mode 100644 src/java/Keepass2AndroidPluginSDK/res/values/dimens.xml create mode 100644 src/java/Keepass2AndroidPluginSDK/res/values/strings.xml create mode 100644 src/java/Keepass2AndroidPluginSDK/res/values/styles.xml create mode 100644 src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/AccessManager.java create mode 100644 src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/KeepassDefs.java create mode 100644 src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/Kp2aControl.java create mode 100644 src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/PluginAccessBroadcastReceiver.java create mode 100644 src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/PluginAccessException.java create mode 100644 src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/PluginActionBroadcastReceiver.java create mode 100644 src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/Strings.java diff --git a/src/java/Keepass2AndroidPluginSDK/.classpath b/src/java/Keepass2AndroidPluginSDK/.classpath new file mode 100644 index 00000000..7bc01d9a --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/java/Keepass2AndroidPluginSDK/.project b/src/java/Keepass2AndroidPluginSDK/.project new file mode 100644 index 00000000..ee51db99 --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/.project @@ -0,0 +1,33 @@ + + + Keepass2AndroidPluginSDK + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/src/java/Keepass2AndroidPluginSDK/.settings/org.eclipse.jdt.core.prefs b/src/java/Keepass2AndroidPluginSDK/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..b080d2dd --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/src/java/Keepass2AndroidPluginSDK/.settings/org.eclipse.ltk.core.refactoring.prefs b/src/java/Keepass2AndroidPluginSDK/.settings/org.eclipse.ltk.core.refactoring.prefs new file mode 100644 index 00000000..b196c64a --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/.settings/org.eclipse.ltk.core.refactoring.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false diff --git a/src/java/Keepass2AndroidPluginSDK/AndroidManifest.xml b/src/java/Keepass2AndroidPluginSDK/AndroidManifest.xml new file mode 100644 index 00000000..91aef12e --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + + + + + + diff --git a/src/java/Keepass2AndroidPluginSDK/bin/keepass2androidpluginsdk.jar b/src/java/Keepass2AndroidPluginSDK/bin/keepass2androidpluginsdk.jar new file mode 100644 index 0000000000000000000000000000000000000000..d250b0a8e0a0e46fc205a077d4e89e8b810e63a6 GIT binary patch literal 20359 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 literal 0 HcmV?d00001 diff --git a/src/java/Keepass2AndroidPluginSDK/lint.xml b/src/java/Keepass2AndroidPluginSDK/lint.xml new file mode 100644 index 00000000..ee0eead5 --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/lint.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/java/Keepass2AndroidPluginSDK/proguard-project.txt b/src/java/Keepass2AndroidPluginSDK/proguard-project.txt new file mode 100644 index 00000000..f2fe1559 --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/src/java/Keepass2AndroidPluginSDK/project.properties b/src/java/Keepass2AndroidPluginSDK/project.properties new file mode 100644 index 00000000..91d2b024 --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/project.properties @@ -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/src/java/Keepass2AndroidPluginSDK/res/drawable-hdpi/ic_launcher.png b/src/java/Keepass2AndroidPluginSDK/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..96a442e5b8e9394ccf50bab9988cb2316026245d GIT binary patch literal 9397 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 literal 0 HcmV?d00001 diff --git a/src/java/Keepass2AndroidPluginSDK/res/drawable-xhdpi/ic_launcher.png b/src/java/Keepass2AndroidPluginSDK/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..71c6d760f05183ef8a47c614d8d13380c8528499 GIT binary patch literal 14383 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 literal 0 HcmV?d00001 diff --git a/src/java/Keepass2AndroidPluginSDK/res/values-v11/styles.xml b/src/java/Keepass2AndroidPluginSDK/res/values-v11/styles.xml new file mode 100644 index 00000000..3c02242a --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/res/values-v11/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/java/Keepass2AndroidPluginSDK/res/values-v14/styles.xml b/src/java/Keepass2AndroidPluginSDK/res/values-v14/styles.xml new file mode 100644 index 00000000..a91fd037 --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/res/values-v14/styles.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/src/java/Keepass2AndroidPluginSDK/res/values-w820dp/dimens.xml b/src/java/Keepass2AndroidPluginSDK/res/values-w820dp/dimens.xml new file mode 100644 index 00000000..f3e70203 --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/res/values-w820dp/dimens.xml @@ -0,0 +1,10 @@ + + + + 64dp + + diff --git a/src/java/Keepass2AndroidPluginSDK/res/values/dimens.xml b/src/java/Keepass2AndroidPluginSDK/res/values/dimens.xml new file mode 100644 index 00000000..55c1e590 --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/res/values/dimens.xml @@ -0,0 +1,7 @@ + + + + 16dp + 16dp + + diff --git a/src/java/Keepass2AndroidPluginSDK/res/values/strings.xml b/src/java/Keepass2AndroidPluginSDK/res/values/strings.xml new file mode 100644 index 00000000..5f5b7531 --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + Keepass2Android Plugin SDK + + diff --git a/src/java/Keepass2AndroidPluginSDK/res/values/styles.xml b/src/java/Keepass2AndroidPluginSDK/res/values/styles.xml new file mode 100644 index 00000000..6ce89c7b --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/res/values/styles.xml @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/AccessManager.java b/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/AccessManager.java new file mode 100644 index 00000000..c8fe8d56 --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/AccessManager.java @@ -0,0 +1,201 @@ +package keepass2android.pluginsdk; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +import org.json.JSONArray; +import org.json.JSONException; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.preference.PreferenceManager; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.widget.PopupMenu; + + +public class AccessManager +{ + + private static final String _tag = "Kp2aPluginSDK"; + private static final String PREF_KEY_SCOPE = "scope"; + private static final String PREF_KEY_TOKEN = "token"; + + public static String stringArrayToString(ArrayList values) { + JSONArray a = new JSONArray(); + for (int i = 0; i < values.size(); i++) { + a.put(values.get(i)); + } + if (!values.isEmpty()) { + return a.toString(); + } else { + return null; + } + + } + + public static ArrayList stringToStringArray(String s) { + ArrayList strings = new ArrayList(); + if (TextUtils.isEmpty(s) == false) { + try { + JSONArray a = new JSONArray(s); + for (int i = 0; i < a.length(); i++) { + String url = a.optString(i); + strings.add(url); + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + return strings; + } + + public static void storeAccessToken(Context ctx, String hostPackage, String accessToken, ArrayList scopes) + { + SharedPreferences prefs = getPrefsForHost(ctx, hostPackage); + + Editor edit = prefs.edit(); + edit.putString(PREF_KEY_TOKEN, accessToken); + String scopesString = stringArrayToString(scopes); + edit.putString(PREF_KEY_SCOPE, scopesString); + edit.commit(); + Log.d(_tag, "stored access token " + accessToken.substring(0, 4)+"... for "+scopes.size()+" scopes ("+scopesString+")."); + + SharedPreferences hostPrefs = ctx.getSharedPreferences("KP2A.PluginAccess.hosts", Context.MODE_PRIVATE); + if (!hostPrefs.contains(hostPackage)) + { + hostPrefs.edit().putString(hostPackage, "").commit(); + } + + + } + + public static void preparePopup(Object popupMenu) + { + try + { + Field[] fields = popupMenu.getClass().getDeclaredFields(); + for (Field field : fields) { + if ("mPopup".equals(field.getName())) { + field.setAccessible(true); + Object menuPopupHelper = field.get(popupMenu); + Class classPopupHelper = Class.forName(menuPopupHelper + .getClass().getName()); + Method setForceIcons = classPopupHelper.getMethod( + "setForceShowIcon", boolean.class); + setForceIcons.invoke(menuPopupHelper, true); + break; + } + } + + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + private static SharedPreferences getPrefsForHost(Context ctx, + String hostPackage) { + SharedPreferences prefs = ctx.getSharedPreferences("KP2A.PluginAccess."+hostPackage, Context.MODE_PRIVATE); + return prefs; + } + + public static String tryGetAccessToken(Context ctx, String hostPackage, ArrayList scopes) { + + if (TextUtils.isEmpty(hostPackage)) + { + Log.d(_tag, "hostPackage is empty!"); + return null; + } + Log.d(_tag, "trying to find prefs for "+hostPackage); + SharedPreferences prefs = getPrefsForHost(ctx, hostPackage); + String scopesString = prefs.getString(PREF_KEY_SCOPE, ""); + Log.d(_tag, "available scopes: "+ scopesString); + ArrayList currentScope = stringToStringArray(scopesString); + if (isSubset(scopes, currentScope)) + { + return prefs.getString(PREF_KEY_TOKEN, null); + } + else + { + Log.d(_tag, "looks like scope changed. Access token invalid."); + return null; + } + } + + public static boolean isSubset(ArrayList requiredScopes, + ArrayList availableScopes) { + for (String r: requiredScopes){ + if (availableScopes.indexOf(r)<0) + { + Log.d(_tag, "Scope "+r+" not available. "+availableScopes.size()); + return false; + } + } + return true; + } + + public static void removeAccessToken(Context ctx, String hostPackage, + String accessToken) { + SharedPreferences prefs = getPrefsForHost(ctx, hostPackage); + + Log.d(_tag, "removing AccessToken."); + if (prefs.getString(PREF_KEY_TOKEN, "").equals(accessToken)) + { + Editor edit = prefs.edit(); + edit.clear(); + edit.commit(); + + } + + SharedPreferences hostPrefs = ctx.getSharedPreferences("KP2A.PluginAccess.hosts", Context.MODE_PRIVATE); + if (hostPrefs.contains(hostPackage)) + { + hostPrefs.edit().remove(hostPackage).commit(); + } + + } + + public static Set getAllHostPackages(Context ctx) + { + SharedPreferences prefs = ctx.getSharedPreferences("KP2A.PluginAccess.hosts", Context.MODE_PRIVATE); + Set result = new HashSet(); + for (String host: prefs.getAll().keySet()) + { + try + { + PackageInfo info = ctx.getPackageManager().getPackageInfo(host, PackageManager.GET_META_DATA); + //if we get here, the package is still there + result.add(host); + } + catch (PackageManager.NameNotFoundException e) + { + //host gone. ignore. + } + } + return result; + + } + + + + /** + * Returns a valid access token or throws PluginAccessException + */ + public static String getAccessToken(Context context, String hostPackage, + ArrayList scopes) throws PluginAccessException { + String accessToken = tryGetAccessToken(context, hostPackage, scopes); + if (accessToken == null) + throw new PluginAccessException(hostPackage, scopes); + return accessToken; + } +} diff --git a/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/KeepassDefs.java b/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/KeepassDefs.java new file mode 100644 index 00000000..7dd2e706 --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/KeepassDefs.java @@ -0,0 +1,48 @@ +package keepass2android.pluginsdk; + +public class KeepassDefs { + + /// + /// Default identifier string for the title field. Should not contain + /// spaces, tabs or other whitespace. + /// + public static String TitleField = "Title"; + + /// + /// Default identifier string for the user name field. Should not contain + /// spaces, tabs or other whitespace. + /// + public static String UserNameField = "UserName"; + + /// + /// Default identifier string for the password field. Should not contain + /// spaces, tabs or other whitespace. + /// + public static String PasswordField = "Password"; + + /// + /// Default identifier string for the URL field. Should not contain + /// spaces, tabs or other whitespace. + /// + public static String UrlField = "URL"; + + /// + /// Default identifier string for the notes field. Should not contain + /// spaces, tabs or other whitespace. + /// + public static String NotesField = "Notes"; + + + public static boolean IsStandardField(String strFieldName) + { + if(strFieldName == null) + return false; + if(strFieldName.equals(TitleField)) return true; + if(strFieldName.equals(UserNameField)) return true; + if(strFieldName.equals(PasswordField)) return true; + if(strFieldName.equals(UrlField)) return true; + if(strFieldName.equals(NotesField)) return true; + + return false; + } +} diff --git a/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/Kp2aControl.java b/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/Kp2aControl.java new file mode 100644 index 00000000..84bfc4e4 --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/Kp2aControl.java @@ -0,0 +1,111 @@ +package keepass2android.pluginsdk; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.content.Intent; +import android.text.TextUtils; + +public class Kp2aControl { + + /** + * Creates and returns an intent to launch Keepass2Android for adding an entry with the given fields. + * @param fields Key/Value pairs of the field values. See KeepassDefs for standard keys. + * @param protectedFields List of keys of the protected fields. + * @return Intent to start Keepass2Android. + * @throws JSONException + */ + public static Intent getAddEntryIntent(HashMap fields, ArrayList protectedFields) + { + return getAddEntryIntent(new JSONObject(fields).toString(), protectedFields); + } + + public static Intent getAddEntryIntent(String outputData, ArrayList protectedFields) + { + Intent startKp2aIntent = new Intent(Strings.ACTION_START_WITH_TASK); + startKp2aIntent.addCategory(Intent.CATEGORY_DEFAULT); + startKp2aIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + startKp2aIntent.putExtra("KP2A_APPTASK", "CreateEntryThenCloseTask"); + startKp2aIntent.putExtra("ShowUserNotifications", "false"); //KP2A expects a StringExtra + startKp2aIntent.putExtra(Strings.EXTRA_ENTRY_OUTPUT_DATA, outputData); + if (protectedFields != null) + startKp2aIntent.putStringArrayListExtra(Strings.EXTRA_PROTECTED_FIELDS_LIST, protectedFields); + + + return startKp2aIntent; + } + + + /** + * Creates an intent to open a Password Entry matching searchText + * @param searchText queryString + * @param showUserNotifications if true, the notifications (copy to clipboard, keyboard) are displayed + * @param closeAfterOpen if true, the entry is opened and KP2A is immediately closed + * @return Intent to start KP2A with + */ + public static Intent getOpenEntryIntent(String searchText, boolean showUserNotifications, boolean closeAfterOpen) + { + Intent startKp2aIntent = new Intent(Strings.ACTION_START_WITH_TASK); + startKp2aIntent.addCategory(Intent.CATEGORY_DEFAULT); + startKp2aIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + startKp2aIntent.putExtra("KP2A_APPTASK", "SearchUrlTask"); + startKp2aIntent.putExtra("ShowUserNotifications", String.valueOf(showUserNotifications)); + startKp2aIntent.putExtra("CloseAfterCreate", String.valueOf(closeAfterOpen)); + startKp2aIntent.putExtra("UrlToSearch", searchText); + return startKp2aIntent; + } + + /** + * Creates an intent to query a password entry from KP2A. The credentials are returned as Activity result. + * @param searchText Text to search for. Should be a URL or "androidapp://com.my.package." + * @return an Intent to start KP2A with + */ + public static Intent getQueryEntryIntent(String searchText) + { + Intent i = new Intent(Strings.ACTION_QUERY_CREDENTIALS); + if (!TextUtils.isEmpty(searchText)) + i.putExtra(Strings.EXTRA_QUERY_STRING, searchText); + return i; + } + + /** + * Creates an intent to query a password entry from KP2A, matching to the current app's package . + * The credentials are returned as Activity result. + * This requires SCOPE_QUERY_CREDENTIALS_FOR_OWN_PACKAGE. + * @return an Intent to start KP2A with + */ + public static Intent getQueryEntryIntentForOwnPackage() + { + return new Intent(Strings.ACTION_QUERY_CREDENTIALS_FOR_OWN_PACKAGE); + } + + /** + * Converts the entry fields returned in an intent from a query to a hashmap. + * @param intent data received in onActivityResult after getQueryEntryIntent(ForOwnPackage) + * @return HashMap with keys = field names (see KeepassDefs for standard keys) and values = values + */ + public static HashMap getEntryFieldsFromIntent(Intent intent) + { + HashMap res = new HashMap(); + try { + JSONObject json = new JSONObject(intent.getStringExtra(Strings.EXTRA_ENTRY_OUTPUT_DATA)); + for(Iterator iter = json.keys();iter.hasNext();) { + String key = iter.next(); + String value = json.get(key).toString(); + res.put(key, value); + } + + } catch (JSONException e) { + e.printStackTrace(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + return res; + } + +} diff --git a/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/PluginAccessBroadcastReceiver.java b/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/PluginAccessBroadcastReceiver.java new file mode 100644 index 00000000..3f3d6f13 --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/PluginAccessBroadcastReceiver.java @@ -0,0 +1,101 @@ +package keepass2android.pluginsdk; + +import java.util.ArrayList; + +import android.R.anim; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +/** + * Broadcast flow between Host and Plugin + * ====================================== + * + * The host is responsible for deciding when to initiate the session. It + * should initiate the session as soon as plugins are required or when a plugin + * has been updated through the OS. + * It will then send a broadcast to request the currently required scope. + * The plugin then sends a broadcast to the app which scope is required. If an + * access token is already available, it's sent along with the requset. + * + * If a previous permission has been revoked (or the app settings cleared or the + * permissions have been extended or the token is invalid for any other reason) + * the host will answer with a Revoked-Permission broadcast (i.e. the plugin is + * unconnected.) + * + * Unconnected plugins must be permitted by the user (requiring user action). + * When the user grants access, the plugin will receive an access token for + * the host. This access token is valid for the requested scope. If the scope + * changes (e.g after an update of the plugin), the access token becomes invalid. + * + */ +public abstract class PluginAccessBroadcastReceiver extends BroadcastReceiver { + + private static final String _tag = "Kp2aPluginSDK"; + + @Override + public void onReceive(Context ctx, Intent intent) { + String action = intent.getAction(); + android.util.Log.d(_tag, "received broadcast with action="+action); + if (action == null) + return; + if (action.equals(Strings.ACTION_TRIGGER_REQUEST_ACCESS)) + { + requestAccess(ctx, intent); + } else if (action.equals(Strings.ACTION_RECEIVE_ACCESS)) + { + receiveAccess(ctx, intent); + } else if (action.equals(Strings.ACTION_REVOKE_ACCESS)) + { + revokeAccess(ctx, intent); + } else + { + //TODO handle unexpected action + } + } + + + + private void revokeAccess(Context ctx, Intent intent) { + String senderPackage = intent.getStringExtra(Strings.EXTRA_SENDER); + String accessToken = intent.getStringExtra(Strings.EXTRA_ACCESS_TOKEN); + //this intent must send the invalid(ated) token to prevent malicious apps + //from revoking access of all plugins. + AccessManager.removeAccessToken(ctx, senderPackage, accessToken); + } + + + + private void receiveAccess(Context ctx, Intent intent) { + String senderPackage = intent.getStringExtra(Strings.EXTRA_SENDER); + String accessToken = intent.getStringExtra(Strings.EXTRA_ACCESS_TOKEN); + AccessManager.storeAccessToken(ctx, senderPackage, accessToken, getScopes()); + } + + public void requestAccess(Context ctx, Intent intent) { + String senderPackage = intent.getStringExtra(Strings.EXTRA_SENDER); + String requestToken = intent.getStringExtra(Strings.EXTRA_REQUEST_TOKEN); + Intent rpi = new Intent(Strings.ACTION_REQUEST_ACCESS); + rpi.setPackage(senderPackage); + rpi.putExtra(Strings.EXTRA_SENDER, ctx.getPackageName()); + rpi.putExtra(Strings.EXTRA_REQUEST_TOKEN, requestToken); + + String token = AccessManager.tryGetAccessToken(ctx, senderPackage, getScopes()); + if (token != null) + { + rpi.putExtra(Strings.EXTRA_ACCESS_TOKEN, token); + } + + rpi.putStringArrayListExtra(Strings.EXTRA_SCOPES, getScopes()); + Log.d(_tag, "requesting access for "+getScopes().size()+" tokens."); + ctx.sendBroadcast(rpi); + } + + /** + * + * @return the list of required scopes for this plugin. + */ + abstract public ArrayList getScopes(); + +} diff --git a/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/PluginAccessException.java b/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/PluginAccessException.java new file mode 100644 index 00000000..5f2a73ad --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/PluginAccessException.java @@ -0,0 +1,21 @@ +package keepass2android.pluginsdk; + +import java.util.ArrayList; + +public class PluginAccessException extends Exception { + + public PluginAccessException(String what) + { + super(what); + } + + public PluginAccessException(String hostPackage, ArrayList scopes) { + + } + + /** + * + */ + private static final long serialVersionUID = 1L; + +} diff --git a/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/PluginActionBroadcastReceiver.java b/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/PluginActionBroadcastReceiver.java new file mode 100644 index 00000000..3d924ad8 --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/PluginActionBroadcastReceiver.java @@ -0,0 +1,303 @@ +package keepass2android.pluginsdk; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import org.json.JSONException; +import org.json.JSONObject; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +public abstract class PluginActionBroadcastReceiver extends BroadcastReceiver { + + protected abstract class PluginActionBase + { + protected Context _context; + protected Intent _intent; + + + public PluginActionBase(Context context, Intent intent) + { + _context = context; + _intent = intent; + } + + public String getHostPackage() { + return _intent.getStringExtra(Strings.EXTRA_SENDER); + } + + public Context getContext() + { + return _context; + } + + } + + protected abstract class PluginEntryActionBase extends PluginActionBase + { + + public PluginEntryActionBase(Context context, Intent intent) + { + super(context, intent); + } + + protected HashMap getEntryFieldsFromIntent() + { + HashMap res = new HashMap(); + try { + JSONObject json = new JSONObject(_intent.getStringExtra(Strings.EXTRA_ENTRY_OUTPUT_DATA)); + for(Iterator iter = json.keys();iter.hasNext();) { + String key = iter.next(); + String value = json.get(key).toString(); + Log.d("KP2APluginSDK", "received " + key+"/"+value); + res.put(key, value); + } + + } catch (JSONException e) { + e.printStackTrace(); + } + return res; + } + + protected String[] getProtectedFieldsListFromIntent() + { + return _intent.getStringArrayExtra(Strings.EXTRA_PROTECTED_FIELDS_LIST); + } + + + public String getEntryId() + { + return _intent.getStringExtra(Strings.EXTRA_ENTRY_ID); + } + + + public void setEntryField(String fieldId, String fieldValue, boolean isProtected) throws PluginAccessException + { + Intent i = new Intent(Strings.ACTION_SET_ENTRY_FIELD); + ArrayList scope = new ArrayList(); + scope.add(Strings.SCOPE_CURRENT_ENTRY); + i.putExtra(Strings.EXTRA_ACCESS_TOKEN, AccessManager.getAccessToken(_context, getHostPackage(), scope)); + i.setPackage(getHostPackage()); + i.putExtra(Strings.EXTRA_SENDER, _context.getPackageName()); + i.putExtra(Strings.EXTRA_FIELD_VALUE, fieldValue); + i.putExtra(Strings.EXTRA_ENTRY_ID, getEntryId()); + i.putExtra(Strings.EXTRA_FIELD_ID, fieldId); + i.putExtra(Strings.EXTRA_FIELD_PROTECTED, isProtected); + + _context.sendBroadcast(i); + } + + } + + protected class ActionSelectedAction extends PluginEntryActionBase + { + public ActionSelectedAction(Context ctx, Intent intent) { + super(ctx, intent); + + } + + + + + /** + * + * @return the Bundle associated with the action. This bundle can be set in OpenEntry.add(Entry)FieldAction + */ + public Bundle getActionData() + { + return _intent.getBundleExtra(Strings.EXTRA_ACTION_DATA); + } + + /** + * + * @return the field id which was selected. null if an entry action (in the options menu) was selected. + */ + public String getFieldId() + { + return _intent.getStringExtra(Strings.EXTRA_FIELD_ID); + } + + /** + * + * @return true if an entry action, i.e. an option from the options menu, was selected. False if an option + * in a popup menu for a certain field was selected. + */ + public boolean isEntryAction() + { + return getFieldId() == null; + } + + /** + * + * @return a hashmap containing the entry fields in key/value form + */ + public HashMap getEntryFields() + { + return getEntryFieldsFromIntent(); + } + + /** + * + * @return an array with the keys of all protected fields in the entry + */ + public String[] getProtectedFieldsList() + { + return getProtectedFieldsListFromIntent(); + } + } + + protected class CloseEntryViewAction extends PluginEntryActionBase + { + public CloseEntryViewAction(Context context, Intent intent) { + super(context, intent); + } + + public String getEntryId() + { + return _intent.getStringExtra(Strings.EXTRA_ENTRY_ID); + } + } + + protected class OpenEntryAction extends PluginEntryActionBase + { + + public OpenEntryAction(Context context, Intent intent) + { + super(context, intent); + } + + + public HashMap getEntryFields() + { + return getEntryFieldsFromIntent(); + } + + /** + * + * @return an array with the keys of all protected fields in the entry + */ + public String[] getProtectedFieldsList() + { + return getProtectedFieldsListFromIntent(); + } + + public void addEntryAction(String actionDisplayText, int actionIconResourceId, Bundle actionData) throws PluginAccessException + { + addEntryFieldAction(null, null, actionDisplayText, actionIconResourceId, actionData); + } + + public void addEntryFieldAction(String actionId, String fieldId, String actionDisplayText, int actionIconResourceId, Bundle actionData) throws PluginAccessException + { + Intent i = new Intent(Strings.ACTION_ADD_ENTRY_ACTION); + ArrayList scope = new ArrayList(); + scope.add(Strings.SCOPE_CURRENT_ENTRY); + i.putExtra(Strings.EXTRA_ACCESS_TOKEN, AccessManager.getAccessToken(_context, getHostPackage(), scope)); + i.setPackage(getHostPackage()); + i.putExtra(Strings.EXTRA_SENDER, _context.getPackageName()); + i.putExtra(Strings.EXTRA_ACTION_DATA, actionData); + i.putExtra(Strings.EXTRA_ACTION_DISPLAY_TEXT, actionDisplayText); + i.putExtra(Strings.EXTRA_ACTION_ICON_RES_ID, actionIconResourceId); + i.putExtra(Strings.EXTRA_ENTRY_ID, getEntryId()); + i.putExtra(Strings.EXTRA_FIELD_ID, fieldId); + i.putExtra(Strings.EXTRA_ACTION_ID, actionId); + + _context.sendBroadcast(i); + } + + + + + } + + protected class DatabaseAction extends PluginActionBase + { + + public DatabaseAction(Context context, Intent intent) { + super(context, intent); + } + + public String getFileDisplayName() + { + return _intent.getStringExtra(Strings.EXTRA_DATABASE_FILE_DISPLAYNAME); + } + + public String getFilePath() + { + return _intent.getStringExtra(Strings.EXTRA_DATABASE_FILEPATH); + } + + public String getAction() + { + return _intent.getAction(); + } + + } + //EntryOutputModified is very similar to OpenEntry because it receives the same + //data (+ the field id which was modified) + protected class EntryOutputModifiedAction extends OpenEntryAction + { + + public EntryOutputModifiedAction(Context context, Intent intent) + { + super(context, intent); + } + + public String getModifiedFieldId() + { + return _intent.getStringExtra(Strings.EXTRA_FIELD_ID); + } + } + + @Override + public void onReceive(Context ctx, Intent intent) { + String action = intent.getAction(); + android.util.Log.d("KP2A.pluginsdk", "received broadcast in PluginActionBroadcastReceiver with action="+action); + if (action == null) + return; + if (action.equals(Strings.ACTION_OPEN_ENTRY)) + { + openEntry(new OpenEntryAction(ctx, intent)); + } + else if (action.equals(Strings.ACTION_CLOSE_ENTRY_VIEW)) + { + closeEntryView(new CloseEntryViewAction(ctx, intent)); + } + else if (action.equals(Strings.ACTION_ENTRY_ACTION_SELECTED)) + { + actionSelected(new ActionSelectedAction(ctx, intent)); + } + else if (action.equals(Strings.ACTION_ENTRY_OUTPUT_MODIFIED)) + { + entryOutputModified(new EntryOutputModifiedAction(ctx, intent)); + } + else if (action.equals(Strings.ACTION_LOCK_DATABASE) + || action.equals(Strings.ACTION_UNLOCK_DATABASE) + || action.equals(Strings.ACTION_OPEN_DATABASE) + || action.equals(Strings.ACTION_CLOSE_DATABASE)) + { + dbAction(new DatabaseAction(ctx, intent)); + } + else + { + //TODO handle unexpected action + } + + + } + + protected void closeEntryView(CloseEntryViewAction closeEntryView) {} + + protected void actionSelected(ActionSelectedAction actionSelected) {} + + protected void openEntry(OpenEntryAction oe) {} + + protected void entryOutputModified(EntryOutputModifiedAction eom) {} + + protected void dbAction(DatabaseAction db) {} + +} diff --git a/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/Strings.java b/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/Strings.java new file mode 100644 index 00000000..779eb01b --- /dev/null +++ b/src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/Strings.java @@ -0,0 +1,190 @@ +package keepass2android.pluginsdk; + +public class Strings { + /** + * Plugin is notified about actions like open/close/update a database. + */ + public static final String SCOPE_DATABASE_ACTIONS = "keepass2android.SCOPE_DATABASE_ACTIONS"; + /** + * Plugin is notified when an entry is opened. + */ + public static final String SCOPE_CURRENT_ENTRY = "keepass2android.SCOPE_CURRENT_ENTRY"; + + /** + * Plugin may query credentials for its own package + */ + public static final String SCOPE_QUERY_CREDENTIALS_FOR_OWN_PACKAGE = "keepass2android.SCOPE_QUERY_CREDENTIALS_FOR_OWN_PACKAGE"; + + /** + * Plugin may query credentials for a deliberate package + */ + public static final String SCOPE_QUERY_CREDENTIALS = "keepass2android.SCOPE_QUERY_CREDENTIALS"; + + /** + * Extra key to transfer a (json serialized) list of scopes + */ + public static final String EXTRA_SCOPES = "keepass2android.EXTRA_SCOPES"; + + + public static final String EXTRA_PLUGIN_PACKAGE = "keepass2android.EXTRA_PLUGIN_PACKAGE"; + + /** + * Extra key for sending the package name of the sender of a broadcast. + * Should be set in every broadcast. + */ + public static final String EXTRA_SENDER = "keepass2android.EXTRA_SENDER"; + + /** + * Extra key for sending a request token. The request token is passed from + * KP2A to the plugin. It's used in the authorization process. + */ + public static final String EXTRA_REQUEST_TOKEN = "keepass2android.EXTRA_REQUEST_TOKEN"; + + /** + * Action to start KP2A with an AppTask + */ + public static final String ACTION_START_WITH_TASK = "keepass2android.ACTION_START_WITH_TASK"; + + /** + * Action sent from KP2A to the plugin to indicate that the plugin should request + * access (sending it's scopes) + */ + public static final String ACTION_TRIGGER_REQUEST_ACCESS = "keepass2android.ACTION_TRIGGER_REQUEST_ACCESS"; + /** + * Action sent from the plugin to KP2A including the scopes. + */ + public static final String ACTION_REQUEST_ACCESS = "keepass2android.ACTION_REQUEST_ACCESS"; + /** + * Action sent from the KP2A to the plugin when the user grants access. + * Will contain an access token. + */ + public static final String ACTION_RECEIVE_ACCESS = "keepass2android.ACTION_RECEIVE_ACCESS"; + /** + * Action sent from KP2A to the plugin to indicate that access is not or no longer valid. + */ + public static final String ACTION_REVOKE_ACCESS = "keepass2android.ACTION_REVOKE_ACCESS"; + + + /** + * Action for startActivity(). Opens an activity in the Plugin Host to edit the plugin settings (i.e. enable it) + */ + public static final String ACTION_EDIT_PLUGIN_SETTINGS = "keepass2android.ACTION_EDIT_PLUGIN_SETTINGS"; + + /** + * Action sent from KP2A to the plugin to indicate that an entry was opened. + * The Intent contains the full entry data. + */ + public static final String ACTION_OPEN_ENTRY= "keepass2android.ACTION_OPEN_ENTRY"; + + /** + * Action sent from KP2A to the plugin to indicate that an entry output field was modified/added. + * The Intent contains the full new entry data. + */ + public static final String ACTION_ENTRY_OUTPUT_MODIFIED= "keepass2android.ACTION_ENTRY_OUTPUT_MODIFIED"; + + /** + * Action sent from KP2A to the plugin to indicate that an entry activity was closed. + */ + public static final String ACTION_CLOSE_ENTRY_VIEW= "keepass2android.ACTION_CLOSE_ENTRY_VIEW"; + + /** + * Extra key for a string containing the GUID of the entry. + */ + public static final String EXTRA_ENTRY_ID= "keepass2android.EXTRA_ENTRY_DATA"; + + /** + * Json serialized data of the PwEntry (C# class) representing the opened entry. + * currently not implemented. + */ + //public static final String EXTRA_ENTRY_DATA = "keepass2android.EXTRA_ENTRY_DATA"; + + /** + * Json serialized list of fields, transformed using the database context (i.e. placeholders are replaced already) + */ + public static final String EXTRA_ENTRY_OUTPUT_DATA = "keepass2android.EXTRA_ENTRY_OUTPUT_DATA"; + + /** + * Json serialized lisf of field keys, specifying which field of the EXTRA_ENTRY_OUTPUT_DATA is protected. + */ + public static final String EXTRA_PROTECTED_FIELDS_LIST = "keepass2android.EXTRA_PROTECTED_FIELDS_LIST"; + + + /** + * Extra key for passing the access token (both ways) + */ + public static final String EXTRA_ACCESS_TOKEN = "keepass2android.EXTRA_ACCESS_TOKEN"; + + /** + * Action for an intent from the plugin to KP2A to add menu options regarding the currently open entry. + * Requires SCOPE_CURRENT_ENTRY. + */ + public static final String ACTION_ADD_ENTRY_ACTION = "keepass2android.ACTION_ADD_ENTRY_ACTION"; + + public static final String EXTRA_ACTION_DISPLAY_TEXT = "keepass2android.EXTRA_ACTION_DISPLAY_TEXT"; + public static final String EXTRA_ACTION_ICON_RES_ID = "keepass2android.EXTRA_ACTION_ICON_RES_ID"; + + public static final String EXTRA_FIELD_ID = "keepass2android.EXTRA_FIELD_ID"; + + /** + * Used to pass an id for the action. Each actionId may occur only once per field, otherwise the previous + * action with same id is replaced by the new action. + */ + public static final String EXTRA_ACTION_ID = "keepass2android.EXTRA_ACTION_ID"; + + /** Extra for ACTION_ADD_ENTRY_ACTION and ACTION_ENTRY_ACTION_SELECTED to pass data specifying the action parameters.*/ + public static final String EXTRA_ACTION_DATA = "keepass2android.EXTRA_ACTION_DATA"; + + /** + * Action for an intent from KP2A to the plugin when an action added with ACTION_ADD_ENTRY_ACTION was selected by the user. + * + */ + public static final String ACTION_ENTRY_ACTION_SELECTED = "keepass2android.ACTION_ENTRY_ACTION_SELECTED"; + + /** + * Extra key for the string which is used to query the credentials. This should be either a URL for + * a web login (google.com or a full URI) or something in the form "androidapp://com.my.package" + */ + public static final String EXTRA_QUERY_STRING = "keepass2android.EXTRA_QUERY_STRING"; + + /** + * Action when plugin wants to query credentials for its own package + */ + public static final String ACTION_QUERY_CREDENTIALS_FOR_OWN_PACKAGE = "keepass2android.ACTION_QUERY_CREDENTIALS_FOR_OWN_PACKAGE"; + + + /** + * Action when plugin wants to query credentials for a deliberate package + * The query string is passed as intent data + */ + public static final String ACTION_QUERY_CREDENTIALS = "keepass2android.ACTION_QUERY_CREDENTIALS"; + + /** + * Action for an intent from the plugin to KP2A to set (i.e. add or update) a field in the entry. + * May be used to update existing or add new fields at any time while the entry is opened. + */ + public static final String ACTION_SET_ENTRY_FIELD = "keepass2android.ACTION_SET_ENTRY_FIELD"; + + /** Actions for an intent from KP2A to the plugin to inform that a database was opened, closed, quicklocked or quickunlocked.*/ + public static final String ACTION_OPEN_DATABASE = "keepass2android.ACTION_OPEN_DATABASE"; + public static final String ACTION_CLOSE_DATABASE = "keepass2android.ACTION_CLOSE_DATABASE"; + public static final String ACTION_LOCK_DATABASE = "keepass2android.ACTION_LOCK_DATABASE"; + public static final String ACTION_UNLOCK_DATABASE = "keepass2android.ACTION_UNLOCK_DATABASE"; + + /** Extra for ACTION_OPEN_DATABASE and ACTION_CLOSE_DATABASE containing a filepath which is used + * by KP2A internally to identify the file. Use only where necessary, might contain credentials + * for accessing the file (on remote storage).*/ + public static final String EXTRA_DATABASE_FILEPATH = "keepass2android.EXTRA_DATABASE_FILEPATH"; + /** Extra for ACTION_OPEN_DATABASE and ACTION_CLOSE_DATABASE containing a filepath which can be + * displayed to the user.*/ + public static final String EXTRA_DATABASE_FILE_DISPLAYNAME = "keepass2android.EXTRA_DATABASE_FILE_DISPLAYNAME"; + + + public static final String EXTRA_FIELD_VALUE = "keepass2android.EXTRA_FIELD_VALUE"; + public static final String EXTRA_FIELD_PROTECTED = "keepass2android.EXTRA_FIELD_PROTECTED"; + + public static final String PREFIX_STRING = "STRING_"; + public static final String PREFIX_BINARY = "BINARY_"; + + + +}