diff --git a/mobile/android/base/tests/BaseTest.java.in b/mobile/android/base/tests/BaseTest.java.in index bfbc26f758e..025763280c9 100644 --- a/mobile/android/base/tests/BaseTest.java.in +++ b/mobile/android/base/tests/BaseTest.java.in @@ -204,4 +204,33 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2 { } return true; } + + @SuppressWarnings({"unchecked", "non-varargs"}) + public void SqliteCompare(Cursor c, ContentValues[] cvs) { + mAsserter.is(c.getCount(), cvs.length, "List is correct length"); + if (c.moveToFirst()) { + do { + boolean found = false; + for (int i = 0; !found && i < cvs.length; i++) { + if (CursorMatches(c, cvs[i])) { + found = true; + } + } + mAsserter.is(found, true, "Password was found"); + } while(c.moveToNext()); + } + } + + public boolean CursorMatches(Cursor c, ContentValues cv) { + for (int i = 0; i < c.getColumnCount(); i++) { + String column = c.getColumnName(i); + if (cv.containsKey(column)) { + mAsserter.info("Pass","Column value " + c.getString(i) + " ?= " + cv.get(column).toString()); + if (!cv.get(column).toString().equals(c.getString(i))) { + return false; + } + } + } + return true; + } } diff --git a/mobile/android/base/tests/robocop.ini b/mobile/android/base/tests/robocop.ini index 7824b5945d1..28d75049127 100644 --- a/mobile/android/base/tests/robocop.ini +++ b/mobile/android/base/tests/robocop.ini @@ -11,6 +11,7 @@ [testAboutPage] [testWebContentContextMenu] [testPasswordProvider] +[testPasswordEncrypt] [testFormHistory] # Used for Talos, please don't use in mochitest diff --git a/mobile/android/base/tests/testPasswordEncrypt.java.in b/mobile/android/base/tests/testPasswordEncrypt.java.in new file mode 100644 index 00000000000..67d0edfc11f --- /dev/null +++ b/mobile/android/base/tests/testPasswordEncrypt.java.in @@ -0,0 +1,112 @@ +#filter substitution +package @ANDROID_PACKAGE_NAME@.tests; + +import @ANDROID_PACKAGE_NAME@.*; +import android.app.Activity; +import android.content.ContentValues; +import android.content.ContentResolver; +import android.database.Cursor; +import android.content.Context; +import android.net.Uri; +import java.io.File; +import java.lang.reflect.Method; + +public class testPasswordEncrypt extends BaseTest { + public void testPasswordEncrypt() { + setTestType("mochitest"); + Context context = (Context)getActivity(); + ContentResolver cr = context.getContentResolver(); + ContentValues cvs = new ContentValues(); + + Actions.EventExpecter contentEventExpecter = mActions.expectGeckoEvent("Gecko:Ready"); + contentEventExpecter.blockForEvent(); + + File db = new File(mProfile, "signons.sqlite"); + String dbPath = db.getPath(); + + Uri passwordUri; + try { + ClassLoader classLoader = getActivity().getClassLoader(); + Class pwds = classLoader.loadClass("org.mozilla.gecko.db.BrowserContract$Passwords"); + Class nss = classLoader.loadClass("org.mozilla.gecko.NSSBridge"); + Class contextClass = classLoader.loadClass("android.content.Context"); + Class stringClass = classLoader.loadClass("java.lang.String"); + Class appshell = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell"); + + Method loadNSSLibs = appshell.getMethod("loadNSSLibs", contextClass, stringClass); + Method decrypt = nss.getMethod("decrypt", contextClass, stringClass, stringClass); + Method encrypt = nss.getMethod("encrypt", contextClass, stringClass, stringClass); + + cvs.put("hostname", "http://www.example.com"); + cvs.put("encryptedUsername", "username"); + cvs.put("encryptedPassword", "password"); + + // Attempt to insert into the db + passwordUri = (Uri)pwds.getField("CONTENT_URI").get(null); + Uri.Builder builder = passwordUri.buildUpon(); + passwordUri = builder.appendQueryParameter("profilePath", mProfile).build(); + + // This should fail the first time round because there is no pw database + // Wait for gecko to reply and then we'll try again + contentEventExpecter = mActions.expectGeckoEvent("Passwords:Init:Return"); + Uri uri = cr.insert(passwordUri, cvs); + contentEventExpecter.blockForEvent(); + mAsserter.is(uri, null, "Insert returned null correctly"); + + uri = cr.insert(passwordUri, cvs); + Uri expectedUri = passwordUri.buildUpon().appendPath("1").build(); + mAsserter.is(uri.toString(), expectedUri.toString(), "Insert returned correct uri"); + + Cursor list = mActions.querySql(dbPath, "SELECT encryptedUsername FROM moz_logins"); + String resourcePath = getActivity().getApplication().getPackageResourcePath(); + loadNSSLibs.invoke(null, (Context)getActivity(), resourcePath); + + list.moveToFirst(); + String decryptedU = (String)decrypt.invoke(null, context, mProfile, list.getString(0)); + mAsserter.is(decryptedU, "username", "Username was encrypted correctly when inserting"); + + list = mActions.querySql(dbPath, "SELECT encryptedPassword FROM moz_logins"); + list.moveToFirst(); + String decryptedP = (String)decrypt.invoke(null, context, mProfile, list.getString(0)); + mAsserter.is(decryptedP, "password", "Password was encrypted correctly when inserting"); + + cvs.put("encryptedUsername", "username2"); + cvs.put("encryptedPassword", "password2"); + cr.update(passwordUri, cvs, null, null); + + list = mActions.querySql(dbPath, "SELECT encryptedUsername FROM moz_logins"); + list.moveToFirst(); + decryptedU = (String)decrypt.invoke(null, context, mProfile, list.getString(0)); + mAsserter.is(decryptedU, "username2", "Username was encrypted when updating"); + + list = mActions.querySql(dbPath, "SELECT encryptedPassword FROM moz_logins"); + list.moveToFirst(); + decryptedP = (String)decrypt.invoke(null, context, mProfile, list.getString(0)); + mAsserter.is(decryptedP, "password2", "Password was encrypted when updating"); + } catch(ClassNotFoundException ex) { + mAsserter.ok(false, "Error getting class", ex.toString()); + return; + } catch(NoSuchFieldException ex) { + mAsserter.ok(false, "Error getting field", ex.toString()); + return; + } catch(IllegalAccessException ex) { + mAsserter.ok(false, "Error using field", ex.toString()); + return; + } catch(java.lang.NoSuchMethodException ex) { + mAsserter.ok(false, "Error getting method", ex.toString()); + return; + } catch(java.lang.reflect.InvocationTargetException ex) { + mAsserter.ok(false, "Error invoking method", ex.toString()); + return; + } + } + + public void tearDown() throws Exception { + super.tearDown(); + + // remove the entire signons.sqlite file + File profile = new File(mProfile); + File db = new File(profile, "signons.sqlite"); + db.delete(); + } +} diff --git a/mobile/android/base/tests/testPasswordProvider.java.in b/mobile/android/base/tests/testPasswordProvider.java.in index 6db2c327953..5dbab8e9c4b 100644 --- a/mobile/android/base/tests/testPasswordProvider.java.in +++ b/mobile/android/base/tests/testPasswordProvider.java.in @@ -65,21 +65,24 @@ public class testPasswordProvider extends BaseTest { contentEventExpecter.blockForEvent(); uri = cr.insert(passwordUri, cvs[0]); - SqliteCompare(DB_NAME, "SELECT * FROM moz_logins", cvs); Uri expectedUri = passwordUri.buildUpon().appendPath("1").build(); - mAsserter.is(expectedUri.toString(), uri.toString(), "Insert returned correct uri"); + mAsserter.is(uri.toString(), expectedUri.toString(), "Insert returned correct uri"); + Cursor c = cr.query(passwordUri, null, null, null, null); + SqliteCompare(c, cvs); cvs[0].put("usernameField", "usernameField2"); cvs[0].put("passwordField", "passwordField2"); int numUpdated = cr.update(passwordUri, cvs[0], null, null); mAsserter.is(1, numUpdated, "Correct number updated"); - SqliteCompare(DB_NAME, "SELECT * FROM moz_logins", cvs); + c = cr.query(passwordUri, null, null, null, null); + SqliteCompare(c, cvs); int numDeleted = cr.delete(passwordUri, null, null); mAsserter.is(1, numDeleted, "Correct number deleted"); cvs = new ContentValues[0]; - SqliteCompare(DB_NAME, "SELECT * FROM moz_logins", cvs); + c = cr.query(passwordUri, null, null, null, null); + SqliteCompare(c, cvs); } public void tearDown() throws Exception {