diff options
author | Parménides GV <parmegv@sdf.org> | 2015-01-19 16:00:14 +0100 |
---|---|---|
committer | Parménides GV <parmegv@sdf.org> | 2015-01-19 16:00:14 +0100 |
commit | dbc8bc453d71a6c485a40f021fb66436c8e63616 (patch) | |
tree | 12aaee0692ed2ecd0aa213d379617bbe4fa98f48 | |
parent | 05ad5f226aa7a1504c756b0d77ab03e6add9fea9 (diff) | |
parent | 250b56762857f2e52626236d3716459786ce126d (diff) |
Merge branch 'bug/Add-unit-tests-#6470' into develop
25 files changed, 885 insertions, 452 deletions
@@ -65,7 +65,7 @@ The resulting apk(s) will be in `app/build/apk`. ## Running tests To run the automated tests: - 1. Run an emulator + 1. Run an emulator (device doesn't necesarily has root, so testVpnCertificateValidator.testIsValid may fail). 2. Unlock Android 3. Issue the command ./gradlew connectedCheck 4. Pay attention and check the "Trust this app" checkbox, if you don't do so tests won't run. diff --git a/app/src/androidTest/assets/eip-service-test.json b/app/src/androidTest/assets/eip-service-test.json new file mode 100644 index 00000000..78b49bae --- /dev/null +++ b/app/src/androidTest/assets/eip-service-test.json @@ -0,0 +1,69 @@ +{ + "gateways": [ + { + "capabilities": { + "adblock": false, + "filter_dns": false, + "limited": false, + "ports": [ + "443" + ], + "protocols": [ + "tcp", + "udp" + ], + "transport": [ + "openvpn" + ], + "user_ips": false + }, + "host": "millipede.demo.bitmask.net", + "ip_address": "198.252.153.84", + "location": "seattle__wa" + }, + { + "capabilities": { + "adblock": false, + "filter_dns": false, + "limited": false, + "ports": [ + "443" + ], + "protocols": [ + "tcp", + "udp" + ], + "transport": [ + "openvpn" + ], + "user_ips": false + }, + "host": "otter.demo.bitmask.net", + "ip_address": "46.165.242.169", + "location": "frankfurt" + } + ], + "locations": { + "frankfurt": { + "country_code": "DE", + "hemisphere": "N", + "name": "Frankfurt", + "timezone": "+1" + }, + "seattle__wa": { + "country_code": "US", + "hemisphere": "N", + "name": "Seattle, WA", + "timezone": "-7" + } + }, + "openvpn_configuration": { + "auth": "SHA1", + "cipher": "AES-128-CBC", + "keepalive": "10 30", + "tls-cipher": "DHE-RSA-AES128-SHA", + "tun-ipv6": true + }, + "serial": 1, + "version": 1 +}
\ No newline at end of file diff --git a/app/src/androidTest/assets/gateway.json b/app/src/androidTest/assets/gateway.json new file mode 100644 index 00000000..51a19ec9 --- /dev/null +++ b/app/src/androidTest/assets/gateway.json @@ -0,0 +1 @@ +{"location":"seattle__wa","ip_address":"198.252.153.84","capabilities":{"limited":false,"ports":["443"],"adblock":false,"transport":["openvpn"],"filter_dns":false,"protocols":["tcp","udp"],"user_ips":false},"host":"millipede.demo.bitmask.net"} diff --git a/app/src/androidTest/assets/secrets.json b/app/src/androidTest/assets/secrets.json new file mode 100644 index 00000000..36ba5977 --- /dev/null +++ b/app/src/androidTest/assets/secrets.json @@ -0,0 +1 @@ +{"ca_cert":"-----BEGIN CERTIFICATE-----\nMIIFbzCCA1egAwIBAgIBATANBgkqhkiG9w0BAQ0FADBKMRgwFgYDVQQDDA9CaXRt\nYXNrIFJvb3QgQ0ExEDAOBgNVBAoMB0JpdG1hc2sxHDAaBgNVBAsME2h0dHBzOi8v\nYml0bWFzay5uZXQwHhcNMTIxMTA2MDAwMDAwWhcNMjIxMTA2MDAwMDAwWjBKMRgw\nFgYDVQQDDA9CaXRtYXNrIFJvb3QgQ0ExEDAOBgNVBAoMB0JpdG1hc2sxHDAaBgNV\nBAsME2h0dHBzOi8vYml0bWFzay5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\nggIKAoICAQC1eV4YvayaU+maJbWrD4OHo3d7S1BtDlcvkIRS1Fw3iYDjsyDkZxai\ndHp4EUasfNQ+EVtXUvtk6170EmLco6Elg8SJBQ27trE6nielPRPCfX3fQzETRfvB\n7tNvGw4Jn2YKiYoMD79kkjgyZjkJ2r\/bEHUSevmR09BRp86syHZerdNGpXYhcQ84\nCA1+V+603GFIHnrP+uQDdssW93rgDNYu+exT+Wj6STfnUkugyjmPRPjL7wh0tzy+\nznCeLl4xiV3g9sjPnc7r2EQKd5uaTe3j71sDPF92KRk0SSUndREz+B1+Dbe\/RGk4\nMEqGFuOzrtsgEhPIX0hplhb0Tgz\/rtug+yTT7oJjBa3u20AAOQ38\/M99EfdeJvc4\nlPFF1XBBLh6X9UKF72an2NuANiX6XPySnJgZ7nZ09RiYZqVwu\/qt3DfvLfhboq+0\nbQvLUPXrVDr70onv5UDjpmEA\/cLmaIqqrduuTkFZOym65\/PfAPvpGnt7crQj\/Ibl\nDEDYZQmP7AS+6zBjoOzNjUGE5r40zWAR1RSi7zliXTu+yfsjXUIhUAWmYR6J3KxB\nlfsiHBQ+8dn9kC3YrUexWoOqBiqJOAJzZh5Y1tqgzfh+2nmHSB2dsQRs7rDRRlyy\nYMbkpzL9ZsOUO2eTP1mmar6YjCN+rggYjRrX71K2SpBG6b1zZxOG+wIDAQABo2Aw\nXjAdBgNVHQ4EFgQUuYGDLL2sswnYpHHvProt1JU+D48wDgYDVR0PAQH\/BAQDAgIE\nMAwGA1UdEwQFMAMBAf8wHwYDVR0jBBgwFoAUuYGDLL2sswnYpHHvProt1JU+D48w\nDQYJKoZIhvcNAQENBQADggIBADeG67vaFcbITGpi51264kHPYPEWaXUa5XYbtmBl\ncXYyB6hY5hv\/YNuVGJ1gWsDmdeXEyj0j2icGQjYdHRfwhrbEri+h1EZOm1cSBDuY\nk\/P5+ctHyOXx8IE79DBsZ6IL61UKIaKhqZBfLGYcWu17DVV6+LT+AKtHhOrv3TSj\nRnAcKnCbKqXLhUPXpK0eTjPYS2zQGQGIhIy9sQXVXJJJsGrPgMxna1Xw2JikBOCG\nhtD\/JKwt6xBmNwktH0GI\/LVtVgSp82Clbn9C4eZN9E5YbVYjLkIEDhpByeC71QhX\nEIQ0ZR56bFuJA\/CwValBqV\/G9gscTPQqd+iETp8yrFpAVHOW+YzSFbxjTEkBte1J\naF0vmbqdMAWLk+LEFPQRptZh0B88igtx6tV5oVd+p5IVRM49poLhuPNJGPvMj99l\nmlZ4+AeRUnbOOeAEuvpLJbel4rhwFzmUiGoeTVoPZyMevWcVFq6BMkS+jRR2w0jK\nG6b0v5XDHlcFYPOgUrtsOBFJVwbutLvxdk6q37kIFnWCd8L3kmES5q4wjyFK47Co\nJa8zlx64jmMZPg\/t3wWqkZgXZ14qnbyG5\/lGsj5CwVtfDljrhN0oCWK1FZaUmW3d\n69db12\/g4f6phldhxiWuGC\/W6fCW5kre7nmhshcltqAJJuU47iX+DarBFiIj816e\nyV8e\n-----END CERTIFICATE-----\n","cert":"-----BEGIN CERTIFICATE-----\nMIIEjDCCAnSgAwIBAgIQG6MBp\/cd9DlY+7cdvp3R3jANBgkqhkiG9w0BAQsFADBmMRAwDgYDVQQK\nDAdCaXRtYXNrMRwwGgYDVQQLDBNodHRwczovL2JpdG1hc2submV0MTQwMgYDVQQDDCtCaXRtYXNr\nIFJvb3QgQ0EgKGNsaWVudCBjZXJ0aWZpY2F0ZXMgb25seSEpMB4XDTE0MTIwNTAwMDAwMFoXDTE1\nMDMwNTAwMDAwMFowLTErMCkGA1UEAwwiVU5MSU1JVEVEZDBwZDdkMzE4eTNtOHNkeXllaTFqYmZl\neDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANRNhZ4aCwdL5+OKObOKeI2rDqEwGnIr\nhL9wzo\/FXbwLfdW45Y9Mxwhh6xy2NkA1YUKCB8VNBKNXlBrGr1QriLbu1rItsJ2VVLqGluVV\/gO4\njcaPU+\/Wu0hMFKG28J\/dPvIGeNbjBWk6mxQAA5WIpRK9RTeQ88wVaGIZDDzIdivza2zpcyiPAyii\ndbkyXh7sLsKvbZB6wLrert6Y1ylR3SlkZP0LfdGAMAdkMyuXKOjgcSnUltR8HSBuZcSUlsTVM11n\nrYeGCYyPNNQ3UYatDW33UASgRDBorrmjhhKP7IW\/opdlnPk5ZrP3i0qI32\/boRe0EWZGXJvr4P3K\ndJ30uCECAwEAAaNvMG0wHQYDVR0OBBYEFK8bMVAM4GBB5sHptoIOAaIvlYueMAsGA1UdDwQEAwIH\ngDATBgNVHSUEDDAKBggrBgEFBQcDAjAJBgNVHRMEAjAAMB8GA1UdIwQYMBaAFId+E7bsWFsUWah9\nvZuPvZ7O+aJsMA0GCSqGSIb3DQEBCwUAA4ICAQAQOX81csVhvP422NKkZH7+g3npBpl+sEHedaGR\nxYPOu4HrA4TVF9h44sljRoRJyenGNdBZCXcLKHg889eePTf8Z5K3lTojp6hvwyA6tgxOMHT1kESW\nPfqnRw8mHfHJuE3g+4YNUMwggzwc\/VZATdV\/7M33sarVN9AUOHou9n9BizgCC+UnYlS+F2POumE3\nFbOhKo5uubI02MwBYlN2JVO2TBt1Q20w8wc6cU07Xi5Epp+1mkgFiOShkNtPcJmEyBWJhxDtSDOW\n2doqWYNqH2kq7B5R\/kyyfcpFJqAnBTV7xs+C5rTS1mW7LpxfdCUMbYuLCpyxpO3A\/DhAm8n47tUH\nlBtmo8Avdb8VdFpYiGBpB0o9kTFcsWFb2GkWFBduGfSEB8jUI7QtqhgZqocAKK\/cweSRV8FwyUcn\nR0prRm3QEi9fbXqEddzjSY9y\/lqWYzT7u+IOAQpKroeZ4wzgYperDNOUFuYk1rP7yuvjP2pV5rcN\nyPoBP60TPVWMRM4WJm6nTogAz2qBrFsf\/XwT\/ajzbsjT6HNB7QbRE+wkFkqspoXG5Agp7KQ8lW3L\nSKCDGOQJz7VIE85pD0tg7QEXBEw8oaRZtMjQ0Gvs25mxXAKka4wGasaWfYH6d0E+iKYcWn86V1rH\nK2ZoknT+Nno5jgjFuUR3fZseNizEfx7BteooKQ==\n-----END CERTIFICATE-----","Constants.PRIVATE_KEY":"-----BEGIN RSA PRIVATE KEY-----\nMIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDUTYWeGgsHS+fjijmziniNqw6h\nMBpyK4S\/cM6PxV28C33VuOWPTMcIYesctjZANWFCggfFTQSjV5Qaxq9UK4i27tayLbCdlVS6hpbl\nVf4DuI3Gj1Pv1rtITBShtvCf3T7yBnjW4wVpOpsUAAOViKUSvUU3kPPMFWhiGQw8yHYr82ts6XMo\njwMoonW5Ml4e7C7Cr22QesC63q7emNcpUd0pZGT9C33RgDAHZDMrlyjo4HEp1JbUfB0gbmXElJbE\n1TNdZ62HhgmMjzTUN1GGrQ1t91AEoEQwaK65o4YSj+yFv6KXZZz5OWaz94tKiN9v26EXtBFmRlyb\n6+D9ynSd9LghAgMBAAECggEBANPHLRXkhsHVj1EkzqBx7gXr8CEMmiTvknFh9zvltrZhhDoRQjWr\nchPDkcRHY2Cznvy4N0YyqQDD2ULIlZdSAgPxxothFoBruWSD47yMBmLx08ORsDpcqt\/YvPAATJI8\nIpFNsXcyaXBp\/M57oRemgnxp\/8UJPJmFdWX99H4hvffh\/jdj7POgYiWUaAl37XTYZKZ4nzKU2wpL\nEDLj9RKPz9gG7CYp2zrLC9LaAsrXVrKwPBw6g+XwbClaqFj97db3mrY4lr6mTo89qmus1AU+fBDH\n3Xlpmc8JwB+30TvhRNKrpLx9cEjuEj7K1gm8Y4dWCjPi+lNbtAyUBcgPJFa\/81ECgYEA7pLoBU\/Y\nZYjyHFca8FvDBcBh6haHfqJr9doXWtgjDrbi3o2n5wHqfKhFWOH6vPEQozkOVeX1ze6HOiRmGBpW\nr+r7x8TD25L7I6HJw3M351RWOAfkF0w\/RTVdetcTgduQtfN1u6BDhYSVceXMjyQYx7MhfETWI8Gh\nKSYm8OEDYiUCgYEA489fmbrCcUnXzpTsbswJ5NmSoEXbcX8cLxnQuzE0z9GHhQdrMjOpXR76reTW\n6jcuudarNcwRUYSWWhjCDKHhpx4HhasWPaHgr7jIzcRw8yZSJRSxKr8sl1qh6g7s47JcmfXOMWLt\nyuyE933XrT19Th4ODZHY40Uv35mPjMi9d00CgYEAyRNAQtndBRa7GG\/B4Ls2T+6pl+aNJIo4e+no\nrURlp800wWabEPRocdBRQmyULBLxduBr2LIMzhgwGSz8b2wji\/l9ZA3PFY135bxClVzSzUIjuO3N\nrGUzHl2wAAyuAFDSUshzfkPBJRNt8aVBF5PQ3t93ZYmPAmv8LPZe875yX5ECgYEAsUEcwK\/ZNW7g\ndQPZR4iJNkC4Xu6cBZ6Cnn92swBheEYvLSoNlX0vDZ7aLE3\/jzQqrjzC8NP8sbH5jtbuvgeDXZX3\nAmGRp5j6C6A61ihAPmEVz3ZfN8SSfJ3vl\/\/PAIg6lyz0J+cy4Q7RkwSeuVQ72Hl4M8TEvmmKC3Af\nispy6Y0CgYEAgl1o2lo+ACyk+oVQPaaPqK3d7WOBFp4eR2nXFor\/vsx9igQOlZUgzRDQsR8jo1o9\nefOSBf87igrZGgssys89pWa2dnXnz5PMmzkKr6bw4D9Ez6u6Puc9UZhGw\/8wDYg6fSosdB9utspm\nM698ycef7jBNMDgmhpSvfw5GctoNQ4s=\n-----END RSA PRIVATE KEY-----"} diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/ConnectionManager.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/ConnectionManager.java index f1cbff19..e67dd820 100644 --- a/app/src/androidTest/java/se/leap/bitmaskclient/test/ConnectionManager.java +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/ConnectionManager.java @@ -17,12 +17,10 @@ public class ConnectionManager { method.setAccessible(true); try { method.invoke(conman, enabled); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { + } catch (InvocationTargetException | IllegalAccessException e) { e.printStackTrace(); } - } + } } } diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/FromAssets.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/FromAssets.java new file mode 100644 index 00000000..4f771922 --- /dev/null +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/FromAssets.java @@ -0,0 +1,26 @@ +package se.leap.bitmaskclient.test; + +import android.content.Context; + +import org.json.JSONException; + +import java.io.IOException; +import java.io.InputStream; + +public class FromAssets { + + Context context; + + public FromAssets(Context context) { + this.context = context; + } + public String toString(String filename) throws IOException, JSONException { + String result = ""; + InputStream is = context.getAssets().open(filename); + byte[] bytes = new byte[is.available()]; + if(is.read(bytes) > 0) { + result = new String(bytes); + } + return result; + } +} diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/TestConstants.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/TestConstants.java new file mode 100644 index 00000000..6b4cdfb1 --- /dev/null +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/TestConstants.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2013, 2014, 2015 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package se.leap.bitmaskclient.test; + +/** + * @author parmegv + */ +public class TestConstants { + public final static String EIP_DEFINITION_FILE = "eip-service-test.json"; + public final static String SECRETS_FILE = "secrets.json"; + public final static String GATEWAY_FILE = "gateway.json"; +} diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/testConfigurationWizard.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/testConfigurationWizard.java index 78ce1c81..755f83a7 100644 --- a/app/src/androidTest/java/se/leap/bitmaskclient/test/testConfigurationWizard.java +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/testConfigurationWizard.java @@ -1,14 +1,10 @@ package se.leap.bitmaskclient.test; -import android.test.ActivityInstrumentationTestCase2; -import android.widget.ListView; -import com.robotium.solo.Solo; -import java.io.IOException; -import se.leap.bitmaskclient.AboutActivity; -import se.leap.bitmaskclient.ConfigurationWizard; -import se.leap.bitmaskclient.ProviderDetailFragment; -import se.leap.bitmaskclient.R; -import se.leap.bitmaskclient.test.ConnectionManager; +import android.test.*; +import android.widget.*; +import com.robotium.solo.*; +import java.io.*; +import se.leap.bitmaskclient.*; public class testConfigurationWizard extends ActivityInstrumentationTestCase2<ConfigurationWizard> { @@ -19,10 +15,15 @@ public class testConfigurationWizard extends ActivityInstrumentationTestCase2<Co super(ConfigurationWizard.class); } + public testConfigurationWizard(Solo solo) { + super(ConfigurationWizard.class); + this.solo = solo; + } + @Override protected void setUp() throws Exception { super.setUp(); - solo = new Solo(getInstrumentation(), getActivity()); + solo = new Solo(getInstrumentation(), getActivity()); ConnectionManager.setMobileDataEnabled(true, solo.getCurrentActivity().getApplicationContext()); } @@ -31,35 +32,76 @@ public class testConfigurationWizard extends ActivityInstrumentationTestCase2<Co solo.finishOpenedActivities(); } - public void testListProviders() throws IOException { + public void testListProviders() { assertEquals(solo.getCurrentViews(ListView.class).size(), 1); - - int number_of_available_providers = solo.getCurrentViews(ListView.class).get(0).getCount(); - - assertEquals("Number of available providers differ", solo.getCurrentActivity().getAssets().list("urls").length + added_providers, number_of_available_providers); + + assertEquals("Number of available providers differ", predefinedProviders() + added_providers, shownProviders()); } + + private int shownProviders() { + return solo.getCurrentViews(ListView.class).get(0).getCount(); + } + + private int predefinedProviders() { + int predefined_providers = 0; + try { + predefined_providers = solo.getCurrentActivity().getAssets().list("urls").length; + } catch (IOException e) { + e.printStackTrace(); + return predefined_providers; + } + + return predefined_providers; + } public void testSelectProvider() { - solo.clickOnText("bitmask"); - assertTrue("Provider details dialog did not appear", solo.waitForFragmentByTag(ProviderDetailFragment.TAG, 60*1000)); + selectProvider("demo.bitmask.net"); } - - public void testAddNewProvider() { - solo.clickOnActionBarItem(R.id.new_provider); - solo.enterText(0, "calyx.net"); - solo.clickOnCheckBox(0); - solo.clickOnText(solo.getString(R.string.save)); - //added_providers = added_providers+1; - assertTrue("Provider details dialog did not appear", solo.waitForFragmentByTag(ProviderDetailFragment.TAG, 60*1000)); - solo.goBack(); + + private void selectProvider(String provider) { + solo.clickOnText(provider); + waitForProviderDetails(); + } + + private void waitForProviderDetails() { + String text = solo.getString(R.string.provider_details_fragment_title); + assertTrue("Provider details dialog did not appear", solo.waitForText(text)); + } + + public void testAddNewProvider() { + addProvider("calyx.net"); } + + private void addProvider(String url) { + boolean is_new_provider = !solo.searchText(url); + if(is_new_provider) + added_providers = added_providers+1; + solo.clickOnActionBarItem(R.id.new_provider); + solo.enterText(0, url); + solo.clickOnCheckBox(0); + solo.clickOnText(solo.getString(R.string.save)); + waitForProviderDetails(); + solo.goBack(); + } public void testShowAbout() { - solo.clickOnMenuItem(solo.getString(R.string.about)); - assertTrue("Provider details dialog did not appear", solo.waitForActivity(AboutActivity.class)); - } - - public void testShowSettings() { - //TODO We still don't have the settings button + showAbout(); } + + private void showAbout() { + String text = solo.getString(R.string.about); + solo.clickOnMenuItem(text); + assertTrue("Provider details dialog did not appear", solo.waitForActivity(AboutActivity.class)); + } + + protected void toDashboard(String provider) { + selectProvider(provider); + useAnonymously(); + } + + private void useAnonymously() { + String text = solo.getString(R.string.use_anonymously_button); + solo.clickOnText(text); + solo.waitForText(solo.getString(R.string.title_activity_dashboard)); + } } diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/testDashboardIntegration.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/testDashboardIntegration.java index 0c7db284..91b93d42 100644 --- a/app/src/androidTest/java/se/leap/bitmaskclient/test/testDashboardIntegration.java +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/testDashboardIntegration.java @@ -1,28 +1,19 @@ package se.leap.bitmaskclient.test; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.provider.Settings; -import android.test.ActivityInstrumentationTestCase2; -import android.util.Log; -import com.robotium.solo.Solo; +import android.test.*; +import com.robotium.solo.*; import java.io.IOException; -import java.io.InputStream; -import java.util.Scanner; -import de.blinkt.openvpn.activities.DisconnectVPN; -import se.leap.bitmaskclient.ConfigurationWizard; -import se.leap.bitmaskclient.Dashboard; -import se.leap.bitmaskclient.R; -import se.leap.bitmaskclient.test.ConnectionManager; +import de.blinkt.openvpn.activities.*; +import se.leap.bitmaskclient.*; public class testDashboardIntegration extends ActivityInstrumentationTestCase2<Dashboard> { private Solo solo; - + private Context context; + public testDashboardIntegration() { super(Dashboard.class); } @@ -30,8 +21,12 @@ public class testDashboardIntegration extends ActivityInstrumentationTestCase2<D @Override protected void setUp() throws Exception { super.setUp(); + context = getInstrumentation().getContext(); solo = new Solo(getInstrumentation(), getActivity()); - ConnectionManager.setMobileDataEnabled(true, solo.getCurrentActivity().getApplicationContext()); + ConnectionManager.setMobileDataEnabled(true, context); + solo.unlockScreen(); + if(solo.searchText(solo.getString(R.string.configuration_wizard_title))) + new testConfigurationWizard(solo).toDashboard("demo.bitmask.net"); } @Override @@ -45,121 +40,128 @@ public class testDashboardIntegration extends ActivityInstrumentationTestCase2<D */ public void testOnOffOpenVpn() { solo.clickOnView(solo.getView(R.id.eipSwitch)); - testEipTurningOn(); + turningEipOn(); solo.clickOnView(solo.getView(R.id.eipSwitch)); - testEipTurningOff(); + turningEipOff(); solo.clickOnView(solo.getView(R.id.eipSwitch)); - testEipTurningOn(); + turningEipOn(); solo.clickOnView(solo.getView(R.id.eipSwitch)); - testEipTurningOff(); - - solo.clickOnView(solo.getView(R.id.eipSwitch)); - testEipTurningOn(); - - solo.clickOnView(solo.getView(R.id.eipSwitch)); - testEipTurningOff(); + turningEipOff(); - solo.clickOnView(solo.getView(R.id.eipSwitch)); - testEipTurningOn(); + /*solo.clickOnView(solo.getView(R.id.eipSwitch)); + turningEipOn(); - testEipIsOnNoNetwork(); + turnNetworkOff(); + restartAdbServer(); // This doesn't work + */ } - private void testEipTurningOn() { - if(!solo.waitForText(getActivity().getString(R.string.state_auth))) - fail(); - if(!solo.waitForText(getActivity().getString(R.string.eip_state_connected), 1, 30*1000)) - fail(); - solo.sleep(2*1000); + private void turningEipOn() { + assertAuthenticating(); + int max_seconds_until_connected = 30; + assertConnected(max_seconds_until_connected); + solo.sleep(2*1000); + } + + private void assertAuthenticating() { + String message = solo.getString(R.string.state_auth); + assertTrue(solo.waitForText(message)); + } + + private void assertConnected(int max_seconds_until_connected) { + String message = solo.getString(R.string.eip_state_connected); + assertTrue(solo.waitForText(message, 1, max_seconds_until_connected * 1000)); } - private void testEipTurningOff() { + private void turningEipOff() { sayOkToDisconnect(); - if(!solo.waitForText(getActivity().getString(R.string.eip_state_not_connected))) - fail(); + assertDisconnected(); solo.sleep(2*1000); } private void sayOkToDisconnect() { - if(!solo.waitForActivity(DisconnectVPN.class)) - fail(); - solo.clickOnText(getActivity().getString(android.R.string.yes)); + assertTrue(solo.waitForActivity(DisconnectVPN.class)); + String yes = solo.getString(android.R.string.yes); + solo.clickOnText(yes); + } + + private void assertDisconnected() { + String message = solo.getString(R.string.eip_state_not_connected); + assertTrue(solo.waitForText(message)); } - private void testEipIsOnNoNetwork() { - ConnectionManager.setMobileDataEnabled(false, solo.getCurrentActivity().getApplicationContext()); + private void turnNetworkOff() { + ConnectionManager.setMobileDataEnabled(false, context); if(!solo.waitForText(getActivity().getString(R.string.eip_state_not_connected), 1, 15*1000)) fail(); } + + private void restartAdbServer() { + runAdbCommand("kill-server"); + runAdbCommand("start-server"); + } public void testLogInAndOut() { - long miliseconds_to_log_in = 40 * 1000; - solo.clickOnActionBarItem(R.id.login_button); - solo.enterText(0, "parmegvtest1"); - solo.enterText(1, " S_Zw3'-"); - solo.clickOnText("Log In"); - solo.waitForDialogToClose(); - solo.waitForDialogToClose(miliseconds_to_log_in); - if(!solo.waitForText(getActivity().getString(R.string.succesful_authentication_message))) - fail(); - - solo.clickOnActionBarItem(R.string.logout_button); - if(!solo.waitForDialogToClose()) - fail(); + long milliseconds_to_log_in = 40 * 1000; + logIn("parmegvtest1", " S_Zw3'-"); + solo.waitForDialogToClose(milliseconds_to_log_in); + assertSuccessfulLogin(); + + logOut(); } - - public void testShowAbout() { - solo.clickOnMenuItem(getActivity().getString(R.string.about)); - solo.waitForText(getActivity().getString(R.string.repository_url_text)); + + private void logIn(String username, String password) { + solo.clickOnActionBarItem(R.id.login_button); + solo.enterText(0, username); + solo.enterText(1, password); + solo.clickOnText("Log In"); + solo.waitForDialogToClose(); + } + + private void assertSuccessfulLogin() { + String message = solo.getString(R.string.succesful_authentication_message); + assertTrue(solo.waitForText(message)); + } + + private void logOut() { + solo.clickOnActionBarItem(R.string.logout_button); + assertTrue(solo.waitForDialogToClose()); + } + + public void testShowAbout() { + showAbout(); solo.goBack(); - - solo.clickOnMenuItem(getActivity().getString(R.string.about)); - solo.waitForText(getActivity().getString(R.string.repository_url_text)); + showAbout(); solo.goBack(); } - - public void testSwitchProvider() { - solo.clickOnMenuItem(getActivity().getString(R.string.switch_provider_menu_option)); + + private void showAbout() { + String menu_item = solo.getString(R.string.about); + solo.clickOnMenuItem(menu_item); + + String text_unique_to_about = solo.getString(R.string.repository_url_text); + solo.waitForText(text_unique_to_about); + } + + public void testSwitchProvider() { + solo.clickOnMenuItem(solo.getString(R.string.switch_provider_menu_option)); solo.waitForActivity(ConfigurationWizard.class); solo.goBack(); } - public void testUpdateExpiredCertificate() { - String certificate = "-----BEGIN CERTIFICATE-----" + - "MIIEnDCCAoSgAwIBAgIRAOBkcbMKR0Jlw+xNalHn7aIwDQYJKoZIhvcNAQELBQAwdTEYMBYGA1UE" + - "CgwPUmlzZXVwIE5ldHdvcmtzMRswGQYDVQQLDBJodHRwczovL3Jpc2V1cC5uZXQxPDA6BgNVBAMM" + - "M1Jpc2V1cCBOZXR3b3JrcyBSb290IENBIChjbGllbnQgY2VydGlmaWNhdGVzIG9ubHkhKTAeFw0x" + - "NDA5MTkwMDAwMDBaFw0xNDExMTkwMDAwMDBaMC0xKzApBgNVBAMMIlVOTElNSVRFRDcwZWhxZG9l" + - "ZXQ2Z243bmc3eWx3ZWNxeGwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDdaKQHSwg2" + - "Q2Uz9t5mae9BfV9Jkk+WSU6jXixsTbtLAr8gvuNcVuI0lKm2zXVqoS8aRCSsCt12vhjU/WBTSv0t" + - "vwTaT2HQYFQ1GlVUBKssJEUpaVyQKL6LN9BA5ZODBpbhefRIX8z+02afxmNWdnOQfDtLU6nHSQLL" + - "IUBSmgu+Y2Q3SdIBojIl9Kj0Zt6uZkhtOXZqkwLBiMr+/ukSidpcmNgbAN0eXSfVouaduzsDPQ6M" + - "eCJTz2lhUvC0/57h5mlkNLzEjyb/pAVTtnK4zdiH6XAuCxU/AkF0yzhaiQWMG0RQb4vEx/UHjkDU" + - "+K0GDy/qx1BmBB7C4vHLauqSXOs1AgMBAAGjbzBtMB0GA1UdDgQWBBQioBn7DdhjmtBKgQKpx/aW" + - "XHYkGjALBgNVHQ8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwIwCQYDVR0TBAIwADAfBgNVHSME" + - "GDAWgBQX9BvV5SoBAU1rol02CikJlmWARjANBgkqhkiG9w0BAQsFAAOCAgEAV7q102FQ62IOX84o" + - "pPvUL3hJkGtZ5chgQwZhfl2fGtEdeqpU27Hx1jLP9o3n1z9XYaZg/d8xYhpY6Mm4rFl6hA4gk81Z" + - "yg/A3QeUgIjOsA0Xp+RNB5ACaLjCPUtWNk5brfuelDdFHjl1noC2P3vQ9ErhUna6TKVsxxrueimO" + - "nc3sV7YMGiVfPC7wEmhERuyhQxftIUHUy2kDCY5QgXtru6IZmc3SP4FcM8LUSC49kqmU9if2GTLo" + - "wQZmz6T7+N5PIJWIOiDh9PyoojRo7ep9szeIZpzgxcsoE/9ed84tg36JLOWi0GOyrdzVExv0rQQt" + - "q/NpqAe1mX5XQVbY8nwgaJ8eWIWIXIn+5RB7b+fm5ZFeM4eFyWeDk99bvS8jdH6uQP5WusL55+ft" + - "ADtESsmBvzUEGqxk5GL4lmmeqE+vsR5TesqGjZ+yH67rR+1+Uy2mhbqJBP0E0LHwWCCPYEVfngHj" + - "aZkDF1UVQdfc9Amc5u5J5YliWrEG80BNeJF7740Gwx69DHEIhElN+BBeeqLLYIZTKmt28/9iWbKL" + - "vhCrz/29wLYksL1bXmyHzvzyAcDHPpO9sQrKYiP1mGRDmXJmZU3i3cgeqQFZ8+lr55wcYdMGJOcx" + - "bz+jL0VkHdnoZdzGzelrAhZtgMtsJ/kgWYRgtFmhpYF1Xtj2MYrpBDxgQck=" + - "-----END CERTIFICATE-----"; - - } + /*public void testReboot() { + runAdbCommand("shell am broadcast -a android.intent.action.BOOT_COMPLETED"); + }*/ - public void testReboot() { + private void runAdbCommand(String adb_command) { try { - String command = "adb shell am broadcast -a android.intent.action.BOOT_COMPLETED"; - Runtime.getRuntime().exec(command); - } catch (IOException e) { + String command = "adb " + adb_command; + Runtime.getRuntime().exec(command).waitFor(); + } catch (IOException | InterruptedException e) { e.printStackTrace(); } } diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/testEIP.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/testEIP.java index 4e1819d0..d9235085 100644 --- a/app/src/androidTest/java/se/leap/bitmaskclient/test/testEIP.java +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/testEIP.java @@ -1,21 +1,45 @@ +/** + * Copyright (c) 2013, 2014, 2015 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ package se.leap.bitmaskclient.test; import android.content.Context; import android.content.Intent; -import android.test.ActivityUnitTestCase; +import android.content.SharedPreferences; import android.test.ServiceTestCase; +import android.test.suitebuilder.annotation.MediumTest; import se.leap.bitmaskclient.Dashboard; +import se.leap.bitmaskclient.eip.Constants; import se.leap.bitmaskclient.eip.EIP; +/** + * @author parmegv + */ public class testEIP extends ServiceTestCase<EIP> { private Context context; private Intent intent; - private EIP activity; + private SharedPreferences preferences; public testEIP(Class<EIP> activityClass) { super(activityClass); + context = getSystemContext(); + intent = new Intent(context, EIP.class); + preferences = context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Context.MODE_PRIVATE); } @Override @@ -28,5 +52,27 @@ public class testEIP extends ServiceTestCase<EIP> { super.tearDown(); } + @MediumTest + private void testCheckCertValidity() { + testEmptyCertificate(); + testExpiredCertificate(); + // Wait for the service to start + // Check result is OK. + } + + private void testEmptyCertificate() { + preferences.edit().putString(Constants.CERTIFICATE, "").apply(); + startService(Constants.ACTION_CHECK_CERT_VALIDITY); + } + private void testExpiredCertificate() { + String expired_certificate = "expired certificate"; + preferences.edit().putString(Constants.CERTIFICATE, expired_certificate).apply(); + startService(Constants.ACTION_CHECK_CERT_VALIDITY); + } + + private void startService(String action) { + intent.setAction(action); + startService(intent); + } } diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/testGatewaysManager.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/testGatewaysManager.java new file mode 100644 index 00000000..c4303251 --- /dev/null +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/testGatewaysManager.java @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2013, 2014, 2015 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package se.leap.bitmaskclient.test; + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.Test; + +import org.json.JSONObject; + +import se.leap.bitmaskclient.Dashboard; +import se.leap.bitmaskclient.eip.Gateway; +import se.leap.bitmaskclient.eip.GatewaysManager; + +/** + * @author parmegv + */ +public class testGatewaysManager extends InstrumentationTestCase { + + GatewaysManager gateways_manager; + Gateway gateway; + JSONObject eip_definition; + + FromAssets assets; + + Context context; + SharedPreferences preferences; + + @Override + protected void setUp() throws Exception { + context = getInstrumentation().getContext(); + assets = new FromAssets(context); + mockGatewaysManager(); + mockRealGateway(); + super.setUp(); + } + + @MediumTest + public void testFromEipServiceJson() { + gateways_manager.fromEipServiceJson(eip_definition); + assertEquals(2, gateways_manager.size()); + gateways_manager.addFromString(gateway.toString()); + assertEquals(2, gateways_manager.size()); + } + + @SmallTest + public void testAddFromString() { + gateways_manager.addFromString(""); + gateways_manager.addFromString(gateway.toString()); + } + + @MediumTest + public void testRemoveDuplicate() { + gateways_manager.addFromString(gateway.toString()); + assertEquals(1, gateways_manager.size()); + + mockArtificialGateway(); + gateways_manager.addFromString(gateway.toString()); + assertEquals(1, gateways_manager.size()); + } + + @MediumTest + public void testToString() { + assertEquals("[]", gateways_manager.toString()); + + gateways_manager.addFromString(gateway.toString()); + assertEquals("["+gateway.toString()+"]", gateways_manager.toString()); + } + + @SmallTest + public void testIsEmpty() { + assertTrue(gateways_manager.isEmpty()); + gateways_manager.addFromString(""); + assertTrue(gateways_manager.isEmpty()); + gateways_manager.addFromString(gateway.toString()); + assertFalse(gateways_manager.isEmpty()); + } + + private void mockGatewaysManager() { + context = getInstrumentation().getContext(); + preferences = context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Activity.MODE_PRIVATE); + gateways_manager = new GatewaysManager(context, preferences); + } + + private void mockRealGateway() { + try { + eip_definition = new JSONObject(assets.toString(TestConstants.EIP_DEFINITION_FILE)); + JSONObject secrets = new JSONObject(assets.toString(TestConstants.SECRETS_FILE)); + JSONObject gateway = new JSONObject(assets.toString(TestConstants.GATEWAY_FILE)); + this.gateway = new Gateway(eip_definition, secrets, gateway); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void mockArtificialGateway() { + try { + eip_definition = new JSONObject(assets.toString(TestConstants.EIP_DEFINITION_FILE)); + JSONObject secrets = new JSONObject(assets.toString(TestConstants.SECRETS_FILE).replace("6u6", "7u7")); + JSONObject gateway = new JSONObject(assets.toString(TestConstants.GATEWAY_FILE)); + this.gateway = new Gateway(eip_definition, secrets, gateway); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/testVpnCertificateValidator.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/testVpnCertificateValidator.java new file mode 100644 index 00000000..cd1d6c3b --- /dev/null +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/testVpnCertificateValidator.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2013, 2014, 2015 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package se.leap.bitmaskclient.test; + +import android.content.*; +import android.os.*; +import android.test.*; + +import org.json.*; + +import java.io.*; +import java.util.*; + +import se.leap.bitmaskclient.*; +import se.leap.bitmaskclient.eip.*; + +/** + * @author parmegv + */ +public class testVpnCertificateValidator extends InstrumentationTestCase { + + String certificate_valid_from_jan2015_to_nov2022 = ""; + + Context context; + FromAssets assets; + + @Override + protected void setUp() throws Exception { + context = getInstrumentation().getContext(); + assets = new FromAssets(context); + JSONObject secrets = new JSONObject(assets.toString(TestConstants.SECRETS_FILE)); + certificate_valid_from_jan2015_to_nov2022 = secrets.getString(Provider.CA_CERT); + super.setUp(); + } + + public void testIsValid() { + VpnCertificateValidator validator = new VpnCertificateValidator(certificate_valid_from_jan2015_to_nov2022); + setTime(2015, 1, 6); + assertTrue(validator.isValid()); + setTime(2020, 1, 6); + assertFalse(validator.isValid()); + } + + private void setTime(int year, int month, int day) { + shellCommand("adb shell chmod 666 /dev/alarm"); + Calendar c = Calendar.getInstance(); + c.set(year, month, day, 12, 00, 00); + SystemClock.setCurrentTimeMillis(c.getTimeInMillis()); + shellCommand("adb shell chmod 664 /dev/alarm"); + } + + private int shellCommand(String command) { + int result = 0; + try { + Runtime.getRuntime().exec(command); + } catch (IOException e) { + e.printStackTrace(); + } + return result; + } +} diff --git a/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java b/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java index f180b444..faf3779a 100644 --- a/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java +++ b/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java @@ -62,9 +62,6 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download private Provider selected_provider; final public static String TAG = ConfigurationWizard.class.getSimpleName(); - final public static String TYPE_OF_CERTIFICATE = "type_of_certificate"; - final public static String ANON_CERTIFICATE = "anon_certificate"; - final public static String AUTHED_CERTIFICATE = "authed_certificate"; final protected static String PROVIDER_SET = "PROVIDER SET"; final protected static String SERVICES_RETRIEVED = "SERVICES RETRIEVED"; @@ -83,7 +80,7 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download private int progress = -1; private void initProviderList() { - List<Renderer<Provider>> prototypes = new ArrayList<Renderer<Provider>>(); + List<Renderer<Provider>> prototypes = new ArrayList<>(); prototypes.add(new ProviderRenderer(this)); ProviderRendererBuilder providerRendererBuilder = new ProviderRendererBuilder(prototypes); adapter = new ProviderListAdapter(getLayoutInflater(), providerRendererBuilder, provider_manager); @@ -186,7 +183,7 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download if (preferences.getBoolean(Constants.ALLOWED_ANON, false)){ mConfigState.putExtra(SERVICES_RETRIEVED, true); - downloadAnonCert(); + downloadVpnCertificate(); } else { mProgressBar.incrementProgressBy(1); hideProgressBar(); @@ -305,17 +302,11 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download /** * Asks ProviderAPI to download an anonymous (anon) VPN certificate. */ - private void downloadAnonCert() { + private void downloadVpnCertificate() { Intent provider_API_command = new Intent(this, ProviderAPI.class); - Bundle parameters = new Bundle(); - - parameters.putString(TYPE_OF_CERTIFICATE, ANON_CERTIFICATE); - provider_API_command.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE); - provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver); - startService(provider_API_command); } diff --git a/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java index d63d641a..1dbe11d3 100644 --- a/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java +++ b/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java @@ -187,7 +187,7 @@ public class ProviderAPI extends IntentService { if(validUserLoginData(username, password)) { session_id_bundle = register(username, password); - broadcast_progress(progress++); + broadcastProgress(progress++); } else { if(!wellFormedPassword(password)) { session_id_bundle.putBoolean(RESULT_KEY, false); @@ -236,7 +236,7 @@ public class ProviderAPI extends IntentService { String password = task.getString(SessionDialog.PASSWORD); if(validUserLoginData(username, password)) { result = authenticate(username, password); - broadcast_progress(progress++); + broadcastProgress(progress++); } else { if(!wellFormedPassword(password)) { result.putBoolean(RESULT_KEY, false); @@ -317,7 +317,7 @@ public class ProviderAPI extends IntentService { * and sends it as a broadcast. * @param progress */ - private void broadcast_progress(int progress) { + private void broadcastProgress(int progress) { Intent intentUpdate = new Intent(); intentUpdate.setAction(UPDATE_PROGRESSBAR); intentUpdate.addCategory(Intent.CATEGORY_DEFAULT); @@ -507,16 +507,16 @@ public class ProviderAPI extends IntentService { if(!PROVIDER_JSON_DOWNLOADED) current_download = getAndSetProviderJson(last_provider_main_url, last_danger_on); if(PROVIDER_JSON_DOWNLOADED || (current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY))) { - broadcast_progress(progress++); + broadcastProgress(progress++); PROVIDER_JSON_DOWNLOADED = true; current_download = downloadCACert(last_danger_on); if(CA_CERT_DOWNLOADED || (current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY))) { - broadcast_progress(progress++); + broadcastProgress(progress++); CA_CERT_DOWNLOADED = true; current_download = getAndSetEipServiceJson(); if(current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY)) { - broadcast_progress(progress++); + broadcastProgress(progress++); EIP_SERVICE_JSON_DOWNLOADED = true; } } @@ -847,7 +847,7 @@ public class ProviderAPI extends IntentService { urlConnection.setSSLSocketFactory(getProviderSSLSocketFactory()); responseCode = urlConnection.getResponseCode(); - broadcast_progress(progress++); + broadcastProgress(progress++); LeapSRPSession.setToken(""); Log.d(TAG, Integer.toString(responseCode)); } catch (ClientProtocolException e) { @@ -864,7 +864,7 @@ public class ProviderAPI extends IntentService { if(urlConnection != null) { responseCode = urlConnection.getResponseCode(); if(responseCode == 401) { - broadcast_progress(progress++); + broadcastProgress(progress++); LeapSRPSession.setToken(""); Log.d(TAG, Integer.toString(responseCode)); return true; diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a2f886c7..0f28e11c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -94,7 +94,7 @@ </activity> <activity android:name="se.leap.bitmaskclient.ConfigurationWizard" - android:label="@string/title_activity_configuration_wizard" + android:label="@string/configuration_wizard_title" android:uiOptions="splitActionBarWhenNarrow" > </activity> <activity diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java index 4e6120ab..afe1a638 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java +++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java @@ -127,33 +127,28 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn case 91: // 0.6.0 without Bug #5999 case 101: // 0.8.0 if(!preferences.getString(Constants.KEY, "").isEmpty()) - updateEipService(); + eip_fragment.updateEipService(); break; } } catch (NameNotFoundException e) { Log.d(TAG, "Handle version didn't find any " + getPackageName() + " package"); } } - - @SuppressLint("CommitPrefEdits") + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data){ if ( requestCode == CONFIGURE_LEAP || requestCode == SWITCH_PROVIDER) { - if ( resultCode == RESULT_OK ) { - preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply(); - updateEipService(); - - if (data.hasExtra(Provider.KEY)) { - provider = data.getParcelableExtra(Provider.KEY); - preferences.edit().putBoolean(Constants.PROVIDER_CONFIGURED, true).commit(); - preferences.edit().putString(Provider.MAIN_URL, provider.mainUrl().toString()).apply(); - preferences.edit().putString(Provider.KEY, provider.definition().toString()).apply(); - } - buildDashboard(false); - invalidateOptionsMenu(); - if (data.hasExtra(SessionDialog.TAG)) { - sessionDialog(Bundle.EMPTY); - } + if ( resultCode == RESULT_OK && data.hasExtra(Provider.KEY)) { + provider = data.getParcelableExtra(Provider.KEY); + providerToPreferences(provider); + + buildDashboard(false); + invalidateOptionsMenu(); + if (data.hasExtra(SessionDialog.TAG)) { + sessionDialog(Bundle.EMPTY); + } + + preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply(); } else if (resultCode == RESULT_CANCELED && data.hasExtra(ACTION_QUIT)) { finish(); } else @@ -163,6 +158,13 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn } } + @SuppressLint("CommitPrefEdits") + private void providerToPreferences(Provider provider) { + preferences.edit().putBoolean(Constants.PROVIDER_CONFIGURED, true).commit(); + preferences.edit().putString(Provider.MAIN_URL, provider.mainUrl().toString()).apply(); + preferences.edit().putString(Provider.KEY, provider.definition().toString()).apply(); + } + private void configErrorDialog() { AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getAppContext()); alertBuilder.setTitle(getResources().getString(R.string.setup_error_title)); @@ -296,11 +298,14 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn providerApiCommand(Bundle.EMPTY, R.string.logout_message, ProviderAPI.LOG_OUT); } - private void downloadAuthedUserCertificate() { - Bundle parameters = new Bundle(); - parameters.putString(ConfigurationWizard.TYPE_OF_CERTIFICATE, ConfigurationWizard.AUTHED_CERTIFICATE); - - providerApiCommand(parameters, R.string.downloading_certificate_message, ProviderAPI.DOWNLOAD_CERTIFICATE); + protected void downloadVpnCertificate() { + boolean is_authenticated = !LeapSRPSession.getToken().isEmpty(); + boolean allowed_anon = preferences.getBoolean(Constants.ALLOWED_ANON, false); + if(allowed_anon || is_authenticated) + providerApiCommand(Bundle.EMPTY, R.string.downloading_certificate_message, ProviderAPI.DOWNLOAD_CERTIFICATE); + else + sessionDialog(Bundle.EMPTY); + } private Bundle bundleParameters(String username, String password) { @@ -312,8 +317,8 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn return parameters; } - private void providerApiCommand(Bundle parameters, int progressbar_message_resId, String providerApi_action) { - if(eip_fragment != null) { + protected void providerApiCommand(Bundle parameters, int progressbar_message_resId, String providerApi_action) { + if(eip_fragment != null && progressbar_message_resId != 0) { eip_fragment.progress_bar.setVisibility(ProgressBar.VISIBLE); setStatusMessage(progressbar_message_resId); } @@ -322,7 +327,7 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn startService(command); } - protected Intent prepareProviderAPICommand(Bundle parameters, String action) { + private Intent prepareProviderAPICommand(Bundle parameters, String action) { providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler()); providerAPI_result_receiver.setReceiver(this); @@ -371,7 +376,7 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply(); updateViewHidingProgressBar(resultCode); - downloadAuthedUserCertificate(); + downloadVpnCertificate(); } else if(resultCode == ProviderAPI.FAILED_LOGIN) { updateViewHidingProgressBar(resultCode); sessionDialog(resultData); @@ -386,16 +391,15 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn setResult(RESULT_CANCELED); } else if(resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE) { updateViewHidingProgressBar(resultCode); - updateEipService(); + eip_fragment.updateEipService(); setResult(RESULT_OK); } else if(resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE) { updateViewHidingProgressBar(resultCode); setResult(RESULT_CANCELED); } else if(resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE) { + eip_fragment.updateEipService(); setResult(RESULT_OK); - - updateEipService(); } else if(resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE) { setResult(RESULT_CANCELED); } @@ -407,12 +411,6 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn invalidateOptionsMenu(); } - private void updateEipService() { - Intent updateEIP = new Intent(getApplicationContext(), EIP.class); - updateEIP.setAction(Constants.ACTION_UPDATE_EIP_SERVICE); - startService(updateEIP); - } - private void changeStatusMessage(final int previous_result_code) { ResultReceiver status_receiver = new ResultReceiver(new Handler()){ protected void onReceiveResult(int resultCode, Bundle resultData){ diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java index 54432033..588b137b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java @@ -37,7 +37,6 @@ public class EipFragment extends Fragment implements Observer { protected static final String STATUS_MESSAGE = TAG + ".status_message"; public static final String START_ON_BOOT = "start on boot"; - private View view; @InjectView(R.id.eipSwitch) Switch eip_switch; @InjectView(R.id.status_message) @@ -45,19 +44,18 @@ public class EipFragment extends Fragment implements Observer { @InjectView(R.id.eipProgress) ProgressBar progress_bar; - private static Activity parent_activity; + private static Dashboard dashboard; private static EIPReceiver mEIPReceiver; private static EipStatus eip_status; private boolean is_starting_to_connect; + private boolean wants_to_connect; @Override public void onAttach(Activity activity) { super.onAttach(activity); - parent_activity = activity; - Dashboard dashboard = (Dashboard) parent_activity; - Intent provider_API_command = dashboard.prepareProviderAPICommand(Bundle.EMPTY, ProviderAPI.DOWNLOAD_EIP_SERVICE); - parent_activity.startService(provider_API_command); + dashboard = (Dashboard) activity; + dashboard.providerApiCommand(Bundle.EMPTY, 0, ProviderAPI.DOWNLOAD_EIP_SERVICE); } @Override @@ -70,7 +68,7 @@ public class EipFragment extends Fragment implements Observer { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - view = inflater.inflate(R.layout.eip_service_fragment, container, false); + View view = inflater.inflate(R.layout.eip_service_fragment, container, false); ButterKnife.inject(this, view); if (eip_status.isConnecting()) @@ -129,8 +127,7 @@ public class EipFragment extends Fragment implements Observer { if(canStartEIP()) startEipFromScratch(); else if(canLogInToStartEIP()) { - Log.d(TAG, "Can Log In to start EIP"); - Dashboard dashboard = (Dashboard) parent_activity; + wants_to_connect = true; Bundle bundle = new Bundle(); bundle.putBoolean(IS_PENDING, true); dashboard.sessionDialog(bundle); @@ -160,16 +157,16 @@ public class EipFragment extends Fragment implements Observer { } private void askPendingStartCancellation() { - AlertDialog.Builder alertBuilder = new AlertDialog.Builder(parent_activity); - alertBuilder.setTitle(parent_activity.getString(R.string.eip_cancel_connect_title)) - .setMessage(parent_activity.getString(R.string.eip_cancel_connect_text)) + AlertDialog.Builder alertBuilder = new AlertDialog.Builder(dashboard); + alertBuilder.setTitle(dashboard.getString(R.string.eip_cancel_connect_title)) + .setMessage(dashboard.getString(R.string.eip_cancel_connect_text)) .setPositiveButton((R.string.yes), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { askToStopEIP(); } }) - .setNegativeButton(parent_activity.getString(R.string.no), new DialogInterface.OnClickListener() { + .setNegativeButton(dashboard.getString(R.string.no), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { eip_switch.setChecked(true); @@ -179,10 +176,11 @@ public class EipFragment extends Fragment implements Observer { } public void startEipFromScratch() { + wants_to_connect = false; is_starting_to_connect = true; progress_bar.setVisibility(View.VISIBLE); eip_switch.setVisibility(View.VISIBLE); - String status = parent_activity.getString(R.string.eip_status_start_pending); + String status = dashboard.getString(R.string.eip_status_start_pending); status_message.setText(status); if(!eip_switch.isChecked()) { @@ -195,19 +193,23 @@ public class EipFragment extends Fragment implements Observer { private void stopEIP() { if(eip_status.isConnecting()) VoidVpnService.stop(); - Intent disconnect_vpn = new Intent(parent_activity, DisconnectVPN.class); - parent_activity.startActivityForResult(disconnect_vpn, EIP.DISCONNECT); + Intent disconnect_vpn = new Intent(dashboard, DisconnectVPN.class); + dashboard.startActivityForResult(disconnect_vpn, EIP.DISCONNECT); eip_status.setDisconnecting(); } protected void askToStopEIP() { hideProgressBar(); - String status = parent_activity.getString(R.string.eip_state_not_connected); + String status = dashboard.getString(R.string.eip_state_not_connected); status_message.setText(status); eipCommand(Constants.ACTION_STOP_EIP); } + + protected void updateEipService() { + eipCommand(Constants.ACTION_UPDATE_EIP_SERVICE); + } /** * Send a command to EIP @@ -217,10 +219,10 @@ public class EipFragment extends Fragment implements Observer { */ private void eipCommand(String action){ // TODO validate "action"...how do we get the list of intent-filters for a class via Android API? - Intent vpn_intent = new Intent(parent_activity.getApplicationContext(), EIP.class); + Intent vpn_intent = new Intent(dashboard.getApplicationContext(), EIP.class); vpn_intent.setAction(action); vpn_intent.putExtra(Constants.RECEIVER_TAG, mEIPReceiver); - parent_activity.startService(vpn_intent); + dashboard.startService(vpn_intent); } @Override @@ -228,7 +230,7 @@ public class EipFragment extends Fragment implements Observer { if(observable instanceof EipStatus) { eip_status = (EipStatus) observable; final EipStatus eip_status = (EipStatus) observable; - parent_activity.runOnUiThread(new Runnable() { + dashboard.runOnUiThread(new Runnable() { @Override public void run() { handleNewState(eip_status); @@ -254,13 +256,13 @@ public class EipFragment extends Fragment implements Observer { Log.d(TAG, "setConnectedUi? " + eip_status.isConnected()); adjustSwitch(); is_starting_to_connect = false; - status_message.setText(parent_activity.getString(R.string.eip_state_connected)); + status_message.setText(dashboard.getString(R.string.eip_state_connected)); } private void setDisconnectedUI(){ hideProgressBar(); adjustSwitch(); - status_message.setText(parent_activity.getString(R.string.eip_state_not_connected)); + status_message.setText(dashboard.getString(R.string.eip_state_not_connected)); } private void adjustSwitch() { @@ -281,13 +283,18 @@ public class EipFragment extends Fragment implements Observer { private void setInProgressUI(EipStatus eip_status) { int localizedResId = eip_status.getLocalizedResId(); String logmessage = eip_status.getLogMessage(); - String prefix = parent_activity.getString(localizedResId); + String prefix = dashboard.getString(localizedResId); status_message.setText(prefix + " " + logmessage); is_starting_to_connect = false; adjustSwitch(); } + private void updatingCertificateUI() { + progress_bar.setVisibility(View.VISIBLE); + status_message.setText(getString(R.string.updating_certificate_message)); + } + private void hideProgressBar() { if(progress_bar != null) progress_bar.setVisibility(View.GONE); @@ -333,19 +340,21 @@ public class EipFragment extends Fragment implements Observer { case Activity.RESULT_OK: break; case Activity.RESULT_CANCELED: - Dashboard dashboard = (Dashboard) parent_activity; - - progress_bar.setVisibility(View.VISIBLE); - status_message.setText(getString(R.string.updating_certificate_message)); - if(LeapSRPSession.getToken().isEmpty() && !Dashboard.preferences.getBoolean(Constants.ALLOWED_ANON, false)) { - dashboard.sessionDialog(Bundle.EMPTY); - } else { - Intent provider_API_command = dashboard.prepareProviderAPICommand(Bundle.EMPTY, ProviderAPI.DOWNLOAD_CERTIFICATE); - parent_activity.startService(provider_API_command); - } + updatingCertificateUI(); + dashboard.downloadVpnCertificate(); break; } - } + } else if (request.equals(Constants.ACTION_UPDATE_EIP_SERVICE)) { + switch (resultCode) { + case Activity.RESULT_OK: + if(wants_to_connect) + startEipFromScratch(); + break; + case Activity.RESULT_CANCELED: + handleNewState(eip_status); + break; + } + } } } diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java index 533b0281..3b72a486 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -16,46 +16,17 @@ */ package se.leap.bitmaskclient.eip; -import android.app.Activity; -import android.app.IntentService; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.os.ResultReceiver; +import android.app.*; +import android.content.*; +import android.os.*; import android.util.Log; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; +import org.json.*; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; +import de.blinkt.openvpn.*; +import se.leap.bitmaskclient.*; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.NoSuchElementException; - -import de.blinkt.openvpn.LaunchVPN; -import de.blinkt.openvpn.VpnProfile; -import de.blinkt.openvpn.core.Connection; -import de.blinkt.openvpn.core.ProfileManager; -import se.leap.bitmaskclient.Dashboard; -import se.leap.bitmaskclient.EipFragment; -import se.leap.bitmaskclient.Provider; - -import static se.leap.bitmaskclient.eip.Constants.ACTION_CHECK_CERT_VALIDITY; -import static se.leap.bitmaskclient.eip.Constants.ACTION_IS_EIP_RUNNING; -import static se.leap.bitmaskclient.eip.Constants.ACTION_START_EIP; -import static se.leap.bitmaskclient.eip.Constants.ACTION_STOP_EIP; -import static se.leap.bitmaskclient.eip.Constants.ACTION_UPDATE_EIP_SERVICE; -import static se.leap.bitmaskclient.eip.Constants.CERTIFICATE; -import static se.leap.bitmaskclient.eip.Constants.KEY; -import static se.leap.bitmaskclient.eip.Constants.RECEIVER_TAG; -import static se.leap.bitmaskclient.eip.Constants.REQUEST_TAG; +import static se.leap.bitmaskclient.eip.Constants.*; /** * EIP is the abstract base class for interacting with and managing the Encrypted @@ -79,26 +50,23 @@ public final class EIP extends IntentService { private static SharedPreferences preferences; private static JSONObject eip_definition; - private static List<Gateway> gateways = new ArrayList<>(); - private static ProfileManager profile_manager; + private static GatewaysManager gateways_manager = new GatewaysManager(); private static Gateway gateway; - public EIP(){ - super(TAG); - } + public EIP(){ + super(TAG); + } - @Override - public void onCreate() { - super.onCreate(); + @Override + public void onCreate() { + super.onCreate(); - context = getApplicationContext(); + context = getApplicationContext(); preferences = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE); - - profile_manager = ProfileManager.getInstance(context); - eip_definition = eipDefinitionFromPreferences(); - if(gateways.isEmpty()) - gateways = gatewaysFromPreferences(); - } + eip_definition = eipDefinitionFromPreferences(); + if(gateways_manager.isEmpty()) + gatewaysFromPreferences(); + } @Override protected void onHandleIntent(Intent intent) { @@ -123,18 +91,17 @@ public final class EIP extends IntentService { * It also sets up early routes. */ private void startEIP() { - if(gateways.isEmpty()) + if(gateways_manager.isEmpty()) updateEIPService(); earlyRoutes(); - GatewaySelector gateway_selector = new GatewaySelector(gateways); - gateway = gateway_selector.select(); + gateway = gateways_manager.select(); if(gateway != null && gateway.getProfile() != null) { mReceiver = EipFragment.getReceiver(); launchActiveGateway(); - tellToReceiver(ACTION_START_EIP, Activity.RESULT_OK); + tellToReceiver(ACTION_START_EIP, Activity.RESULT_OK); } else - tellToReceiver(ACTION_START_EIP, Activity.RESULT_CANCELED); + tellToReceiver(ACTION_START_EIP, Activity.RESULT_CANCELED); } /** @@ -184,178 +151,45 @@ public final class EIP extends IntentService { */ private void updateEIPService() { eip_definition = eipDefinitionFromPreferences(); - if(eip_definition != null) + if(eip_definition.length() > 0) updateGateways(); tellToReceiver(ACTION_UPDATE_EIP_SERVICE, Activity.RESULT_OK); } private JSONObject eipDefinitionFromPreferences() { + JSONObject result = new JSONObject(); try { String eip_definition_string = preferences.getString(KEY, ""); if(!eip_definition_string.isEmpty()) { - return new JSONObject(eip_definition_string); + result = new JSONObject(eip_definition_string); } } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } - return null; - } - - private List<Gateway> gatewaysFromPreferences() { - List<Gateway> result; - - String gateways_string = preferences.getString(Gateway.TAG, ""); - Type type_list_gateways = new TypeToken<ArrayList<Gateway>>() {}.getType(); - result = gateways_string.isEmpty() ? - new ArrayList<Gateway>() - : (List<Gateway>) new Gson().fromJson(gateways_string, type_list_gateways); - preferences.edit().remove(Gateway.TAG); return result; } - - /** - * Walk the list of gateways defined in eip-service.json and parse them into - * Gateway objects. - */ - private void updateGateways(){ - try { - JSONArray gatewaysDefined = eip_definition.getJSONArray("gateways"); - for (int i = 0; i < gatewaysDefined.length(); i++) { - JSONObject gw = gatewaysDefined.getJSONObject(i); - if (isOpenVpnGateway(gw)) { - JSONObject secrets = secretsConfiguration(); - Gateway aux = new Gateway(eip_definition, secrets, gw); - if(!containsProfileWithSecrets(aux.getProfile())) { - addGateway(aux); - } - } - } - gatewaysToPreferences(); - } catch (JSONException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - private boolean isOpenVpnGateway(JSONObject gateway) { - try { - String transport = gateway.getJSONObject("capabilities").getJSONArray("transport").toString(); - return transport.contains("openvpn"); - } catch (JSONException e) { - return false; - } - } - - - private JSONObject secretsConfiguration() { - JSONObject result = new JSONObject(); - try { - result.put(Provider.CA_CERT, preferences.getString(Provider.CA_CERT, "")); - result.put(Constants.PRIVATE_KEY, preferences.getString(Constants.PRIVATE_KEY, "")); - result.put(Constants.CERTIFICATE, preferences.getString(Constants.CERTIFICATE, "")); - } catch (JSONException e) { - e.printStackTrace(); - } - return result; - } - - private void addGateway(Gateway gateway) { - VpnProfile profile = gateway.getProfile(); - removeGateway(gateway); - - profile_manager.addProfile(profile); - profile_manager.saveProfile(context, profile); - profile_manager.saveProfileList(context); - - gateways.add(gateway); - } - - private void removeGateway(Gateway gateway) { - VpnProfile profile = gateway.getProfile(); - removeDuplicatedProfile(profile); - removeDuplicatedGateway(profile); - } - - private void removeDuplicatedProfile(VpnProfile original) { - if(containsProfile(original)) { - VpnProfile remove = duplicatedProfile(original); - profile_manager.removeProfile(context, remove); - }if(containsProfile(original)) removeDuplicatedProfile(original); - } - - private boolean containsProfile(VpnProfile profile) { - Collection<VpnProfile> profiles = profile_manager.getProfiles(); - for(VpnProfile aux : profiles) { - if (sameConnections(profile.mConnections, aux.mConnections)) { - return true; - } - } - return false; - } - - private boolean containsProfileWithSecrets(VpnProfile profile) { - boolean result = false; - - if(containsProfile(profile)) { - Collection<VpnProfile> profiles = profile_manager.getProfiles(); - for(VpnProfile aux : profiles) { - result = result == false ? - sameConnections(profile.mConnections, aux.mConnections) - && profile.mClientCertFilename.equalsIgnoreCase(aux.mClientCertFilename) - && profile.mClientKeyFilename.equalsIgnoreCase(aux.mClientKeyFilename) - : true; - } - } - return result; - } - - private VpnProfile duplicatedProfile(VpnProfile profile) { - VpnProfile duplicated = null; - Collection<VpnProfile> profiles = profile_manager.getProfiles(); - for(VpnProfile aux : profiles) { - if (sameConnections(profile.mConnections, aux.mConnections)) { - duplicated = aux; - } - } - if(duplicated != null) return duplicated; - else throw new NoSuchElementException(profile.getName()); - } - - private boolean sameConnections(Connection[] c1, Connection[] c2) { - int same_connections = 0; - for(Connection c1_aux : c1) { - for(Connection c2_aux : c2) - if(c2_aux.mServerName.equals(c1_aux.mServerName)) { - same_connections++; - break; - } - } - return c1.length == c2.length && c1.length == same_connections; + private void updateGateways(){ + gateways_manager.fromEipServiceJson(eip_definition); + gatewaysToPreferences(); } - private void removeDuplicatedGateway(VpnProfile profile) { - Iterator<Gateway> it = gateways.iterator(); - List<Gateway> gateways_to_remove = new ArrayList<>(); - while(it.hasNext()) { - Gateway aux = it.next(); - if(sameConnections(aux.getProfile().mConnections, profile.mConnections)) { - gateways_to_remove.add(aux); - } - } - gateways.removeAll(gateways_to_remove); + private void gatewaysFromPreferences() { + String gateways_string = preferences.getString(Gateway.TAG, ""); + gateways_manager = new GatewaysManager(context, preferences); + gateways_manager.addFromString(gateways_string); + preferences.edit().remove(Gateway.TAG).apply(); } private void gatewaysToPreferences() { - Type type_list_gateways = new TypeToken<List<Gateway>>() {}.getType(); - String gateways_string = new Gson().toJson(gateways, type_list_gateways); - preferences.edit().putString(Gateway.TAG, gateways_string).apply(); + String gateways_string = gateways_manager.toString(); + preferences.edit().putString(Gateway.TAG, gateways_string).commit(); } private void checkCertValidity() { - VpnCertificateValidator validator = new VpnCertificateValidator(); - int resultCode = validator.isValid(preferences.getString(CERTIFICATE, "")) ? + VpnCertificateValidator validator = new VpnCertificateValidator(preferences.getString(CERTIFICATE, "")); + int resultCode = validator.isValid() ? Activity.RESULT_OK : Activity.RESULT_CANCELED; tellToReceiver(ACTION_CHECK_CERT_VALIDITY, resultCode); diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java index daf7d4a7..0d8a2f7b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java @@ -20,6 +20,8 @@ import android.app.Activity; import android.content.SharedPreferences; import android.util.Log; +import com.google.gson.Gson; + import org.json.JSONException; import org.json.JSONObject; @@ -40,7 +42,7 @@ import se.leap.bitmaskclient.Dashboard; */ public class Gateway { - public static String TAG = Gateway.class.getSimpleName(); + public final static String TAG = Gateway.class.getSimpleName(); private JSONObject general_configuration; private JSONObject secrets; @@ -53,7 +55,7 @@ public class Gateway { * Build a gateway object from a JSON OpenVPN gateway definition in eip-service.json * and create a VpnProfile belonging to it. */ - protected Gateway(JSONObject eip_definition, JSONObject secrets, JSONObject gateway){ + public Gateway(JSONObject eip_definition, JSONObject secrets, JSONObject gateway){ this.gateway = gateway; this.secrets = secrets; @@ -130,4 +132,9 @@ public class Gateway { public int getTimezone() { return timezone; } + + @Override + public String toString() { + return new Gson().toJson(this, Gateway.class); + } } diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java new file mode 100644 index 00000000..b1aa5a2f --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java @@ -0,0 +1,184 @@ +/** + * Copyright (c) 2013, 2014, 2015 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package se.leap.bitmaskclient.eip; + +import android.content.*; +import android.util.Log; + +import com.google.gson.*; +import com.google.gson.reflect.*; + +import org.json.*; + +import java.lang.reflect.*; +import java.util.*; + +import de.blinkt.openvpn.*; +import de.blinkt.openvpn.core.*; +import se.leap.bitmaskclient.*; + +/** + * @author parmegv + */ +public class GatewaysManager { + + private Context context; + private SharedPreferences preferences; + private List<Gateway> gateways = new ArrayList<>(); + private ProfileManager profile_manager; + private Type list_type = new TypeToken<ArrayList<Gateway>>() {}.getType(); + + public GatewaysManager() {} + + public GatewaysManager(Context context, SharedPreferences preferences) { + this.context = context; + this.preferences = preferences; + profile_manager = ProfileManager.getInstance(context); + } + public Gateway select() { + GatewaySelector gateway_selector = new GatewaySelector(gateways); + return gateway_selector.select(); + } + + public boolean isEmpty() { + return gateways.isEmpty(); + } + + public int size() { + return gateways.size(); + } + + public void addFromString(String gateways) { + List<Gateway> gateways_list = new ArrayList<Gateway>(); + try { + gateways_list = new Gson().fromJson(gateways, list_type); + } catch(JsonSyntaxException e) { + gateways_list.add(new Gson().fromJson(gateways, Gateway.class)); + } + + if(gateways_list != null) { + for (Gateway gateway : gateways_list) + removeDuplicatedGateway(gateway); + this.gateways.addAll(gateways_list); + } else + Log.d("GatewaysManager", "No gateways added"); + } + + @Override + public String toString() { + return new Gson().toJson(gateways, list_type); + } + + public void fromEipServiceJson(JSONObject eip_definition) { + try { + JSONArray gatewaysDefined = eip_definition.getJSONArray("gateways"); + for (int i = 0; i < gatewaysDefined.length(); i++) { + JSONObject gw = gatewaysDefined.getJSONObject(i); + if (isOpenVpnGateway(gw)) { + JSONObject secrets = secretsConfiguration(); + Gateway aux = new Gateway(eip_definition, secrets, gw); + if(!containsProfileWithSecrets(aux.getProfile())) { + addGateway(aux); + } + } + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private boolean isOpenVpnGateway(JSONObject gateway) { + try { + String transport = gateway.getJSONObject("capabilities").getJSONArray("transport").toString(); + return transport.contains("openvpn"); + } catch (JSONException e) { + return false; + } + } + + private JSONObject secretsConfiguration() { + JSONObject result = new JSONObject(); + try { + result.put(Provider.CA_CERT, preferences.getString(Provider.CA_CERT, "")); + result.put(Constants.PRIVATE_KEY, preferences.getString(Constants.PRIVATE_KEY, "")); + result.put(Constants.CERTIFICATE, preferences.getString(Constants.CERTIFICATE, "")); + } catch (JSONException e) { + e.printStackTrace(); + } + return result; + } + + private boolean containsProfileWithSecrets(VpnProfile profile) { + boolean result = false; + + Collection<VpnProfile> profiles = profile_manager.getProfiles(); + for(VpnProfile aux : profiles) { + result = result || sameConnections(profile.mConnections, aux.mConnections) + && profile.mClientCertFilename.equalsIgnoreCase(aux.mClientCertFilename) + && profile.mClientKeyFilename.equalsIgnoreCase(aux.mClientKeyFilename); + } + return result; + } + + private void addGateway(Gateway gateway) { + removeDuplicatedGateway(gateway); + + gateways.add(gateway); + + VpnProfile profile = gateway.getProfile(); + profile_manager.addProfile(profile); + //profile_manager.saveProfile(context, profile); + //profile_manager.saveProfileList(context); + } + + private void removeDuplicatedGateway(Gateway gateway) { + Iterator<Gateway> it = gateways.iterator(); + List<Gateway> gateways_to_remove = new ArrayList<>(); + while(it.hasNext()) { + Gateway aux = it.next(); + if(sameConnections(aux.getProfile().mConnections, gateway.getProfile().mConnections)) { + gateways_to_remove.add(aux); + } + } + gateways.removeAll(gateways_to_remove); + removeDuplicatedProfiles(gateway.getProfile()); + } + + private void removeDuplicatedProfiles(VpnProfile original) { + Collection<VpnProfile> profiles = profile_manager.getProfiles(); + List<VpnProfile> remove_list = new ArrayList<>(); + for(VpnProfile aux : profiles) { + if (sameConnections(original.mConnections, aux.mConnections)) + remove_list.add(aux); + } + for (VpnProfile profile : remove_list) + profile_manager.removeProfile(context, profile); + } + + private boolean sameConnections(Connection[] c1, Connection[] c2) { + int same_connections = 0; + for(Connection c1_aux : c1) { + for(Connection c2_aux : c2) + if(c2_aux.mServerName.equals(c1_aux.mServerName)) { + same_connections++; + break; + } + } + return c1.length == c2.length && c1.length == same_connections; + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java index 6487f6c1..0bbe9db4 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java @@ -28,7 +28,13 @@ import se.leap.bitmaskclient.ConfigHelper; public class VpnCertificateValidator { public final static String TAG = VpnCertificateValidator.class.getSimpleName(); - public boolean isValid(String certificate) { + private String certificate; + + public VpnCertificateValidator(String certificate) { + this.certificate = certificate; + } + + public boolean isValid() { if(!certificate.isEmpty()) { X509Certificate certificate_x509 = ConfigHelper.parseX509CertificateFromString(certificate); return isValid(certificate_x509); diff --git a/app/src/main/res/layout/provider_detail_fragment.xml b/app/src/main/res/layout/provider_detail_fragment.xml index eb90fad9..3b35bae7 100644 --- a/app/src/main/res/layout/provider_detail_fragment.xml +++ b/app/src/main/res/layout/provider_detail_fragment.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/provider_detail_fragment" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7d03c221..1608f487 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -19,7 +19,7 @@ <string name="anonymous_secured_status">Connection secure using an anonymous certificate.</string> <string name="authed_secured_status">Connection secure using your own certificate.</string> <string name="eip_service_label">Encrypted Internet</string> - <string name="title_activity_configuration_wizard">Select a service provider</string> + <string name="configuration_wizard_title">Select a service provider</string> <string name="new_provider_button">Add new Provider</string> <string name="introduce_new_provider">Add a new service provider</string> <string name="save">Save</string> diff --git a/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java b/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java index e5095f00..abab106d 100644 --- a/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java +++ b/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java @@ -62,9 +62,6 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download private Provider selected_provider; final public static String TAG = ConfigurationWizard.class.getSimpleName(); - final public static String TYPE_OF_CERTIFICATE = "type_of_certificate"; - final public static String ANON_CERTIFICATE = "anon_certificate"; - final public static String AUTHED_CERTIFICATE = "authed_certificate"; final protected static String PROVIDER_SET = "PROVIDER SET"; final protected static String SERVICES_RETRIEVED = "SERVICES RETRIEVED"; @@ -187,7 +184,7 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download if (preferences.getBoolean(Constants.ALLOWED_ANON, false)){ mConfigState.putExtra(SERVICES_RETRIEVED, true); - downloadAnonCert(); + downloadVpnCertificate(); } else { mProgressBar.incrementProgressBy(1); hideProgressBar(); @@ -303,15 +300,10 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download /** * Asks ProviderAPI to download an anonymous (anon) VPN certificate. */ - private void downloadAnonCert() { + private void downloadVpnCertificate() { Intent provider_API_command = new Intent(this, ProviderAPI.class); - Bundle parameters = new Bundle(); - - parameters.putString(TYPE_OF_CERTIFICATE, ANON_CERTIFICATE); - provider_API_command.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE); - provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver); startService(provider_API_command); diff --git a/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java index d019ca59..334efaa9 100644 --- a/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java +++ b/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java @@ -182,7 +182,7 @@ public class ProviderAPI extends IntentService { if(validUserLoginData(username, password)) { session_id_bundle = register(username, password); - broadcast_progress(progress++); + broadcastProgress(progress++); } else { if(!wellFormedPassword(password)) { session_id_bundle.putBoolean(RESULT_KEY, false); @@ -232,7 +232,7 @@ public class ProviderAPI extends IntentService { String password = (String) task.get(SessionDialog.PASSWORD); if(validUserLoginData(username, password)) { result = authenticate(username, password); - broadcast_progress(progress++); + broadcastProgress(progress++); } else { if(!wellFormedPassword(password)) { result.putBoolean(RESULT_KEY, false); @@ -312,7 +312,7 @@ public class ProviderAPI extends IntentService { * and sends it as a broadcast. * @param progress */ - private void broadcast_progress(int progress) { + private void broadcastProgress(int progress) { Intent intentUpdate = new Intent(); intentUpdate.setAction(UPDATE_PROGRESSBAR); intentUpdate.addCategory(Intent.CATEGORY_DEFAULT); @@ -497,17 +497,17 @@ public class ProviderAPI extends IntentService { if(!PROVIDER_JSON_DOWNLOADED) current_download = getAndSetProviderJson(last_provider_main_url); if(PROVIDER_JSON_DOWNLOADED || (current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY))) { - broadcast_progress(progress++); + broadcastProgress(progress++); PROVIDER_JSON_DOWNLOADED = true; if(!CA_CERT_DOWNLOADED) current_download = downloadCACert(); if(CA_CERT_DOWNLOADED || (current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY))) { - broadcast_progress(progress++); + broadcastProgress(progress++); CA_CERT_DOWNLOADED = true; current_download = getAndSetEipServiceJson(); if(current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY)) { - broadcast_progress(progress++); + broadcastProgress(progress++); EIP_SERVICE_JSON_DOWNLOADED = true; } } @@ -833,7 +833,7 @@ public class ProviderAPI extends IntentService { urlConnection.setSSLSocketFactory(getProviderSSLSocketFactory()); responseCode = urlConnection.getResponseCode(); - broadcast_progress(progress++); + broadcastProgress(progress++); LeapSRPSession.setToken(""); Log.d(TAG, Integer.toString(responseCode)); } catch (ClientProtocolException e) { @@ -850,7 +850,7 @@ public class ProviderAPI extends IntentService { if(urlConnection != null) { responseCode = urlConnection.getResponseCode(); if(responseCode == 401) { - broadcast_progress(progress++); + broadcastProgress(progress++); LeapSRPSession.setToken(""); Log.d(TAG, Integer.toString(responseCode)); return true; |