diff options
Diffstat (limited to 'app')
33 files changed, 435 insertions, 158 deletions
diff --git a/app/assets/urls/calyx.net.url b/app/assets/urls/calyx.net.url index 8de04fe9..807e9e18 100644 --- a/app/assets/urls/calyx.net.url +++ b/app/assets/urls/calyx.net.url @@ -1,3 +1,3 @@ { - "main_url" : "https://calyx.net/" + "main_url" : "https://calyx.net" } diff --git a/app/assets/urls/demo.bitmask.net.url b/app/assets/urls/demo.bitmask.net.url index 1a412055..0c4de648 100644 --- a/app/assets/urls/demo.bitmask.net.url +++ b/app/assets/urls/demo.bitmask.net.url @@ -1,3 +1,3 @@ {
- "main_url" : "https://demo.bitmask.net/"
+ "main_url" : "https://demo.bitmask.net"
}
diff --git a/app/assets/urls/riseup.net.url b/app/assets/urls/riseup.net.url index 4548b433..42cdb979 100644 --- a/app/assets/urls/riseup.net.url +++ b/app/assets/urls/riseup.net.url @@ -1,3 +1,3 @@ { - "main_url" : "https://riseup.net/" + "main_url" : "https://riseup.net" } diff --git a/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java b/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java index 42bd576a..798b6433 100644 --- a/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java +++ b/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java @@ -140,7 +140,7 @@ public class ProviderApiManager extends ProviderApiManagerBase { else providerDotJsonString = downloadFromApiUrlWithProviderCA("/provider.json", caCert, providerDefinition, dangerOn); - if (!isValidJson(providerDotJsonString)) { + if (ConfigHelper.checkErroneousDownload(providerDotJsonString) || !isValidJson(providerDotJsonString)) { result.putString(ERRORS, resources.getString(malformed_url)); result.putBoolean(BROADCAST_RESULT_KEY, false); return result; @@ -206,9 +206,7 @@ public class ProviderApiManager extends ProviderApiManagerBase { protected Bundle updateVpnCertificate(Provider provider) { Bundle result = new Bundle(); try { - JSONObject providerJson = provider.getDefinition(); - String providerMainUrl = providerJson.getString(Provider.API_URL); - URL newCertStringUrl = new URL(providerMainUrl + "/" + providerJson.getString(Provider.API_VERSION) + "/" + PROVIDER_VPN_CERTIFICATE); + URL newCertStringUrl = new URL(provider.getApiUrlWithVersion() + "/" + PROVIDER_VPN_CERTIFICATE); String certString = downloadWithProviderCA(provider.getCaCert(), newCertStringUrl.toString(), lastDangerOn); if (ConfigHelper.checkErroneousDownload(certString)) { @@ -223,8 +221,7 @@ public class ProviderApiManager extends ProviderApiManagerBase { } } result = loadCertificate(provider, certString); - } catch (IOException | JSONException e) { - // TODO try to get Provider Json + } catch (IOException e) { setErrorResult(result, downloading_vpn_certificate_failed, null); e.printStackTrace(); } diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java index 6c312c87..f701b7aa 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -913,7 +913,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac // CONNECTED // Does not work :( notificationManager.buildOpenVpnNotification( - mProfile.mName, + mProfile != null ? mProfile.mName : "", VpnStatus.getLastCleanLogMessage(this), VpnStatus.getLastCleanLogMessage(this), level, @@ -944,7 +944,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac humanReadableByteCount(out, false, getResources()), humanReadableByteCount(diffOut / OpenVPNManagement.mBytecountInterval, true, getResources())); notificationManager.buildOpenVpnNotification( - mProfile.mName, + mProfile != null ? mProfile.mName : "", netstat, null, LEVEL_CONNECTED, @@ -987,7 +987,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac public void requestInputFromUser(int resid, String needed) { VpnStatus.updateStateString("NEED", "need " + needed, resid, LEVEL_WAITING_FOR_USER_INPUT); notificationManager.buildOpenVpnNotification( - mProfile.mName, + mProfile != null ? mProfile.mName : "", getString(resid), getString(resid), LEVEL_WAITING_FOR_USER_INPUT, diff --git a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java index 50d23106..f07a2a23 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java @@ -29,6 +29,8 @@ import org.spongycastle.util.encoders.Base64; import java.io.BufferedReader; import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -49,9 +51,11 @@ import java.security.interfaces.RSAPrivateKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import static android.R.attr.name; import static se.leap.bitmaskclient.Constants.DEFAULT_SHARED_PREFS_BATTERY_SAVER; @@ -77,7 +81,7 @@ public class ConfigHelper { public static boolean checkErroneousDownload(String downloadedString) { try { - if (downloadedString == null || downloadedString.isEmpty() || new JSONObject(downloadedString).has(ProviderAPI.ERRORS)) { + if (downloadedString == null || downloadedString.isEmpty() || new JSONObject(downloadedString).has(ProviderAPI.ERRORS) || new JSONObject(downloadedString).has(ProviderAPI.BACKEND_ERROR_KEY)) { return true; } else { return false; @@ -128,36 +132,14 @@ public class ConfigHelper { return (X509Certificate) certificate; } + public static String loadInputStreamAsString(java.io.InputStream is) { + java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A"); + return s.hasNext() ? s.next() : ""; + } - public static String loadInputStreamAsString(InputStream inputStream) { - BufferedReader in = null; - try { - StringBuilder buf = new StringBuilder(); - in = new BufferedReader(new InputStreamReader(inputStream)); - - String str; - boolean isFirst = true; - while ( (str = in.readLine()) != null ) { - if (isFirst) - isFirst = false; - else - buf.append('\n'); - buf.append(str); - } - return buf.toString(); - } catch (IOException e) { - Log.e(TAG, "Error opening asset " + name); - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException e) { - Log.e(TAG, "Error closing asset " + name); - } - } - } - - return null; + //allows us to mock FileInputStream + public static InputStream getInputStreamFrom(String filePath) throws FileNotFoundException { + return new FileInputStream(filePath); } protected static RSAPrivateKey parseRsaKeyFromString(String rsaKeyString) { diff --git a/app/src/main/java/se/leap/bitmaskclient/ConfigWizardBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/ConfigWizardBaseActivity.java index ea328216..f0e2de85 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ConfigWizardBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/ConfigWizardBaseActivity.java @@ -1,16 +1,19 @@ package se.leap.bitmaskclient; import android.content.SharedPreferences; +import android.graphics.PorterDuff; +import android.os.Build; import android.os.Bundle; -import android.os.PersistableBundle; import android.support.annotation.DrawableRes; import android.support.annotation.Nullable; import android.support.annotation.StringRes; +import android.support.v4.content.ContextCompat; import android.support.v7.widget.AppCompatImageView; import android.support.v7.widget.AppCompatTextView; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; +import android.widget.ProgressBar; import butterknife.InjectView; @@ -38,6 +41,9 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity { @InjectView(R.id.loading_screen) protected LinearLayout loadingScreen; + @InjectView(R.id.progressbar) + protected ProgressBar progressBar; + @InjectView(R.id.progressbar_description) protected AppCompatTextView progressbarText; @@ -59,6 +65,7 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity { super.setContentView(view); if (provider != null) setProviderHeaderText(provider.getName()); + setProgressbarColorForPreLollipop(); } @Override @@ -66,6 +73,7 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity { super.setContentView(layoutResID); if (provider != null) setProviderHeaderText(provider.getName()); + setProgressbarColorForPreLollipop(); } @Override @@ -73,8 +81,18 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity { super.setContentView(view, params); if (provider != null) setProviderHeaderText(provider.getName()); + setProgressbarColorForPreLollipop(); } + private void setProgressbarColorForPreLollipop() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + progressBar.getIndeterminateDrawable().setColorFilter( + ContextCompat.getColor(this, R.color.colorPrimary), + PorterDuff.Mode.SRC_IN); + } + } + + @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java index 7b84657c..34617af3 100644 --- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java @@ -31,6 +31,7 @@ import android.support.annotation.NonNull; import android.support.v4.app.Fragment; import android.support.v7.app.AlertDialog; import android.support.v7.widget.AppCompatImageView; +import android.support.v7.widget.AppCompatTextView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -60,7 +61,7 @@ import static se.leap.bitmaskclient.Constants.REQUEST_CODE_LOG_IN; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_SWITCH_PROVIDER; import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE; -import static se.leap.bitmaskclient.ProviderCredentialsBaseActivity.USER_MESSAGE; +import static se.leap.bitmaskclient.ProviderAPI.USER_MESSAGE; import static se.leap.bitmaskclient.R.string.vpn_certificate_user_message; public class EipFragment extends Fragment implements Observer { @@ -84,10 +85,10 @@ public class EipFragment extends Fragment implements Observer { Button mainButton; @InjectView(R.id.routed_text) - TextView routedText; + AppCompatTextView routedText; @InjectView(R.id.vpn_route) - TextView vpnRoute; + AppCompatTextView vpnRoute; private EipStatus eipStatus; diff --git a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java index 952f2d1f..19294618 100644 --- a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java @@ -73,7 +73,7 @@ import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFI import static se.leap.bitmaskclient.ProviderAPI.ERRORS; import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE; import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE; -import static se.leap.bitmaskclient.ProviderCredentialsBaseActivity.USER_MESSAGE; +import static se.leap.bitmaskclient.ProviderAPI.USER_MESSAGE; import static se.leap.bitmaskclient.R.string.downloading_vpn_certificate_failed; import static se.leap.bitmaskclient.R.string.vpn_certificate_user_message; @@ -82,12 +82,7 @@ public class MainActivity extends AppCompatActivity implements Observer { public final static String TAG = MainActivity.class.getSimpleName(); - private static final String KEY_ACTIVITY_STATE = "key state of activity"; - private static final String DEFAULT_UI_STATE = "default state"; - private static final String SHOW_DIALOG_STATE = "show dialog"; - private static final String REASON_TO_FAIL = "reason to fail"; - - private static Provider provider = new Provider(); + private Provider provider = new Provider(); private SharedPreferences preferences; private EipStatus eipStatus; private NavigationDrawerFragment navigationDrawerFragment; @@ -213,7 +208,7 @@ public class MainActivity extends AppCompatActivity implements Observer { break; } } - + //TODO: Why do we want this --v? legacy and redundant? Fragment fragment = new EipFragment(); Bundle arguments = new Bundle(); arguments.putParcelable(PROVIDER_KEY, provider); diff --git a/app/src/main/java/se/leap/bitmaskclient/Provider.java b/app/src/main/java/se/leap/bitmaskclient/Provider.java index 7104143c..fd067bf9 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Provider.java +++ b/app/src/main/java/se/leap/bitmaskclient/Provider.java @@ -90,9 +90,8 @@ public final class Provider implements Parcelable { } if (definition != null) { try { - this.definition = new JSONObject(definition); - parseDefinition(this.definition); - } catch (JSONException | NullPointerException e) { + define(new JSONObject(definition)); + } catch (JSONException e) { e.printStackTrace(); } } @@ -133,26 +132,8 @@ public final class Provider implements Parcelable { } public boolean define(JSONObject providerJson) { - /* - * fix against "api_uri": "https://calyx.net.malicious.url.net:4430", - * This method aims to prevent attacks where the provider.json file got manipulated by a third party. - * The main url should not change. - */ - - try { - String providerApiUrl = providerJson.getString(Provider.API_URL); - String providerDomain = providerJson.getString(Provider.DOMAIN); - if (getMainUrlString().contains(providerDomain) && providerApiUrl.contains(providerDomain + ":")) { - definition = providerJson; - parseDefinition(definition); - return true; - } else { - return false; - } - } catch (JSONException e) { - e.printStackTrace(); - return false; - } + definition = providerJson; + return parseDefinition(definition); } public JSONObject getDefinition() { @@ -297,8 +278,6 @@ public final class Provider implements Parcelable { try { json.put(Provider.MAIN_URL, mainUrl); //TODO: add other fields here? - //this is used to save custom providers as json. I guess this doesn't work correctly - //TODO 2: verify that } catch (JSONException e) { e.printStackTrace(); } @@ -345,7 +324,7 @@ public final class Provider implements Parcelable { } } - private void parseDefinition(JSONObject definition) { + private boolean parseDefinition(JSONObject definition) { try { String pin = definition.getString(CA_CERT_FINGERPRINT); this.certificatePin = pin.split(":")[1].trim(); @@ -354,8 +333,9 @@ public final class Provider implements Parcelable { this.allowAnonymous = definition.getJSONObject(Provider.SERVICE).getBoolean(PROVIDER_ALLOW_ANONYMOUS); this.allowRegistered = definition.getJSONObject(Provider.SERVICE).getBoolean(PROVIDER_ALLOWED_REGISTERED); this.apiVersion = getDefinition().getString(Provider.API_VERSION); + return true; } catch (JSONException | ArrayIndexOutOfBoundsException | MalformedURLException e) { - e.printStackTrace(); + return false; } } @@ -446,5 +426,4 @@ public final class Provider implements Parcelable { allowRegistered = false; allowAnonymous = false; } - } diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java index f5efde05..f1f474d7 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java @@ -51,6 +51,7 @@ public class ProviderAPI extends IntentService implements ProviderApiManagerBase ERRORID = "errorId", BACKEND_ERROR_KEY = "error", BACKEND_ERROR_MESSAGE = "message", + USER_MESSAGE = "userMessage", DOWNLOAD_SERVICE_JSON = "ProviderAPI.DOWNLOAD_SERVICE_JSON", PROVIDER_SET_UP = "ProviderAPI.PROVIDER_SET_UP"; diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java index b93abaeb..2cde431e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java @@ -46,6 +46,7 @@ import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; import java.util.ArrayList; import java.util.List; +import java.util.NoSuchElementException; import javax.net.ssl.SSLHandshakeException; @@ -63,19 +64,16 @@ import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY; import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.BACKEND_ERROR_KEY; import static se.leap.bitmaskclient.ProviderAPI.BACKEND_ERROR_MESSAGE; -import static se.leap.bitmaskclient.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_CERTIFICATE_PINNING; -import static se.leap.bitmaskclient.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_CORRUPTED_PROVIDER_JSON; -import static se.leap.bitmaskclient.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_INVALID_CERTIFICATE; -import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE; -import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_SERVICE_JSON; +import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.ERRORID; import static se.leap.bitmaskclient.ProviderAPI.ERRORS; import static se.leap.bitmaskclient.ProviderAPI.FAILED_LOGIN; import static se.leap.bitmaskclient.ProviderAPI.FAILED_SIGNUP; -import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE; +import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.LOGOUT_FAILED; import static se.leap.bitmaskclient.ProviderAPI.LOG_IN; import static se.leap.bitmaskclient.ProviderAPI.LOG_OUT; @@ -90,15 +88,18 @@ import static se.leap.bitmaskclient.ProviderAPI.SUCCESSFUL_LOGIN; import static se.leap.bitmaskclient.ProviderAPI.SUCCESSFUL_LOGOUT; import static se.leap.bitmaskclient.ProviderAPI.SUCCESSFUL_SIGNUP; import static se.leap.bitmaskclient.ProviderAPI.UPDATE_PROVIDER_DETAILS; +import static se.leap.bitmaskclient.ProviderAPI.USER_MESSAGE; +import static se.leap.bitmaskclient.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_CERTIFICATE_PINNING; +import static se.leap.bitmaskclient.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_CORRUPTED_PROVIDER_JSON; +import static se.leap.bitmaskclient.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_INVALID_CERTIFICATE; import static se.leap.bitmaskclient.R.string.certificate_error; -import static se.leap.bitmaskclient.R.string.switch_provider_menu_option; -import static se.leap.bitmaskclient.R.string.vpn_certificate_is_invalid; import static se.leap.bitmaskclient.R.string.error_io_exception_user_message; import static se.leap.bitmaskclient.R.string.error_json_exception_user_message; import static se.leap.bitmaskclient.R.string.error_no_such_algorithm_exception_user_message; import static se.leap.bitmaskclient.R.string.malformed_url; import static se.leap.bitmaskclient.R.string.server_unreachable_message; import static se.leap.bitmaskclient.R.string.service_is_down_error; +import static se.leap.bitmaskclient.R.string.vpn_certificate_is_invalid; import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_cert; import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_details; import static se.leap.bitmaskclient.R.string.warning_expired_provider_cert; @@ -290,7 +291,7 @@ public abstract class ProviderApiManagerBase { JSONObject stepResult = null; OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), stepResult); if (okHttpClient == null) { - return authFailedNotification(stepResult, username); + return backendErrorNotification(stepResult, username); } LeapSRPSession client = new LeapSRPSession(username, password); @@ -302,7 +303,7 @@ public abstract class ProviderApiManagerBase { Bundle result = new Bundle(); if (api_result.has(ERRORS) || api_result.has(BACKEND_ERROR_KEY)) - result = authFailedNotification(api_result, username); + result = backendErrorNotification(api_result, username); else { result.putString(CREDENTIALS_USERNAME, username); result.putString(CREDENTIALS_PASSWORD, password); @@ -349,7 +350,7 @@ public abstract class ProviderApiManagerBase { OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), stepResult); if (okHttpClient == null) { - return authFailedNotification(stepResult, username); + return backendErrorNotification(stepResult, username); } LeapSRPSession client = new LeapSRPSession(username, password); @@ -367,15 +368,15 @@ public abstract class ProviderApiManagerBase { if (client.verify(M2)) { result.putBoolean(BROADCAST_RESULT_KEY, true); } else { - authFailedNotification(step_result, username); + backendErrorNotification(step_result, username); } } else { result.putBoolean(BROADCAST_RESULT_KEY, false); result.putString(CREDENTIALS_USERNAME, username); - result.putString(resources.getString(R.string.user_message), resources.getString(R.string.error_srp_math_error_user_message)); + result.putString(USER_MESSAGE, resources.getString(R.string.error_srp_math_error_user_message)); } } catch (JSONException e) { - result = authFailedNotification(step_result, username); + result = backendErrorNotification(step_result, username); e.printStackTrace(); } @@ -391,7 +392,7 @@ public abstract class ProviderApiManagerBase { return true; } - private Bundle authFailedNotification(JSONObject result, String username) { + private Bundle backendErrorNotification(JSONObject result, String username) { Bundle userNotificationBundle = new Bundle(); if (result.has(ERRORS)) { Object baseErrorMessage = result.opt(ERRORS); @@ -400,14 +401,14 @@ public abstract class ProviderApiManagerBase { JSONObject errorMessage = result.getJSONObject(ERRORS); String errorType = errorMessage.keys().next().toString(); String message = errorMessage.get(errorType).toString(); - userNotificationBundle.putString(resources.getString(R.string.user_message), message); - } catch (JSONException e) { + userNotificationBundle.putString(USER_MESSAGE, message); + } catch (JSONException | NoSuchElementException | NullPointerException e) { e.printStackTrace(); } } else if (baseErrorMessage instanceof String) { try { String errorMessage = result.getString(ERRORS); - userNotificationBundle.putString(resources.getString(R.string.user_message), errorMessage); + userNotificationBundle.putString(USER_MESSAGE, errorMessage); } catch (JSONException e) { e.printStackTrace(); } @@ -418,7 +419,7 @@ public abstract class ProviderApiManagerBase { if (result.has(BACKEND_ERROR_MESSAGE)) { backendErrorMessage = resources.getString(R.string.error) + result.getString(BACKEND_ERROR_MESSAGE); } - userNotificationBundle.putString(resources.getString(R.string.user_message), backendErrorMessage); + userNotificationBundle.putString(USER_MESSAGE, backendErrorMessage); } catch (JSONException e) { e.printStackTrace(); } @@ -431,7 +432,7 @@ public abstract class ProviderApiManagerBase { return userNotificationBundle; } - void sendToReceiverOrBroadcast(ResultReceiver receiver, int resultCode, Bundle resultData, Provider provider) { + private void sendToReceiverOrBroadcast(ResultReceiver receiver, int resultCode, Bundle resultData, Provider provider) { if (resultData == null || resultData == Bundle.EMPTY) { resultData = new Bundle(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java index d41be512..15cd9617 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java @@ -30,6 +30,7 @@ import org.json.JSONException; import butterknife.InjectView; import butterknife.OnClick; import se.leap.bitmaskclient.Constants.CREDENTIAL_ERRORS; +import se.leap.bitmaskclient.eip.EipCommand; import se.leap.bitmaskclient.userstatus.User; import static android.view.View.GONE; @@ -41,9 +42,12 @@ import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; import static se.leap.bitmaskclient.Constants.CREDENTIALS_PASSWORD; import static se.leap.bitmaskclient.Constants.CREDENTIALS_USERNAME; import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; +import static se.leap.bitmaskclient.ProviderAPI.BACKEND_ERROR_KEY; import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.ERRORS; import static se.leap.bitmaskclient.ProviderAPI.LOG_IN; import static se.leap.bitmaskclient.ProviderAPI.SIGN_UP; +import static se.leap.bitmaskclient.ProviderAPI.USER_MESSAGE; /** * Base Activity for activities concerning a provider interaction @@ -59,7 +63,6 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc final private static String SHOWING_FORM = "SHOWING_FORM"; final private static String PERFORMING_ACTION = "PERFORMING_ACTION"; - final public static String USER_MESSAGE = "USER_MESSAGE"; final private static String USERNAME_ERROR = "USERNAME_ERROR"; final private static String PASSWORD_ERROR = "PASSWORD_ERROR"; final private static String PASSWORD_VERIFICATION_ERROR = "PASSWORD_VERIFICATION_ERROR"; @@ -183,7 +186,11 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc String username = usernameField.getText().toString(); String providerDomain = provider.getDomain(); if (username.endsWith(providerDomain)) { - return username.split("@" + providerDomain)[0]; + try { + return username.split("@" + providerDomain)[0]; + } catch (ArrayIndexOutOfBoundsException e) { + return ""; + } } return username; } @@ -237,9 +244,15 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc @Override public void afterTextChanged(Editable s) { if (getUsername().equalsIgnoreCase("")) { + s.clear(); usernameError.setError(getString(R.string.username_ask)); } else { usernameError.setError(null); + String suffix = "@" + provider.getDomain(); + if (!usernameField.getText().toString().endsWith(suffix)) { + s.append(suffix); + usernameField.setSelection(usernameField.getText().toString().indexOf('@')); + } } } }); @@ -344,8 +357,8 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc if (arguments.containsKey(CREDENTIAL_ERRORS.USERNAME_MISSING.toString())) { usernameError.setError(getString(R.string.username_ask)); } - if (arguments.containsKey(getString(R.string.user_message))) { - String userMessageString = arguments.getString(getString(R.string.user_message)); + if (arguments.containsKey(USER_MESSAGE)) { + String userMessageString = arguments.getString(USER_MESSAGE); try { userMessageString = new JSONArray(userMessageString).getString(0); } catch (JSONException e) { @@ -395,6 +408,10 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc switch (resultCode) { case ProviderAPI.SUCCESSFUL_SIGNUP: + String password = resultData.getString(CREDENTIALS_PASSWORD); + String username = resultData.getString(CREDENTIALS_USERNAME); + login(username, password); + break; case ProviderAPI.SUCCESSFUL_LOGIN: downloadVpnCertificate(handledProvider); break; @@ -403,12 +420,11 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc handleReceivedErrors((Bundle) intent.getParcelableExtra(BROADCAST_RESULT_KEY)); break; + case ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE: + // error handling takes place in MainActivity case ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFICATE: successfullyFinished(handledProvider); break; - case ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE: - // TODO activity.setResult(RESULT_CANCELED); - break; } } } diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java index e961b0a2..75fffaf7 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java @@ -207,7 +207,8 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity void handleProviderSetUp(Provider handledProvider) { this.provider = handledProvider; - + adapter.add(provider); + adapter.saveProviders(); if (provider.allowsAnonymous()) { mConfigState.putExtra(SERVICES_RETRIEVED, true); downloadVpnCertificate(); @@ -368,12 +369,6 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity } } - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.configuration_wizard_activity, menu); - return true; - } - public class ProviderAPIBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java b/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java index ed41be67..97ba3b98 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java @@ -31,6 +31,8 @@ public class ProviderManager implements AdapteeCollection<Provider> { private File externalFilesDir; private Set<Provider> defaultProviders; private Set<Provider> customProviders; + private Set<URL> defaultProviderURLs; + private Set<URL> customProviderURLs; private static ProviderManager instance; @@ -52,11 +54,20 @@ public class ProviderManager implements AdapteeCollection<Provider> { private void addDefaultProviders(AssetManager assets_manager) { try { defaultProviders = providersFromAssets(URLS, assets_manager.list(URLS)); + defaultProviderURLs = getProviderUrlSetFromProviderSet(defaultProviders); } catch (IOException e) { e.printStackTrace(); } } + private Set<URL> getProviderUrlSetFromProviderSet(Set<Provider> providers) { + HashSet<URL> providerUrls = new HashSet<>(); + for (Provider provider : providers) { + providerUrls.add(provider.getMainUrl().getUrl()); + } + return providerUrls; + } + private Set<Provider> providersFromAssets(String directory, String[] relativeFilePaths) { Set<Provider> providers = new HashSet<>(); @@ -89,13 +100,14 @@ public class ProviderManager implements AdapteeCollection<Provider> { customProviders = externalFilesDir != null && externalFilesDir.isDirectory() ? providersFromFiles(externalFilesDir.list()) : new HashSet<Provider>(); + customProviderURLs = getProviderUrlSetFromProviderSet(customProviders); } private Set<Provider> providersFromFiles(String[] files) { Set<Provider> providers = new HashSet<>(); try { for (String file : files) { - String mainUrl = extractMainUrlFromInputStream(new FileInputStream(externalFilesDir.getAbsolutePath() + "/" + file)); + String mainUrl = extractMainUrlFromInputStream(ConfigHelper.getInputStreamFrom(externalFilesDir.getAbsolutePath() + "/" + file)); providers.add(new Provider(new URL(mainUrl))); } } catch (MalformedURLException | FileNotFoundException e) { @@ -132,6 +144,8 @@ public class ProviderManager implements AdapteeCollection<Provider> { allProviders.addAll(defaultProviders); if(customProviders != null) allProviders.addAll(customProviders); + //add an option to add a custom provider + //TODO: refactor me? allProviders.add(new Provider()); return allProviders; } @@ -153,32 +167,59 @@ public class ProviderManager implements AdapteeCollection<Provider> { @Override public boolean add(Provider element) { - return !defaultProviders.contains(element) || customProviders.add(element); + return element != null && + !defaultProviderURLs.contains(element.getMainUrl().getUrl()) && + customProviders.add(element) && + customProviderURLs.add(element.getMainUrl().getUrl()); } @Override public boolean remove(Object element) { - return customProviders.remove(element); + return element instanceof Provider && + customProviders.remove(element) && + customProviderURLs.remove(((Provider) element).getMainUrl().getUrl()); } @Override public boolean addAll(Collection<? extends Provider> elements) { - return customProviders.addAll(elements); + Iterator iterator = elements.iterator(); + boolean addedAll = true; + while (iterator.hasNext()) { + Provider p = (Provider) iterator.next(); + addedAll = customProviders.add(p) && + customProviderURLs.add(p.getMainUrl().getUrl()) && + addedAll; + } + return addedAll; } @Override public boolean removeAll(Collection<?> elements) { - if(!elements.getClass().equals(Provider.class)) + Iterator iterator = elements.iterator(); + boolean removedAll = true; + try { + while (iterator.hasNext()) { + Provider p = (Provider) iterator.next(); + removedAll = ((defaultProviders.remove(p) && defaultProviderURLs.remove(p.getMainUrl().getUrl())) || + (customProviders.remove(p) && customProviderURLs.remove(p.getMainUrl().getUrl()))) && + removedAll; + } + } catch (ClassCastException e) { return false; - return defaultProviders.removeAll(elements) || customProviders.removeAll(elements); + } + + return removedAll; } @Override public void clear() { defaultProviders.clear(); customProviders.clear(); + customProviderURLs.clear(); + defaultProviderURLs.clear(); } + //FIXME: removed custom providers should be deleted here as well void saveCustomProvidersToFile() { try { for (Provider provider : customProviders) { diff --git a/app/src/main/java/se/leap/bitmaskclient/StartActivity.java b/app/src/main/java/se/leap/bitmaskclient/StartActivity.java index 39717bd8..6bbdeb4f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/StartActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/StartActivity.java @@ -13,12 +13,10 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import de.blinkt.openvpn.core.VpnStatus; -import se.leap.bitmaskclient.eip.EIP; import se.leap.bitmaskclient.eip.EipCommand; import se.leap.bitmaskclient.userstatus.User; import static se.leap.bitmaskclient.Constants.APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE; -import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT; import static se.leap.bitmaskclient.Constants.PREFERENCES_APP_VERSION; import static se.leap.bitmaskclient.Constants.PROVIDER_EIP_DEFINITION; @@ -32,7 +30,7 @@ import static se.leap.bitmaskclient.MainActivity.ACTION_SHOW_VPN_FRAGMENT; * and acts and calls another activity accordingly. * */ -public class StartActivity extends Activity { +public class StartActivity extends Activity{ public static final String TAG = StartActivity.class.getSimpleName(); @Retention(RetentionPolicy.SOURCE) @@ -184,12 +182,9 @@ public class StartActivity extends Activity { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (data == null) { - return; - } if (requestCode == REQUEST_CODE_CONFIGURE_LEAP) { - if (resultCode == RESULT_OK && data.hasExtra(Provider.KEY)) { + if (resultCode == RESULT_OK && data != null && data.hasExtra(Provider.KEY)) { Provider provider = data.getParcelableExtra(Provider.KEY); ConfigHelper.storeProviderInPreferences(preferences, provider); EipCommand.startVPN(this, false); diff --git a/app/src/main/res/layout-xlarge/eip_service_fragment.xml b/app/src/main/res/layout-xlarge/eip_service_fragment.xml index b7af5797..c09bda2d 100644 --- a/app/src/main/res/layout-xlarge/eip_service_fragment.xml +++ b/app/src/main/res/layout-xlarge/eip_service_fragment.xml @@ -54,7 +54,7 @@ app:srcCompat="@drawable/ic_colorsquare" /> - <TextView + <android.support.v7.widget.AppCompatTextView android:id="@+id/eipLabel" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -65,6 +65,7 @@ android:layout_marginRight="@dimen/stdpadding" android:text="@string/eip_service_label" android:textAppearance="?android:attr/textAppearanceMedium" + android:gravity="center" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -97,7 +98,7 @@ style="@style/BitmaskButtonBlack" /> - <TextView + <android.support.v7.widget.AppCompatTextView android:id="@+id/routed_text" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -107,12 +108,13 @@ android:layout_marginLeft="@dimen/stdpadding" android:layout_marginRight="@dimen/stdpadding" android:text="@string/vpn_securely_routed" + android:gravity="center" android:visibility="visible" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/eipLabel" /> - <TextView + <android.support.v7.widget.AppCompatTextView android:id="@+id/vpn_route" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -120,6 +122,7 @@ android:layout_marginStart="@dimen/stdpadding" android:layout_marginLeft="@dimen/stdpadding" android:layout_marginRight="@dimen/stdpadding" + android:gravity="center" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/routed_text" /> diff --git a/app/src/main/res/layout/eip_service_fragment.xml b/app/src/main/res/layout/eip_service_fragment.xml index 814ec310..30b94de9 100644 --- a/app/src/main/res/layout/eip_service_fragment.xml +++ b/app/src/main/res/layout/eip_service_fragment.xml @@ -52,7 +52,7 @@ app:srcCompat="@drawable/ic_colorsquare" /> - <TextView + <android.support.v7.widget.AppCompatTextView android:id="@+id/eipLabel" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -63,6 +63,7 @@ android:layout_marginRight="@dimen/stdpadding" android:text="@string/eip_service_label" android:textAppearance="?android:attr/textAppearanceMedium" + android:gravity="center" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -97,7 +98,7 @@ style="@style/BitmaskButtonBlack" /> - <TextView + <android.support.v7.widget.AppCompatTextView android:id="@+id/routed_text" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -107,12 +108,13 @@ android:layout_marginLeft="@dimen/stdpadding" android:layout_marginRight="@dimen/stdpadding" android:text="@string/vpn_securely_routed" + android:gravity="center" android:visibility="visible" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/eipLabel" /> - <TextView + <android.support.v7.widget.AppCompatTextView android:id="@+id/vpn_route" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -120,6 +122,7 @@ android:layout_marginStart="@dimen/stdpadding" android:layout_marginLeft="@dimen/stdpadding" android:layout_marginRight="@dimen/stdpadding" + android:gravity="center" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/routed_text" /> diff --git a/app/src/main/res/menu/configuration_wizard_activity.xml b/app/src/main/res/menu/configuration_wizard_activity.xml deleted file mode 100644 index 21591547..00000000 --- a/app/src/main/res/menu/configuration_wizard_activity.xml +++ /dev/null @@ -1,15 +0,0 @@ -<menu xmlns:android="http://schemas.android.com/apk/res/android" > - - <item - android:id="@+id/about_leap" - android:orderInCategory="110" - android:title="@string/about"/> - - <item - android:id="@+id/new_provider" - android:orderInCategory="210" - android:title="@string/add_provider" - android:showAsAction="ifRoom|withText" - android:icon="@drawable/ic_menu_add" - /> -</menu>
\ No newline at end of file diff --git a/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java b/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java index e5ee6c49..885d69db 100644 --- a/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java +++ b/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java @@ -34,6 +34,7 @@ import se.leap.bitmaskclient.eip.EIP; import static android.text.TextUtils.isEmpty; import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; +import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.ERRORS; import static se.leap.bitmaskclient.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_CERTIFICATE_PINNING; @@ -84,6 +85,7 @@ public class ProviderApiManager extends ProviderApiManagerBase { //provider details invalid if (currentDownload.containsKey(ERRORS)) { + currentDownload.putParcelable(PROVIDER_KEY, provider); return currentDownload; } @@ -121,7 +123,7 @@ public class ProviderApiManager extends ProviderApiManagerBase { providerDotJsonString = downloadFromApiUrlWithProviderCA("/provider.json", caCert, providerDefinition); } - if (!isValidJson(providerDotJsonString)) { + if (ConfigHelper.checkErroneousDownload(providerDotJsonString) || !isValidJson(providerDotJsonString)) { setErrorResult(result, malformed_url, null); return result; } @@ -177,9 +179,7 @@ public class ProviderApiManager extends ProviderApiManagerBase { protected Bundle updateVpnCertificate(Provider provider) { Bundle result = new Bundle(); try { - JSONObject providerJson = provider.getDefinition(); - String providerMainUrl = providerJson.getString(Provider.API_URL); - URL newCertStringUrl = new URL(providerMainUrl + "/" + providerJson.getString(Provider.API_VERSION) + "/" + PROVIDER_VPN_CERTIFICATE); + URL newCertStringUrl = new URL(provider.getApiUrlWithVersion() + "/" + PROVIDER_VPN_CERTIFICATE); String certString = downloadWithProviderCA(provider.getCaCert(), newCertStringUrl.toString()); if (ConfigHelper.checkErroneousDownload(certString)) { @@ -194,7 +194,7 @@ public class ProviderApiManager extends ProviderApiManagerBase { } } return loadCertificate(provider, certString); - } catch (IOException | JSONException e) { + } catch (IOException e) { // TODO try to get Provider Json setErrorResult(result, downloading_vpn_certificate_failed, null); e.printStackTrace(); diff --git a/app/src/production/java/se/leap/bitmaskclient/ProviderListActivity.java b/app/src/production/java/se/leap/bitmaskclient/ProviderListActivity.java index b6e67331..cf1d1aa6 100644 --- a/app/src/production/java/se/leap/bitmaskclient/ProviderListActivity.java +++ b/app/src/production/java/se/leap/bitmaskclient/ProviderListActivity.java @@ -39,11 +39,10 @@ public class ProviderListActivity extends ProviderListBaseActivity { setUpProvider(); } + @Override public void showAndSelectProvider(String provider_main_url) { try { provider = new Provider(new URL((provider_main_url))); - adapter.add(provider); - adapter.saveProviders(); autoSelectProvider(provider); } catch (MalformedURLException e) { e.printStackTrace(); diff --git a/app/src/test/java/se/leap/bitmaskclient/ProviderTest.java b/app/src/test/java/se/leap/bitmaskclient/ProviderTest.java index 794c3087..495d5b3f 100644 --- a/app/src/test/java/se/leap/bitmaskclient/ProviderTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/ProviderTest.java @@ -1,9 +1,15 @@ package se.leap.bitmaskclient; +import org.json.JSONException; import org.junit.Test; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + import se.leap.bitmaskclient.testutils.TestSetupHelper; +import static junit.framework.Assert.assertFalse; import static org.junit.Assert.assertTrue; /** @@ -18,4 +24,28 @@ public class ProviderTest { assertTrue("Providers should be same:", p1.equals(p2)); } + @Test + public void testEquals_sameFields_returnsFalse() throws Exception { + Provider p1 = TestSetupHelper.getConfiguredProvider(); + Provider p2 = TestSetupHelper.getConfiguredProvider(); + p2.setMainUrl("http://somethingsdiffer.org"); + assertFalse("Providers should be same:", p1.equals(p2)); + } + + // see ProviderManagerTest testing add(...) + @Test + public void testEqualsThroughSetContains_differentFields_returnsFalse() throws Exception { + Provider p1 = TestSetupHelper.getConfiguredProvider(); + Provider p2 = TestSetupHelper.getConfiguredProvider(); + p2.setMainUrl("http://somethingsdiffer.org"); + Provider p3 = new Provider("https://anotherprovider.net"); + + Set<Provider> defaultProviders = new HashSet<>(); + defaultProviders.add(p1); + defaultProviders.add(p2); + + assertTrue(defaultProviders.contains(p1)); + assertTrue(defaultProviders.contains(p2)); + assertFalse(defaultProviders.contains(p3)); + } } diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java index c2362c7b..d85b050f 100644 --- a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java +++ b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java @@ -14,6 +14,7 @@ import org.json.JSONObject; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.security.NoSuchAlgorithmException; @@ -29,7 +30,6 @@ import java.util.Set; import okhttp3.OkHttpClient; import se.leap.bitmaskclient.ConfigHelper; -import se.leap.bitmaskclient.Constants; import se.leap.bitmaskclient.OkHttpClientGenerator; import se.leap.bitmaskclient.Provider; import se.leap.bitmaskclient.R; @@ -343,6 +343,18 @@ public class MockHelper { return resultReceiver; } + public static void mockConfigHelperForFileInputStream() throws FileNotFoundException { + mockStatic(ConfigHelper.class); + when(ConfigHelper.loadInputStreamAsString(any(InputStream.class))).thenCallRealMethod(); + when(ConfigHelper.getInputStreamFrom(anyString())).thenAnswer(new Answer<InputStream>() { + @Override + public InputStream answer(InvocationOnMock invocation) throws Throwable { + String filename = (String) invocation.getArguments()[0]; + return getClass().getClassLoader().getResourceAsStream(filename); + } + }); + } + public static void mockConfigHelper(String mockedFingerprint, final Provider providerFromPrefs) throws CertificateEncodingException, NoSuchAlgorithmException { // FIXME use MockSharedPreferences instead of provider mockStatic(ConfigHelper.class); @@ -366,6 +378,7 @@ public class MockHelper { when(ConfigHelper.getFingerprintFromCertificate(any(X509Certificate.class), anyString())).thenReturn(mockedFingerprint); when(ConfigHelper.checkErroneousDownload(anyString())).thenCallRealMethod(); when(ConfigHelper.parseX509CertificateFromString(anyString())).thenCallRealMethod(); + when(ConfigHelper.loadInputStreamAsString(any(InputStream.class))).thenCallRealMethod(); } public static void mockFingerprintForCertificate(String mockedFingerprint) throws CertificateEncodingException, NoSuchAlgorithmException { mockStatic(ConfigHelper.class); diff --git a/app/src/test/resources/externalDir/leapcolombia.json b/app/src/test/resources/externalDir/leapcolombia.json new file mode 100644 index 00000000..6820988c --- /dev/null +++ b/app/src/test/resources/externalDir/leapcolombia.json @@ -0,0 +1,3 @@ +{ + "main_url" : "https://leapcolombia.org" +}
\ No newline at end of file diff --git a/app/src/test/resources/preconfigured/calyx.net.json b/app/src/test/resources/preconfigured/calyx.net.json new file mode 100644 index 00000000..30ab43c5 --- /dev/null +++ b/app/src/test/resources/preconfigured/calyx.net.json @@ -0,0 +1,37 @@ +{ + "api_uri": "https://calyx.net:4430", + "api_version": "1", + "ca_cert_fingerprint": "SHA256: 43683c9ba3862c5384a8c1885072fcac40b5d2d4dd67331443f13a3077fa2e69", + "ca_cert_uri": "https://calyx.net/ca.crt", + "default_language": "en", + "description": { + "en": "Calyx Institute privacy focused ISP testbed" + }, + "domain": "calyx.net", + "enrollment_policy": "open", + "languages": [ + "en" + ], + "name": { + "en": "calyx" + }, + "service": { + "allow_anonymous": false, + "allow_free": true, + "allow_limited_bandwidth": false, + "allow_paid": false, + "allow_registration": true, + "allow_unlimited_bandwidth": true, + "bandwidth_limit": 102400, + "default_service_level": 1, + "levels": { + "1": { + "description": "Please donate.", + "name": "free" + } + } + }, + "services": [ + "openvpn" + ] +}
\ No newline at end of file diff --git a/app/src/test/resources/preconfigured/calyx.net.pem b/app/src/test/resources/preconfigured/calyx.net.pem new file mode 100644 index 00000000..cedb2e38 --- /dev/null +++ b/app/src/test/resources/preconfigured/calyx.net.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQ0FADBEMQ4wDAYDVQQKDAVjYWx5 +eDEaMBgGA1UECwwRaHR0cHM6Ly9jYWx5eC5uZXQxFjAUBgNVBAMMDWNhbHl4IFJv +b3QgQ0EwHhcNMTMwNzAyMDAwMDAwWhcNMjMwNzAyMDAwMDAwWjBEMQ4wDAYDVQQK +DAVjYWx5eDEaMBgGA1UECwwRaHR0cHM6Ly9jYWx5eC5uZXQxFjAUBgNVBAMMDWNh +bHl4IFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDupdnx +Bgat537XOqrZOulE/RvjoXB1S07sy9/MMtksXFoQuWJZRCSTp1Jaqg3H/e9o1nct +LQO91+izfJe07TUyajFl7CfllYgMeyKTYcT85dFwNX4pcIHZr8UpmO0MpGBoR4W1 +8cPa3vxAG0CsyUmrASJVyhRouk4qazRosM5RwBxTdMzCK7L3SwqPQoxlY9YmRJlD +XYZlK5VMJd0dj9XxhMeFs5n43R0bsDENryrExSbuxoNfnUoQg3wffKk+Z0gW7YgW +ivPsbObqOgXUuBEU0xr9xMNBpU33ffLIsccrHq1EKp8zGfCOcww6v7+zEadUkVLo +6j/rRhYYgRw9lijZG1rMuV/mTGnUqbjHsdoz5mzkFFWeTSqo44lvhveUyCcwRNmi +2sjS77l0fCTzfreufffFoOEcRVMRfsnJdu/xPeARoXILEx8nQ421mSn6spOZlDQr +Tt0T0BAWt+VNc+m0IGSW3SwS7r5MUyQ/M5GrbQBGi5W2SzPriKZ79YTOwPVmXKLZ +vJoEuKRDkEPJLBAhcD5oSQljOm/Wp/hjmRH4HnI1y4XMshWlDsyRDB1Au5yrsfwN +noFVSskEcbXlZfNgml4lktLBqz+qwsw+voq6Ak7ROKbc0ii5s8+iNMbAtIK7GcFF +kuKKIyRmmGlDim/SDhlNdWo7Ah4Akde7zfWufwIDAQABo2AwXjAdBgNVHQ4EFgQU +AY8+K4ZupAQ+L9ttFJG3vaLBq5gwDgYDVR0PAQH/BAQDAgIEMAwGA1UdEwQFMAMB +Af8wHwYDVR0jBBgwFoAUAY8+K4ZupAQ+L9ttFJG3vaLBq5gwDQYJKoZIhvcNAQEN +BQADggIBAOpXi5o3g/2o2rPa53iG7Zgcy8RpePGgZk6xknGYWeLamEqSh+XWQZ2w +2kQP54bf8HfPj3ugJBWsVtYAs/ltJwzeBfYDrwEJd1N8tw2IRuGlQOWiTAVVLBj4 +Zs+dikSuMoA399f/7BlUIEpVLUiV/emTtbkjFnDeKEV9zql6ypR0BtR8Knf8ALvL +YfMsWLvTe4rXeypzxIaE2pn8ttcXLYAX0ml2MofTi5xcDhMn1vznKIvs82xhncQx +I1MJMWqPHNHgJUJpA+y1IFh5LPbpag9PKQ0yQ9sM+/dyGumF2jElsMw71flh/Txr +2dEv8+FNV1pPK26XJZBK24rNWFs30eAFfH9EQCwVla174I4PDoWqsIR7vtQMObDt +Bq34R3TjjJJIt2sCSlYLooWwiK7Q+d/SgYqA+MSDmmwhzm86ToK6cwbCsvuw1AxR +X6VIs4U8wOotgljzX/CSpKqlxcqZjhnAuelZ1+KiN8RHKPj7AzSLYOv/YwTjLTIq +EOxquoNR58uDa5pBG22a7xWbSaKosn/mEl8SrUr6klzzc8Vh09IMoxrw74uLdAg2 +1jnrhm7qg91Ttb0aXiqbV+Kg/qQzojdewnnoBFnv4jaQ3y8zDCfMhsBtWlWz4Knb +Zqga1WyRm3Gj1j6IV0oOincYMrw5YA7bgXpwop/Lo/mmliMA14ps +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/app/src/test/resources/preconfigured/demo.bitmask.net.json b/app/src/test/resources/preconfigured/demo.bitmask.net.json new file mode 100644 index 00000000..e7fe6099 --- /dev/null +++ b/app/src/test/resources/preconfigured/demo.bitmask.net.json @@ -0,0 +1,42 @@ +{ + "api_uri": "https://api.demo.bitmask.net:4430", + "api_version": "1", + "ca_cert_fingerprint": "SHA256: 0f17c033115f6b76ff67871872303ff65034efe7dd1b910062ca323eb4da5c7e", + "ca_cert_uri": "https://demo.bitmask.net/ca.crt", + "default_language": "en", + "description": { + "el": "demo.bitmask.net allows you to test the Bitmask application. User accounts may be periodically deleted.", + "en": "demo.bitmask.net allows you to test the Bitmask application. User accounts may be periodically deleted.", + "es": "demo.bitmask.net allows you to test the Bitmask application. User accounts may be periodically deleted." + }, + "domain": "demo.bitmask.net", + "enrollment_policy": "open", + "languages": [ + "de", + "en", + "es", + "pt" + ], + "name": { + "en": "Bitmask" + }, + "service": { + "allow_anonymous": true, + "allow_free": true, + "allow_limited_bandwidth": false, + "allow_paid": false, + "allow_registration": true, + "allow_unlimited_bandwidth": true, + "bandwidth_limit": 102400, + "default_service_level": 1, + "levels": { + "1": { + "description": "Please donate.", + "name": "free" + } + } + }, + "services": [ + "openvpn" + ] +}
\ No newline at end of file diff --git a/app/src/test/resources/preconfigured/demo.bitmask.net.pem b/app/src/test/resources/preconfigured/demo.bitmask.net.pem new file mode 100644 index 00000000..9a422161 --- /dev/null +++ b/app/src/test/resources/preconfigured/demo.bitmask.net.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFbzCCA1egAwIBAgIBATANBgkqhkiG9w0BAQ0FADBKMRgwFgYDVQQDDA9CaXRt +YXNrIFJvb3QgQ0ExEDAOBgNVBAoMB0JpdG1hc2sxHDAaBgNVBAsME2h0dHBzOi8v +Yml0bWFzay5uZXQwHhcNMTIxMTA2MDAwMDAwWhcNMjIxMTA2MDAwMDAwWjBKMRgw +FgYDVQQDDA9CaXRtYXNrIFJvb3QgQ0ExEDAOBgNVBAoMB0JpdG1hc2sxHDAaBgNV +BAsME2h0dHBzOi8vYml0bWFzay5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQC1eV4YvayaU+maJbWrD4OHo3d7S1BtDlcvkIRS1Fw3iYDjsyDkZxai +dHp4EUasfNQ+EVtXUvtk6170EmLco6Elg8SJBQ27trE6nielPRPCfX3fQzETRfvB +7tNvGw4Jn2YKiYoMD79kkjgyZjkJ2r/bEHUSevmR09BRp86syHZerdNGpXYhcQ84 +CA1+V+603GFIHnrP+uQDdssW93rgDNYu+exT+Wj6STfnUkugyjmPRPjL7wh0tzy+ +znCeLl4xiV3g9sjPnc7r2EQKd5uaTe3j71sDPF92KRk0SSUndREz+B1+Dbe/RGk4 +MEqGFuOzrtsgEhPIX0hplhb0Tgz/rtug+yTT7oJjBa3u20AAOQ38/M99EfdeJvc4 +lPFF1XBBLh6X9UKF72an2NuANiX6XPySnJgZ7nZ09RiYZqVwu/qt3DfvLfhboq+0 +bQvLUPXrVDr70onv5UDjpmEA/cLmaIqqrduuTkFZOym65/PfAPvpGnt7crQj/Ibl +DEDYZQmP7AS+6zBjoOzNjUGE5r40zWAR1RSi7zliXTu+yfsjXUIhUAWmYR6J3KxB +lfsiHBQ+8dn9kC3YrUexWoOqBiqJOAJzZh5Y1tqgzfh+2nmHSB2dsQRs7rDRRlyy +YMbkpzL9ZsOUO2eTP1mmar6YjCN+rggYjRrX71K2SpBG6b1zZxOG+wIDAQABo2Aw +XjAdBgNVHQ4EFgQUuYGDLL2sswnYpHHvProt1JU+D48wDgYDVR0PAQH/BAQDAgIE +MAwGA1UdEwQFMAMBAf8wHwYDVR0jBBgwFoAUuYGDLL2sswnYpHHvProt1JU+D48w +DQYJKoZIhvcNAQENBQADggIBADeG67vaFcbITGpi51264kHPYPEWaXUa5XYbtmBl +cXYyB6hY5hv/YNuVGJ1gWsDmdeXEyj0j2icGQjYdHRfwhrbEri+h1EZOm1cSBDuY +k/P5+ctHyOXx8IE79DBsZ6IL61UKIaKhqZBfLGYcWu17DVV6+LT+AKtHhOrv3TSj +RnAcKnCbKqXLhUPXpK0eTjPYS2zQGQGIhIy9sQXVXJJJsGrPgMxna1Xw2JikBOCG +htD/JKwt6xBmNwktH0GI/LVtVgSp82Clbn9C4eZN9E5YbVYjLkIEDhpByeC71QhX +EIQ0ZR56bFuJA/CwValBqV/G9gscTPQqd+iETp8yrFpAVHOW+YzSFbxjTEkBte1J +aF0vmbqdMAWLk+LEFPQRptZh0B88igtx6tV5oVd+p5IVRM49poLhuPNJGPvMj99l +mlZ4+AeRUnbOOeAEuvpLJbel4rhwFzmUiGoeTVoPZyMevWcVFq6BMkS+jRR2w0jK +G6b0v5XDHlcFYPOgUrtsOBFJVwbutLvxdk6q37kIFnWCd8L3kmES5q4wjyFK47Co +Ja8zlx64jmMZPg/t3wWqkZgXZ14qnbyG5/lGsj5CwVtfDljrhN0oCWK1FZaUmW3d +69db12/g4f6phldhxiWuGC/W6fCW5kre7nmhshcltqAJJuU47iX+DarBFiIj816e +yV8e +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/app/src/test/resources/preconfigured/riseup.net.json b/app/src/test/resources/preconfigured/riseup.net.json new file mode 100644 index 00000000..9a5ec79e --- /dev/null +++ b/app/src/test/resources/preconfigured/riseup.net.json @@ -0,0 +1,37 @@ +{ + "api_uri": "https://api.black.riseup.net:443", + "api_version": "1", + "ca_cert_fingerprint": "SHA256: a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494", + "ca_cert_uri": "https://black.riseup.net/ca.crt", + "default_language": "en", + "description": { + "en": "Riseup is a non-profit collective in Seattle that provides online communication tools for people and groups working toward liberatory social change." + }, + "domain": "riseup.net", + "enrollment_policy": "open", + "languages": [ + "en" + ], + "name": { + "en": "Riseup Networks" + }, + "service": { + "allow_anonymous": false, + "allow_free": true, + "allow_limited_bandwidth": false, + "allow_paid": false, + "allow_registration": true, + "allow_unlimited_bandwidth": true, + "bandwidth_limit": 102400, + "default_service_level": 1, + "levels": { + "1": { + "description": "Please donate.", + "name": "free" + } + } + }, + "services": [ + "openvpn" + ] +}
\ No newline at end of file diff --git a/app/src/test/resources/preconfigured/riseup.net.pem b/app/src/test/resources/preconfigured/riseup.net.pem new file mode 100644 index 00000000..c890aff4 --- /dev/null +++ b/app/src/test/resources/preconfigured/riseup.net.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIBATANBgkqhkiG9w0BAQ0FADBZMRgwFgYDVQQKDA9SaXNl +dXAgTmV0d29ya3MxGzAZBgNVBAsMEmh0dHBzOi8vcmlzZXVwLm5ldDEgMB4GA1UE +AwwXUmlzZXVwIE5ldHdvcmtzIFJvb3QgQ0EwHhcNMTQwNDI4MDAwMDAwWhcNMjQw +NDI4MDAwMDAwWjBZMRgwFgYDVQQKDA9SaXNldXAgTmV0d29ya3MxGzAZBgNVBAsM +Emh0dHBzOi8vcmlzZXVwLm5ldDEgMB4GA1UEAwwXUmlzZXVwIE5ldHdvcmtzIFJv +b3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC76J4ciMJ8Sg0m +TP7DF2DT9zNe0Csk4myoMFC57rfJeqsAlJCv1XMzBmXrw8wq/9z7XHv6n/0sWU7a +7cF2hLR33ktjwODlx7vorU39/lXLndo492ZBhXQtG1INMShyv+nlmzO6GT7ESfNE +LliFitEzwIegpMqxCIHXFuobGSCWF4N0qLHkq/SYUMoOJ96O3hmPSl1kFDRMtWXY +iw1SEKjUvpyDJpVs3NGxeLCaA7bAWhDY5s5Yb2fA1o8ICAqhowurowJpW7n5ZuLK +5VNTlNy6nZpkjt1QycYvNycffyPOFm/Q/RKDlvnorJIrihPkyniV3YY5cGgP+Qkx +HUOT0uLA6LHtzfiyaOqkXwc4b0ZcQD5Vbf6Prd20Ppt6ei0zazkUPwxld3hgyw58 +m/4UIjG3PInWTNf293GngK2Bnz8Qx9e/6TueMSAn/3JBLem56E0WtmbLVjvko+LF +PM5xA+m0BmuSJtrD1MUCXMhqYTtiOvgLBlUm5zkNxALzG+cXB28k6XikXt6MRG7q +hzIPG38zwkooM55yy5i1YfcIi5NjMH6A+t4IJxxwb67MSb6UFOwg5kFokdONZcwj +shczHdG9gLKSBIvrKa03Nd3W2dF9hMbRu//STcQxOailDBQCnXXfAATj9pYzdY4k +ha8VCAREGAKTDAex9oXf1yRuktES4QIDAQABo2AwXjAdBgNVHQ4EFgQUC4tdmLVu +f9hwfK4AGliaet5KkcgwDgYDVR0PAQH/BAQDAgIEMAwGA1UdEwQFMAMBAf8wHwYD +VR0jBBgwFoAUC4tdmLVuf9hwfK4AGliaet5KkcgwDQYJKoZIhvcNAQENBQADggIB +AGzL+GRnYu99zFoy0bXJKOGCF5XUXP/3gIXPRDqQf5g7Cu/jYMID9dB3No4Zmf7v +qHjiSXiS8jx1j/6/Luk6PpFbT7QYm4QLs1f4BlfZOti2KE8r7KRDPIecUsUXW6P/ +3GJAVYH/+7OjA39za9AieM7+H5BELGccGrM5wfl7JeEz8in+V2ZWDzHQO4hMkiTQ +4ZckuaL201F68YpiItBNnJ9N5nHr1MRiGyApHmLXY/wvlrOpclh95qn+lG6/2jk7 +3AmihLOKYMlPwPakJg4PYczm3icFLgTpjV5sq2md9bRyAg3oPGfAuWHmKj2Ikqch +Td5CHKGxEEWbGUWEMP0s1A/JHWiCbDigc4Cfxhy56CWG4q0tYtnc2GMw8OAUO6Wf +Xu5pYKNkzKSEtT/MrNJt44tTZWbKV/Pi/N2Fx36my7TgTUj7g3xcE9eF4JV2H/sg +tsK3pwE0FEqGnT4qMFbixQmc8bGyuakr23wjMvfO7eZUxBuWYR2SkcP26sozF9PF +tGhbZHQVGZUTVPyvwahMUEhbPGVerOW0IYpxkm0x/eaWdTc4vPpf/rIlgbAjarnJ +UN9SaWRlWKSdP4haujnzCoJbM7dU9bjvlGZNyXEekgeT0W2qFeGGp+yyUWw8tNsp +0BuC1b7uW/bBn/xKm319wXVDvBgZgcktMolak39V7DVO +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/app/src/test/resources/preconfigured/urls/calyx.net.url b/app/src/test/resources/preconfigured/urls/calyx.net.url new file mode 100644 index 00000000..807e9e18 --- /dev/null +++ b/app/src/test/resources/preconfigured/urls/calyx.net.url @@ -0,0 +1,3 @@ +{ + "main_url" : "https://calyx.net" +} diff --git a/app/src/test/resources/preconfigured/urls/demo.bitmask.net.url b/app/src/test/resources/preconfigured/urls/demo.bitmask.net.url new file mode 100644 index 00000000..0c4de648 --- /dev/null +++ b/app/src/test/resources/preconfigured/urls/demo.bitmask.net.url @@ -0,0 +1,3 @@ +{
+ "main_url" : "https://demo.bitmask.net"
+}
diff --git a/app/src/test/resources/preconfigured/urls/riseup.net.url b/app/src/test/resources/preconfigured/urls/riseup.net.url new file mode 100644 index 00000000..42cdb979 --- /dev/null +++ b/app/src/test/resources/preconfigured/urls/riseup.net.url @@ -0,0 +1,3 @@ +{ + "main_url" : "https://riseup.net" +} |