diff options
28 files changed, 1142 insertions, 1336 deletions
diff --git a/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java b/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java index 8ca971e0..83a3044e 100644 --- a/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java +++ b/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java @@ -48,13 +48,11 @@ import okhttp3.OkHttpClient; import se.leap.bitmaskclient.eip.EIP; import static android.text.TextUtils.isEmpty; -import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOWED_REGISTERED; -import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; +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.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_CERTIFICATE_PINNING; import static se.leap.bitmaskclient.ProviderAPI.ERRORS; -import static se.leap.bitmaskclient.ProviderAPI.RESULT_KEY; import static se.leap.bitmaskclient.R.string.certificate_error; import static se.leap.bitmaskclient.R.string.malformed_url; import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_cert; @@ -64,6 +62,9 @@ import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_cert; */ public class ProviderApiManager extends ProviderApiManagerBase { + + private static final String TAG = ProviderApiManagerBase.class.getName(); + protected static boolean lastDangerOn = true; @@ -79,83 +80,58 @@ public class ProviderApiManager extends ProviderApiManagerBase { * Downloads a provider.json from a given URL, adding a new provider using the given name. * * @param task containing a boolean meaning if the provider is custom or not, another boolean meaning if the user completely trusts this provider, the provider name and its provider.json url. - * @return a bundle with a boolean value mapped to a key named RESULT_KEY, and which is true if the update was successful. + * @return a bundle with a boolean value mapped to a key named BROADCAST_RESULT_KEY, and which is true if the update was successful. */ @Override - protected Bundle setUpProvider(Bundle task) { - int progress = 0; + protected Bundle setUpProvider(Provider provider, Bundle task) { Bundle currentDownload = new Bundle(); if (task != null) { lastDangerOn = task.containsKey(ProviderListContent.ProviderItem.DANGER_ON) && task.getBoolean(ProviderListContent.ProviderItem.DANGER_ON); - lastProviderMainUrl = task.containsKey(Provider.MAIN_URL) ? - task.getString(Provider.MAIN_URL) : - ""; - - if (isEmpty(lastProviderMainUrl)) { - setErrorResult(currentDownload, malformed_url, null); - return currentDownload; - } - - providerCaCertFingerprint = task.containsKey(Provider.CA_CERT_FINGERPRINT) ? - task.getString(Provider.CA_CERT_FINGERPRINT) : - ""; - providerCaCert = task.containsKey(Provider.CA_CERT) ? - task.getString(Provider.CA_CERT) : - ""; + } - try { - providerDefinition = task.containsKey(Provider.KEY) ? - new JSONObject(task.getString(Provider.KEY)) : - new JSONObject(); - } catch (JSONException e) { - e.printStackTrace(); - providerDefinition = new JSONObject(); - } - providerApiUrl = getApiUrlWithVersion(providerDefinition); + if (isEmpty(provider.getMainUrlString()) || provider.getMainUrl().isDefault()) { + setErrorResult(currentDownload, malformed_url, null); + currentDownload.putParcelable(PROVIDER_KEY, provider); + return currentDownload; + } - checkPersistedProviderUpdates(); - currentDownload = validateProviderDetails(); + getPersistedProviderUpdates(provider); + currentDownload = validateProviderDetails(provider); - //provider details invalid - if (currentDownload.containsKey(ERRORS)) { - return currentDownload; - } - - //no provider certificate available - if (currentDownload.containsKey(RESULT_KEY) && !currentDownload.getBoolean(RESULT_KEY)) { - resetProviderDetails(); - } + //provider details invalid + if (currentDownload.containsKey(ERRORS)) { + currentDownload.putParcelable(PROVIDER_KEY, provider); + return currentDownload; + } - EIP_SERVICE_JSON_DOWNLOADED = false; - go_ahead = true; + //no provider certificate available + if (currentDownload.containsKey(BROADCAST_RESULT_KEY) && !currentDownload.getBoolean(BROADCAST_RESULT_KEY)) { + resetProviderDetails(provider); } - if (!PROVIDER_JSON_DOWNLOADED) - currentDownload = getAndSetProviderJson(lastProviderMainUrl, lastDangerOn, providerCaCert, providerDefinition); - if (PROVIDER_JSON_DOWNLOADED || (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY))) { - broadcastProgress(progress++); - PROVIDER_JSON_DOWNLOADED = true; - - if (!CA_CERT_DOWNLOADED) - currentDownload = downloadCACert(lastDangerOn); - if (CA_CERT_DOWNLOADED || (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY))) { - broadcastProgress(progress++); - CA_CERT_DOWNLOADED = true; - currentDownload = getAndSetEipServiceJson(); - if (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY)) { - broadcastProgress(progress++); - EIP_SERVICE_JSON_DOWNLOADED = true; - } + go_ahead = true; + + if (!provider.hasDefinition()) + currentDownload = getAndSetProviderJson(provider, lastDangerOn); + if (provider.hasDefinition() || (currentDownload.containsKey(BROADCAST_RESULT_KEY) && currentDownload.getBoolean(BROADCAST_RESULT_KEY))) { + if (!provider.hasCaCert()) + currentDownload = downloadCACert(provider, lastDangerOn); + if (provider.hasCaCert() || (currentDownload.containsKey(BROADCAST_RESULT_KEY) && currentDownload.getBoolean(BROADCAST_RESULT_KEY))) { + currentDownload = getAndSetEipServiceJson(provider); } } - + currentDownload.putParcelable(PROVIDER_KEY, provider); return currentDownload; } - private Bundle getAndSetProviderJson(String providerMainUrl, boolean dangerOn, String caCert, JSONObject providerDefinition) { + private Bundle getAndSetProviderJson(Provider provider, boolean dangerOn) { Bundle result = new Bundle(); + JSONObject providerDefinition = provider.getDefinition(); + String caCert = provider.getCaCert(); + String providerMainUrl = provider.getMainUrlString(); + if (go_ahead) { String providerDotJsonString; if(providerDefinition.length() == 0 || caCert.isEmpty()) @@ -165,56 +141,53 @@ public class ProviderApiManager extends ProviderApiManagerBase { if (!isValidJson(providerDotJsonString)) { result.putString(ERRORS, resources.getString(malformed_url)); - result.putBoolean(RESULT_KEY, false); + result.putBoolean(BROADCAST_RESULT_KEY, false); return result; } try { JSONObject providerJson = new JSONObject(providerDotJsonString); - String providerDomain = getDomainFromMainURL(lastProviderMainUrl); - providerApiUrl = getApiUrlWithVersion(providerJson); - //String name = providerJson.getString(Provider.NAME); - //TODO setProviderName(name); - - preferences.edit().putString(Provider.KEY, providerJson.toString()). - putBoolean(PROVIDER_ALLOW_ANONYMOUS, providerJson.getJSONObject(Provider.SERVICE).getBoolean(PROVIDER_ALLOW_ANONYMOUS)). - putBoolean(PROVIDER_ALLOWED_REGISTERED, providerJson.getJSONObject(Provider.SERVICE).getBoolean(PROVIDER_ALLOWED_REGISTERED)). - putString(Provider.KEY + "." + providerDomain, providerJson.toString()).commit(); - result.putBoolean(RESULT_KEY, true); + + provider.define(providerJson); + + result.putBoolean(BROADCAST_RESULT_KEY, true); } catch (JSONException e) { String reason_to_fail = pickErrorMessage(providerDotJsonString); result.putString(ERRORS, reason_to_fail); - result.putBoolean(RESULT_KEY, false); + result.putBoolean(BROADCAST_RESULT_KEY, false); } } + result.putParcelable(PROVIDER_KEY, provider); return result; } /** * Downloads the eip-service.json from a given URL, and saves eip service capabilities including the offered gateways - * @return a bundle with a boolean value mapped to a key named RESULT_KEY, and which is true if the download was successful. + * @return a bundle with a boolean value mapped to a key named BROADCAST_RESULT_KEY, and which is true if the download was successful. */ @Override - protected Bundle getAndSetEipServiceJson() { + protected Bundle getAndSetEipServiceJson(Provider provider) { Bundle result = new Bundle(); - String eip_service_json_string = ""; + String eipServiceJsonString = ""; if (go_ahead) { try { - JSONObject provider_json = new JSONObject(preferences.getString(Provider.KEY, "")); - String eip_service_url = provider_json.getString(Provider.API_URL) + "/" + provider_json.getString(Provider.API_VERSION) + "/" + EIP.SERVICE_API_PATH; - eip_service_json_string = downloadWithProviderCA(eip_service_url, lastDangerOn); - JSONObject eip_service_json = new JSONObject(eip_service_json_string); - eip_service_json.getInt(Provider.API_RETURN_SERIAL); + JSONObject providerDefinition = provider.getDefinition(); + String eipServiceUrl = providerDefinition.getString(Provider.API_URL) + "/" + providerDefinition.getString(Provider.API_VERSION) + "/" + EIP.SERVICE_API_PATH; + eipServiceJsonString = downloadWithProviderCA(provider.getCaCert(), eipServiceUrl, lastDangerOn); + JSONObject eipServiceJson = new JSONObject(eipServiceJsonString); + eipServiceJson.getInt(Provider.API_RETURN_SERIAL); - preferences.edit().putString(PROVIDER_KEY, eip_service_json.toString()).commit(); + //preferences.edit().putString(PROVIDER_KEY, eipServiceJson.toString()).commit(); + provider.setEipServiceJson(eipServiceJson); - result.putBoolean(RESULT_KEY, true); + result.putBoolean(BROADCAST_RESULT_KEY, true); } catch (NullPointerException | JSONException e) { - String reason_to_fail = pickErrorMessage(eip_service_json_string); - result.putString(ERRORS, reason_to_fail); - result.putBoolean(RESULT_KEY, false); + String reasonToFail = pickErrorMessage(eipServiceJsonString); + result.putString(ERRORS, reasonToFail); + result.putBoolean(BROADCAST_RESULT_KEY, false); } } + result.putParcelable(PROVIDER_KEY, provider); return result; } @@ -224,19 +197,19 @@ public class ProviderApiManager extends ProviderApiManagerBase { * @return true if certificate was downloaded correctly, false if provider.json is not present in SharedPreferences, or if the certificate url could not be parsed as a URI, or if there was an SSL error. */ @Override - protected boolean updateVpnCertificate() { + protected boolean updateVpnCertificate(Provider provider) { try { - JSONObject provider_json = new JSONObject(preferences.getString(Provider.KEY, "")); + JSONObject providerDefinition = provider.getDefinition(); - String provider_main_url = provider_json.getString(Provider.API_URL); - URL new_cert_string_url = new URL(provider_main_url + "/" + provider_json.getString(Provider.API_VERSION) + "/" + PROVIDER_VPN_CERTIFICATE); + String providerMainUrl = providerDefinition.getString(Provider.API_URL); + URL newCertStringUrl = new URL(providerMainUrl + "/" + providerDefinition.getString(Provider.API_VERSION) + "/" + PROVIDER_VPN_CERTIFICATE); - String cert_string = downloadWithProviderCA(new_cert_string_url.toString(), lastDangerOn); + String certString = downloadWithProviderCA(provider.getCaCert(), newCertStringUrl.toString(), lastDangerOn); - if (cert_string == null || cert_string.isEmpty() || ConfigHelper.checkErroneousDownload(cert_string)) + if (certString == null || certString.isEmpty() || ConfigHelper.checkErroneousDownload(certString)) return false; else - return loadCertificate(cert_string); + return loadCertificate(certString); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); @@ -249,19 +222,18 @@ public class ProviderApiManager extends ProviderApiManagerBase { } - private Bundle downloadCACert(boolean dangerOn) { + private Bundle downloadCACert(Provider provider, boolean dangerOn) { Bundle result = new Bundle(); try { - JSONObject providerJson = new JSONObject(preferences.getString(Provider.KEY, "")); - String caCertUrl = providerJson.getString(Provider.CA_CERT_URI); - String providerDomain = providerJson.getString(Provider.DOMAIN); + String caCertUrl = provider.getDefinition().getString(Provider.CA_CERT_URI); + String providerDomain = provider.getDomain(); String certString = downloadWithCommercialCA(caCertUrl, dangerOn); - if (validCertificate(certString) && go_ahead) { - preferences.edit().putString(Provider.CA_CERT, certString).commit(); - preferences.edit().putString(Provider.CA_CERT + "." + providerDomain, certString).commit(); - result.putBoolean(RESULT_KEY, true); + if (validCertificate(provider, certString) && go_ahead) { + provider.setCaCert(certString); + preferences.edit().putString(Provider.CA_CERT + "." + providerDomain, certString).apply(); + result.putBoolean(BROADCAST_RESULT_KEY, true); } else { setErrorResult(result, warning_corrupted_provider_cert, ERROR_CERTIFICATE_PINNING.toString()); } @@ -275,13 +247,13 @@ public class ProviderApiManager extends ProviderApiManagerBase { /** * Tries to download the contents of the provided url using commercially validated CA certificate from chosen provider. * <p/> - * If danger_on flag is true, SSL exceptions will be managed by futher methods that will try to use some bypass methods. + * If dangerOn flag is true, SSL exceptions will be managed by futher methods that will try to use some bypass methods. * - * @param string_url - * @param danger_on if the user completely trusts this provider + * @param stringUrl + * @param dangerOn if the user completely trusts this provider * @return */ - private String downloadWithCommercialCA(String string_url, boolean danger_on) { + private String downloadWithCommercialCA(String stringUrl, boolean dangerOn) { String responseString; JSONObject errorJson = new JSONObject(); @@ -292,14 +264,14 @@ public class ProviderApiManager extends ProviderApiManagerBase { List<Pair<String, String>> headerArgs = getAuthorizationHeader(); - responseString = sendGetStringToServer(string_url, headerArgs, okHttpClient); + responseString = sendGetStringToServer(stringUrl, headerArgs, okHttpClient); if (responseString != null && responseString.contains(ERRORS)) { try { // try to download with provider CA on certificate error JSONObject responseErrorJson = new JSONObject(responseString); - if (danger_on && responseErrorJson.getString(ERRORS).equals(resources.getString(R.string.certificate_error))) { - responseString = downloadWithoutCA(string_url); + if (dangerOn && responseErrorJson.getString(ERRORS).equals(resources.getString(R.string.certificate_error))) { + responseString = downloadWithoutCA(stringUrl); } } catch (JSONException e) { e.printStackTrace(); @@ -344,11 +316,11 @@ public class ProviderApiManager extends ProviderApiManagerBase { * @param dangerOn true to download CA certificate in case it has not been downloaded. * @return an empty string if it fails, the url content if not. */ - private String downloadWithProviderCA(String urlString, boolean dangerOn) { + private String downloadWithProviderCA(String caCert, String urlString, boolean dangerOn) { JSONObject initError = new JSONObject(); String responseString; - OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(initError); + OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(caCert, initError); if (okHttpClient == null) { return initError.toString(); } @@ -376,7 +348,7 @@ public class ProviderApiManager extends ProviderApiManagerBase { * Downloads the string that's in the url with any certificate. */ // This method is totally insecure anyways. So no need to refactor that in order to use okHttpClient, force modern TLS etc.. DO NOT USE IN PRODUCTION! - private String downloadWithoutCA(String url_string) { + private String downloadWithoutCA(String urlString) { String string = ""; try { @@ -406,7 +378,7 @@ public class ProviderApiManager extends ProviderApiManagerBase { SSLContext context = SSLContext.getInstance("TLS"); context.init(new KeyManager[0], new TrustManager[]{new DefaultTrustManager()}, new SecureRandom()); - URL url = new URL(url_string); + URL url = new URL(urlString); HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection(); urlConnection.setSSLSocketFactory(context.getSocketFactory()); urlConnection.setHostnameVerifier(hostnameVerifier); diff --git a/app/src/insecure/java/se/leap/bitmaskclient/ProviderDetailActivity.java b/app/src/insecure/java/se/leap/bitmaskclient/ProviderDetailActivity.java index 6977753f..cf9ee5f5 100644 --- a/app/src/insecure/java/se/leap/bitmaskclient/ProviderDetailActivity.java +++ b/app/src/insecure/java/se/leap/bitmaskclient/ProviderDetailActivity.java @@ -2,15 +2,12 @@ package se.leap.bitmaskclient; import android.content.SharedPreferences; -import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; -import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; - public class ProviderDetailActivity extends AbstractProviderDetailActivity { @Override public void onBackPressed() { SharedPreferences.Editor editor = preferences.edit(); - editor.remove(Provider.KEY).remove(ProviderListContent.ProviderItem.DANGER_ON).remove(PROVIDER_ALLOW_ANONYMOUS).remove(PROVIDER_KEY).apply(); + editor.remove(ProviderListContent.ProviderItem.DANGER_ON).apply(); super.onBackPressed(); } diff --git a/app/src/insecure/java/se/leap/bitmaskclient/ProviderListActivity.java b/app/src/insecure/java/se/leap/bitmaskclient/ProviderListActivity.java index 823b5635..554085b1 100644 --- a/app/src/insecure/java/se/leap/bitmaskclient/ProviderListActivity.java +++ b/app/src/insecure/java/se/leap/bitmaskclient/ProviderListActivity.java @@ -16,7 +16,6 @@ */ package se.leap.bitmaskclient; -import android.content.Intent; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.support.v4.app.FragmentTransaction; @@ -26,6 +25,8 @@ import java.net.URL; import se.leap.bitmaskclient.ProviderListContent.ProviderItem; +import static se.leap.bitmaskclient.ProviderAPI.SET_UP_PROVIDER; + /** * Activity that builds and shows the list of known available providers. * <p/> @@ -51,12 +52,12 @@ public class ProviderListActivity extends ProviderListBaseActivity { /** * Open the new provider dialog with data */ - public void addAndSelectNewProvider(String main_url, boolean danger_on) { + public void addAndSelectNewProvider(String mainUrl, boolean danger_on) { FragmentTransaction fragment_transaction = fragmentManager.removePreviousFragment(NewProviderDialog.TAG); DialogFragment newFragment = new NewProviderDialog(); Bundle data = new Bundle(); - data.putString(Provider.MAIN_URL, main_url); + data.putString(Provider.MAIN_URL, mainUrl); data.putBoolean(ProviderItem.DANGER_ON, danger_on); newFragment.setArguments(data); newFragment.show(fragment_transaction, NewProviderDialog.TAG); @@ -87,45 +88,25 @@ public class ProviderListActivity extends ProviderListBaseActivity { */ public void setUpProvider(boolean danger_on) { mConfigState.setAction(SETTING_UP_PROVIDER); - Intent providerAPICommand = new Intent(this, ProviderAPI.class); + Bundle parameters = new Bundle(); - parameters.putString(Provider.MAIN_URL, provider.getMainUrl().toString()); parameters.putBoolean(ProviderItem.DANGER_ON, danger_on); - if (provider.hasCertificatePin()){ - parameters.putString(Provider.CA_CERT_FINGERPRINT, provider.certificatePin()); - } - if (provider.hasCaCert()) { - parameters.putString(Provider.CA_CERT, provider.getCaCert()); - } - if (provider.hasDefinition()) { - parameters.putString(Provider.KEY, provider.getDefinition().toString()); - } - - providerAPICommand.setAction(ProviderAPI.SET_UP_PROVIDER); - providerAPICommand.putExtra(ProviderAPI.PARAMETERS, parameters); - startService(providerAPICommand); + ProviderAPICommand.execute(this, SET_UP_PROVIDER, parameters, provider); } /** * Retrys setup of last used provider, allows bypassing ca certificate validation. */ @Override - public void retrySetUpProvider() { + public void retrySetUpProvider(Provider provider) { cancelSettingUpProvider(); - if (!ProviderAPI.caCertDownloaded()) { - addAndSelectNewProvider(ProviderAPI.lastProviderMainUrl(), ProviderAPI.lastDangerOn()); + if (!provider.hasCaCert()) { + addAndSelectNewProvider(provider.getMainUrlString(), ProviderAPI.lastDangerOn()); } else { showProgressBar(); - Intent providerAPICommand = new Intent(this, ProviderAPI.class); - - providerAPICommand.setAction(ProviderAPI.SET_UP_PROVIDER); - Bundle parameters = new Bundle(); - parameters.putString(Provider.MAIN_URL, provider.getMainUrl().toString()); - providerAPICommand.putExtra(ProviderAPI.PARAMETERS, parameters); - - startService(providerAPICommand); + ProviderAPICommand.execute(this, SET_UP_PROVIDER, provider); } } diff --git a/app/src/main/java/se/leap/bitmaskclient/AbstractProviderDetailActivity.java b/app/src/main/java/se/leap/bitmaskclient/AbstractProviderDetailActivity.java index fbb27b58..9826b89b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/AbstractProviderDetailActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/AbstractProviderDetailActivity.java @@ -1,7 +1,6 @@ package se.leap.bitmaskclient; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.widget.AppCompatTextView; @@ -12,14 +11,10 @@ import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.TextView; -import org.json.JSONException; -import org.json.JSONObject; - import java.util.ArrayList; import butterknife.InjectView; -import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_CONFIGURE_LEAP; @@ -36,56 +31,58 @@ public abstract class AbstractProviderDetailActivity extends ConfigWizardBaseAct @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + provider = getIntent().getParcelableExtra(PROVIDER_KEY); setContentView(R.layout.a_provider_detail); - try { - JSONObject providerJson = new JSONObject(preferences.getString(Provider.KEY, "")); - setProviderHeaderText(ConfigHelper.getProviderName(preferences)); - description.setText(ConfigHelper.getDescription(preferences)); + if (provider == null) { + return; + } - // Show only the options allowed by the provider - ArrayList<String> optionsList = new ArrayList<>(); - if (registrationAllowed(providerJson)) { - optionsList.add(getString(R.string.login_to_profile)); - optionsList.add(getString(R.string.create_profile)); - } - if (anonAllowed(providerJson)) { - optionsList.add(getString(R.string.use_anonymously_button)); - } - options.setAdapter(new ArrayAdapter<>( - this, - android.R.layout.simple_list_item_activated_1, - android.R.id.text1, - optionsList.toArray(new String[optionsList.size()]) - )); - options.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - String text = ((TextView) view).getText().toString(); - Intent intent; - if (text.equals(getString(R.string.login_to_profile))) { - Log.d(TAG, "login selected"); - intent = new Intent(getApplicationContext(), LoginActivity.class); - } else if (text.equals(getString(R.string.create_profile))) { - Log.d(TAG, "signup selected"); - intent = new Intent(getApplicationContext(), SignupActivity.class); - } else { - Log.d(TAG, "use anonymously selected"); - intent = new Intent(); - intent.putExtra(Provider.KEY, provider); - setResult(RESULT_OK, intent); - finish(); - return; - } - intent.putExtra(PROVIDER_KEY, provider); - intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); - startActivityForResult(intent, REQUEST_CODE_CONFIGURE_LEAP); - } - }); - } catch (JSONException e) { - // TODO show error and return + setProviderHeaderText(provider.getName()); + description.setText(provider.getDescription()); + + // Show only the options allowed by the provider + ArrayList<String> optionsList = new ArrayList<>(); + if (provider.allowsRegistered()) { + optionsList.add(getString(R.string.login_to_profile)); + optionsList.add(getString(R.string.create_profile)); + } + if (provider.allowsAnonymous()) { + optionsList.add(getString(R.string.use_anonymously_button)); } + + options.setAdapter(new ArrayAdapter<>( + this, + android.R.layout.simple_list_item_activated_1, + android.R.id.text1, + optionsList.toArray(new String[optionsList.size()]) + )); + options.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + String text = ((TextView) view).getText().toString(); + Intent intent; + if (text.equals(getString(R.string.login_to_profile))) { + Log.d(TAG, "login selected"); + intent = new Intent(getApplicationContext(), LoginActivity.class); + } else if (text.equals(getString(R.string.create_profile))) { + Log.d(TAG, "signup selected"); + intent = new Intent(getApplicationContext(), SignupActivity.class); + } else { + Log.d(TAG, "use anonymously selected"); + intent = new Intent(); + intent.putExtra(Provider.KEY, provider); + setResult(RESULT_OK, intent); + finish(); + return; + } + intent.putExtra(PROVIDER_KEY, provider); + intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); + startActivityForResult(intent, REQUEST_CODE_CONFIGURE_LEAP); + } + }); } @Override @@ -104,29 +101,4 @@ public abstract class AbstractProviderDetailActivity extends ConfigWizardBaseAct } } - private boolean anonAllowed(JSONObject providerJson) { - try { - JSONObject serviceDescription = providerJson.getJSONObject(Provider.SERVICE); - return serviceDescription.has(PROVIDER_ALLOW_ANONYMOUS) && serviceDescription.getBoolean(PROVIDER_ALLOW_ANONYMOUS); - } catch (JSONException e) { - return false; - } - } - - private boolean registrationAllowed(JSONObject providerJson) { - try { - JSONObject serviceDescription = providerJson.getJSONObject(Provider.SERVICE); - return serviceDescription.has(Provider.ALLOW_REGISTRATION) && serviceDescription.getBoolean(Provider.ALLOW_REGISTRATION); - } catch (JSONException e) { - return false; - } - } - - @Override - public void onBackPressed() { - SharedPreferences.Editor editor = preferences.edit(); - editor.remove(Provider.KEY).remove(PROVIDER_ALLOW_ANONYMOUS).remove(PROVIDER_KEY).apply(); - super.onBackPressed(); - } - } diff --git a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java index 741a6f56..5a97624d 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java @@ -21,7 +21,6 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; -import org.jetbrains.annotations.NotNull; import org.json.JSONException; import org.json.JSONObject; import org.spongycastle.util.encoders.Base64; @@ -47,10 +46,15 @@ import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; +import java.util.Map; import static android.R.attr.name; +import static se.leap.bitmaskclient.Constants.PREFERENCES_APP_VERSION; import static se.leap.bitmaskclient.Constants.PROVIDER_CONFIGURED; +import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; /** * Stores constants, and implements auxiliary methods used across all Bitmask Android classes. @@ -98,14 +102,14 @@ public class ConfigHelper { return ret; } - public static X509Certificate parseX509CertificateFromString(String certificate_string) { + public static X509Certificate parseX509CertificateFromString(String certificateString) { java.security.cert.Certificate certificate = null; CertificateFactory cf; try { cf = CertificateFactory.getInstance("X.509"); - certificate_string = certificate_string.replaceFirst("-----BEGIN CERTIFICATE-----", "").replaceFirst("-----END CERTIFICATE-----", "").trim(); - byte[] cert_bytes = Base64.decode(certificate_string); + certificateString = certificateString.replaceFirst("-----BEGIN CERTIFICATE-----", "").replaceFirst("-----END CERTIFICATE-----", "").trim(); + byte[] cert_bytes = Base64.decode(certificateString); InputStream caInput = new ByteArrayInputStream(cert_bytes); try { certificate = cf.generateCertificate(caInput); @@ -271,9 +275,9 @@ public class ConfigHelper { public static Provider getSavedProviderFromSharedPreferences(@NonNull SharedPreferences preferences) { Provider provider = new Provider(); try { - provider.setUrl(new URL(preferences.getString(Provider.MAIN_URL, ""))); + provider.setMainUrl(new URL(preferences.getString(Provider.MAIN_URL, ""))); provider.define(new JSONObject(preferences.getString(Provider.KEY, ""))); - provider.setCACert(preferences.getString(Provider.CA_CERT, "")); + provider.setCaCert(preferences.getString(Provider.CA_CERT, "")); } catch (MalformedURLException | JSONException e) { e.printStackTrace(); } @@ -349,6 +353,40 @@ public class ConfigHelper { putString(Provider.MAIN_URL, provider.getMainUrlString()). putString(Provider.KEY, provider.getDefinitionString()). putString(Provider.CA_CERT, provider.getCaCert()). - apply(); + putString(PROVIDER_KEY, provider.getEipServiceJsonString()). + commit(); } + + + public static void clearDataOfLastProvider(SharedPreferences preferences) { + clearDataOfLastProvider(preferences, false); + } + + public static void clearDataOfLastProvider(SharedPreferences preferences, boolean commit) { + Map<String, ?> allEntries = preferences.getAll(); + List<String> lastProvidersKeys = new ArrayList<>(); + for (Map.Entry<String, ?> entry : allEntries.entrySet()) { + //sort out all preferences that don't belong to the last provider + if (entry.getKey().startsWith(Provider.KEY + ".") || + entry.getKey().startsWith(Provider.CA_CERT + ".") || + entry.getKey().startsWith(Provider.CA_CERT_FINGERPRINT + "." )|| + entry.getKey().equals(PREFERENCES_APP_VERSION) + ) { + continue; + } + lastProvidersKeys.add(entry.getKey()); + } + + SharedPreferences.Editor preferenceEditor = preferences.edit(); + for (String key : lastProvidersKeys) { + preferenceEditor.remove(key); + } + if (commit) { + preferenceEditor.commit(); + } else { + preferenceEditor.apply(); + } + } + + } diff --git a/app/src/main/java/se/leap/bitmaskclient/ConfigWizardBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/ConfigWizardBaseActivity.java index 79a78fe1..7fcb5816 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ConfigWizardBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/ConfigWizardBaseActivity.java @@ -56,19 +56,22 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity { @Override public void setContentView(View view) { super.setContentView(view); - setProviderHeaderText(ConfigHelper.getProviderName(preferences)); + if (provider != null) + setProviderHeaderText(provider.getName()); } @Override public void setContentView(int layoutResID) { super.setContentView(layoutResID); - setProviderHeaderText(ConfigHelper.getProviderName(preferences)); + if (provider != null) + setProviderHeaderText(provider.getName()); } @Override public void setContentView(View view, ViewGroup.LayoutParams params) { super.setContentView(view, params); - setProviderHeaderText(ConfigHelper.getProviderName(preferences)); + if (provider != null) + setProviderHeaderText(provider.getName()); } protected void setProviderHeaderLogo(@DrawableRes int providerHeaderLogo) { diff --git a/app/src/main/java/se/leap/bitmaskclient/Constants.java b/app/src/main/java/se/leap/bitmaskclient/Constants.java index 7ee3adab..23c5e805 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Constants.java +++ b/app/src/main/java/se/leap/bitmaskclient/Constants.java @@ -17,6 +17,7 @@ public interface Constants { String REQUEST_CODE_KEY = "request_code"; int REQUEST_CODE_CONFIGURE_LEAP = 0; int REQUEST_CODE_SWITCH_PROVIDER = 1; + int REQUEST_CODE_LOG_IN = 2; ////////////////////////////////////////////// @@ -50,10 +51,34 @@ public interface Constants { ////////////////////////////////////////////// // PROVIDER CONSTANTS ///////////////////////////////////////////// + String PROVIDER_ALLOW_ANONYMOUS = "allow_anonymous"; String PROVIDER_ALLOWED_REGISTERED = "allow_registration"; String PROVIDER_VPN_CERTIFICATE = "cert"; String PROVIDER_PRIVATE_KEY = "Constants.PROVIDER_PRIVATE_KEY"; String PROVIDER_KEY = "Constants.PROVIDER_KEY"; String PROVIDER_CONFIGURED = "Constants.PROVIDER_CONFIGURED"; + + ////////////////////////////////////////////// + // CREDENTIAL CONSTANTS + ///////////////////////////////////////////// + + String CREDENTIALS_USERNAME = "username"; + String CREDENTIALS_PASSWORD = "password"; + + enum CREDENTIAL_ERRORS { + USERNAME_MISSING, + PASSWORD_INVALID_LENGTH, + RISEUP_WARNING + } + + ////////////////////////////////////////////// + // BROADCAST CONSTANTS + ///////////////////////////////////////////// + + String BROADCAST_EIP_EVENT = "BROADCAST.EIP_EVENT"; + String BROADCAST_PROVIDER_API_EVENT = "BROADCAST.PROVIDER_API_EVENT"; + String BROADCAST_RESULT_CODE = "BROADCAST.RESULT_CODE"; + String BROADCAST_RESULT_KEY = "BROADCAST.RESULT_KEY"; + } diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java index a4db5f84..2f129d02 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java +++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java @@ -23,8 +23,6 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Bundle; -import android.os.Handler; -import android.support.v4.app.FragmentTransaction; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -36,14 +34,11 @@ import org.json.JSONObject; import java.net.MalformedURLException; import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; import butterknife.InjectView; import de.blinkt.openvpn.core.VpnStatus; +import se.leap.bitmaskclient.eip.EipCommand; import se.leap.bitmaskclient.fragments.AboutFragment; -import se.leap.bitmaskclient.userstatus.SessionDialog; import se.leap.bitmaskclient.userstatus.User; import se.leap.bitmaskclient.userstatus.UserStatusFragment; @@ -51,8 +46,6 @@ import static se.leap.bitmaskclient.Constants.APP_ACTION_CONFIGURE_ALWAYS_ON_PRO import static se.leap.bitmaskclient.Constants.APP_ACTION_QUIT; import static se.leap.bitmaskclient.Constants.EIP_IS_ALWAYS_ON; 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_ALLOW_ANONYMOUS; import static se.leap.bitmaskclient.Constants.PROVIDER_CONFIGURED; import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_CONFIGURE_LEAP; @@ -91,20 +84,15 @@ public class Dashboard extends ButterKnifeActivity { private UserStatusFragment user_status_fragment; private static Provider provider = new Provider(); - public static ProviderAPIResultReceiver providerAPI_result_receiver; - private static boolean switching_provider; private boolean handledVersion; - public static DashboardReceiver dashboardReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - dashboardReceiver = new DashboardReceiver(this); preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); fragment_manager = new FragmentManagerEnhanced(getSupportFragmentManager()); - providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler(), dashboardReceiver); if (!handledVersion) { handleVersion(); @@ -112,7 +100,6 @@ public class Dashboard extends ButterKnifeActivity { } // initialize app necessities - ProviderAPICommand.initialize(this); VpnStatus.initLogCache(getApplicationContext().getCacheDir()); User.init(getString(R.string.default_username)); @@ -157,9 +144,9 @@ public class Dashboard extends ButterKnifeActivity { private Provider getSavedProviderFromSharedPreferences() { Provider provider = new Provider(); try { - provider.setUrl(new URL(preferences.getString(Provider.MAIN_URL, ""))); + provider.setMainUrl(new URL(preferences.getString(Provider.MAIN_URL, ""))); provider.define(new JSONObject(preferences.getString(Provider.KEY, ""))); - provider.setCACert(preferences.getString(Provider.CA_CERT, "")); + provider.setCaCert(preferences.getString(Provider.CA_CERT, "")); } catch (MalformedURLException | JSONException e) { e.printStackTrace(); } @@ -175,7 +162,7 @@ public class Dashboard extends ButterKnifeActivity { case 91: // 0.6.0 without Bug #5999 case 101: // 0.8.0 if (!preferences.getString(PROVIDER_KEY, "").isEmpty()) - eip_fragment.updateEipService(); + EipCommand.updateEipService(this); break; } } catch (NameNotFoundException e) { @@ -211,9 +198,9 @@ public class Dashboard extends ButterKnifeActivity { buildDashboard(false); invalidateOptionsMenuOnUiThread(); - if (data.hasExtra(SessionDialog.TAG)) { - sessionDialog(Bundle.EMPTY); - } + //if (data.hasExtra(SessionDialog.TAG)) { + // sessionDialog(Bundle.EMPTY); + //} } else if (resultCode == RESULT_CANCELED && data != null && data.hasExtra(APP_ACTION_QUIT)) { finish(); @@ -304,14 +291,14 @@ public class Dashboard extends ButterKnifeActivity { user_status_fragment.setArguments(bundle); fragment_manager.replace(R.id.user_status_fragment, user_status_fragment, UserStatusFragment.TAG); - if (provider.hasEIP()) { - fragment_manager.removePreviousFragment(EipFragment.TAG); - eip_fragment = prepareEipFragment(hideAndTurnOnEipOnBoot); - fragment_manager.replace(R.id.servicesCollection, eip_fragment, EipFragment.TAG); - if (hideAndTurnOnEipOnBoot) { - onBackPressed(); - } - } +// if (provider.hasEIP()) { +// fragment_manager.removePreviousFragment(EipFragment.TAG); +// eip_fragment = prepareEipFragment(hideAndTurnOnEipOnBoot); +// fragment_manager.replace(R.id.servicesCollection, eip_fragment, EipFragment.TAG); +// if (hideAndTurnOnEipOnBoot) { +// onBackPressed(); +// } +// } } /** @@ -367,12 +354,11 @@ public class Dashboard extends ButterKnifeActivity { showLog(); return true; case R.id.switch_provider: - switching_provider = true; if (User.loggedIn()) user_status_fragment.logOut(); else switchProvider(); return true; case R.id.signup_button: - sessionDialog(Bundle.EMPTY); + //sessionDialog(Bundle.EMPTY); return true; default: return super.onOptionsItemSelected(item); @@ -389,99 +375,14 @@ public class Dashboard extends ButterKnifeActivity { log_window_wrapper.showLog(); } - - // TODO MOVE TO VPNManager(?) - public static void downloadVpnCertificate() { - boolean is_authenticated = User.loggedIn(); - boolean allowed_anon = preferences.getBoolean(PROVIDER_ALLOW_ANONYMOUS, false); - if (allowed_anon || is_authenticated) - ProviderAPICommand.execute(Bundle.EMPTY, ProviderAPI.DOWNLOAD_CERTIFICATE, providerAPI_result_receiver); - else - sessionDialog(Bundle.EMPTY); - } - - // TODO how can we replace this - public static void sessionDialog(Bundle resultData) { - try { - FragmentTransaction transaction = fragment_manager.removePreviousFragment(SessionDialog.TAG); - SessionDialog.getInstance(provider, resultData).show(transaction, SessionDialog.TAG); - } catch (IllegalStateException e) { - e.printStackTrace(); - } - } - private void switchProvider() { - if (provider.hasEIP()) eip_fragment.stopEipIfPossible(); - - clearDataOfLastProvider(); - - switching_provider = false; - startActivityForResult(new Intent(this, ProviderListActivity.class), REQUEST_CODE_SWITCH_PROVIDER); - } +// if (provider.hasEIP()) eip_fragment.stopEipIfPossible(); - private void clearDataOfLastProvider() { - Map<String, ?> allEntries = preferences.getAll(); - List<String> lastProvidersKeys = new ArrayList<>(); - for (Map.Entry<String, ?> entry : allEntries.entrySet()) { - //sort out all preferences that don't belong to the last provider - if (entry.getKey().startsWith(Provider.KEY + ".") || - entry.getKey().startsWith(Provider.CA_CERT + ".") || - entry.getKey().startsWith(Provider.CA_CERT_FINGERPRINT + "." )|| - entry.getKey().equals(PREFERENCES_APP_VERSION) - ) { - continue; - } - lastProvidersKeys.add(entry.getKey()); - } + ConfigHelper.clearDataOfLastProvider(preferences); - SharedPreferences.Editor preferenceEditor = preferences.edit(); - for (String key : lastProvidersKeys) { - preferenceEditor.remove(key); - } - preferenceEditor.apply(); - - switching_provider = false; startActivityForResult(new Intent(this, ProviderListActivity.class), REQUEST_CODE_SWITCH_PROVIDER); } - private static class DashboardReceiver implements ProviderAPIResultReceiver.Receiver{ - - private Dashboard dashboard; - - DashboardReceiver(Dashboard dashboard) { - this.dashboard = dashboard; - } - - @Override - public void onReceiveResult(int resultCode, Bundle resultData) { - if (resultCode == ProviderAPI.SUCCESSFUL_SIGNUP) { - String username = resultData.getString(SessionDialog.USERNAME); - String password = resultData.getString(SessionDialog.PASSWORD); - dashboard.user_status_fragment.logIn(username, password); - } else if (resultCode == ProviderAPI.FAILED_SIGNUP) { - MainActivity.sessionDialog(resultData); - } else if (resultCode == ProviderAPI.SUCCESSFUL_LOGIN) { - Dashboard.downloadVpnCertificate(); - } else if (resultCode == ProviderAPI.FAILED_LOGIN) { - MainActivity.sessionDialog(resultData); - } else if (resultCode == ProviderAPI.SUCCESSFUL_LOGOUT) { - if (switching_provider) dashboard.switchProvider(); - } else if (resultCode == ProviderAPI.LOGOUT_FAILED) { - dashboard.setResult(RESULT_CANCELED); - } else if (resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE) { - dashboard.eip_fragment.updateEipService(); - dashboard.setResult(RESULT_OK); - } else if (resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE) { - dashboard.setResult(RESULT_CANCELED); - } else if (resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE) { - dashboard.eip_fragment.updateEipService(); - dashboard.setResult(RESULT_OK); - } else if (resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE) { - dashboard.setResult(RESULT_CANCELED); - } - } - } - public static Provider getProvider() { return provider; } @Override diff --git a/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java b/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java index dde71642..1a0c85ad 100644 --- a/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java @@ -89,7 +89,7 @@ public class DownloadFailedDialog extends DialogFragment { dialog.dismiss(); } }); - switch (downloadError) { +switch (downloadError) { case ERROR_CORRUPTED_PROVIDER_JSON: builder.setPositiveButton(R.string.update_provider_details, new DialogInterface.OnClickListener() { @Override @@ -113,7 +113,7 @@ public class DownloadFailedDialog extends DialogFragment { builder.setPositiveButton(R.string.retry, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dismiss(); - interface_with_ConfigurationWizard.retrySetUpProvider(); + interface_with_ConfigurationWizard.retrySetUpProvider(null); } }); break; @@ -124,7 +124,7 @@ public class DownloadFailedDialog extends DialogFragment { } public interface DownloadFailedDialogInterface { - void retrySetUpProvider(); + void retrySetUpProvider(Provider provider); void cancelSettingUpProvider(); diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java index 4027bc14..a86afd78 100644 --- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java @@ -18,10 +18,12 @@ package se.leap.bitmaskclient; import android.app.Activity; import android.app.AlertDialog; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.graphics.ColorMatrix; @@ -50,26 +52,33 @@ import de.blinkt.openvpn.core.IOpenVPNServiceInternal; import de.blinkt.openvpn.core.OpenVPNService; import de.blinkt.openvpn.core.ProfileManager; import de.blinkt.openvpn.core.VpnStatus; -import se.leap.bitmaskclient.eip.EIP; +import se.leap.bitmaskclient.eip.EipCommand; import se.leap.bitmaskclient.eip.EipStatus; import se.leap.bitmaskclient.eip.VoidVpnService; +import se.leap.bitmaskclient.userstatus.User; +import static android.app.Activity.RESULT_OK; import static android.view.View.GONE; import static android.view.View.VISIBLE; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_NONETWORK; +import static se.leap.bitmaskclient.Constants.BROADCAST_EIP_EVENT; +import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; import static se.leap.bitmaskclient.Constants.EIP_ACTION_CHECK_CERT_VALIDITY; import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP; import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP_BLOCKING_VPN; import static se.leap.bitmaskclient.Constants.EIP_ACTION_UPDATE; import static se.leap.bitmaskclient.Constants.EIP_NOTIFICATION; -import static se.leap.bitmaskclient.Constants.EIP_RECEIVER; import static se.leap.bitmaskclient.Constants.EIP_REQUEST; import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT; import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOWED_REGISTERED; import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; +import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; +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_CERTIFICATE; public class EipFragment extends Fragment implements Observer { @@ -79,6 +88,7 @@ public class EipFragment extends Fragment implements Observer { public static final String START_EIP_ON_BOOT = "start on boot"; private SharedPreferences preferences; + private Provider provider; @InjectView(R.id.background) AppCompatImageView background; @@ -102,6 +112,9 @@ public class EipFragment extends Fragment implements Observer { private EipStatus eipStatus; private boolean wantsToConnect; + private ProviderAPIResultReceiver providerAPIResultReceiver; + private EIPBroadcastReceiver eipBroadcastReceiver; + private IOpenVPNServiceInternal mService; private ServiceConnection openVpnConnection = new ServiceConnection() { @@ -123,7 +136,18 @@ public class EipFragment extends Fragment implements Observer { public void onAttach(Context context) { super.onAttach(context); - downloadEIPServiceConfig(); + Bundle arguments = getArguments(); + if (arguments != null) { + provider = getArguments().getParcelable(PROVIDER_KEY); + if (provider == null) { + getActivity().startActivityForResult(new Intent(getActivity(), ProviderListActivity.class), REQUEST_CODE_SWITCH_PROVIDER); + } else { + Log.d(TAG, provider.getName() + " configured as provider"); + } + } else { + Log.e(TAG, "no provider given - starting ProviderListActivity"); + getActivity().startActivityForResult(new Intent(getActivity(), ProviderListActivity.class), REQUEST_CODE_SWITCH_PROVIDER); + } } @Override @@ -131,6 +155,8 @@ public class EipFragment extends Fragment implements Observer { super.onCreate(savedInstanceState); eipStatus = EipStatus.getInstance(); eipReceiver = new EIPReceiver(new Handler()); + eipBroadcastReceiver = new EIPBroadcastReceiver(); + providerAPIResultReceiver = new ProviderAPIResultReceiver(new Handler(), new EipFragmentReceiver()); preferences = getActivity().getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE); } @@ -152,6 +178,7 @@ public class EipFragment extends Fragment implements Observer { super.onResume(); //FIXME: avoid race conditions while checking certificate an logging in at about the same time //eipCommand(Constants.EIP_ACTION_CHECK_CERT_VALIDITY); + setUpBroadcastReceiver(); handleNewState(); bindOpenVpnService(); } @@ -160,6 +187,8 @@ public class EipFragment extends Fragment implements Observer { public void onPause() { super.onPause(); getActivity().unbindService(openVpnConnection); + getActivity().unregisterReceiver(eipBroadcastReceiver); + Log.d(TAG, "broadcast unregistered"); } @Override @@ -205,8 +234,8 @@ public class EipFragment extends Fragment implements Observer { startEipFromScratch(); else if (canLogInToStartEIP()) { wantsToConnect = true; - Bundle bundle = new Bundle(); - MainActivity.sessionDialog(bundle); + Intent intent = new Intent(getContext(), LoginActivity.class); + getActivity().startActivityForResult(intent, REQUEST_CODE_LOG_IN); } else { Log.d(TAG, "WHAT IS GOING ON HERE?!"); // TODO: implement a fallback: check if vpncertificate was not downloaded properly or give @@ -256,7 +285,7 @@ public class EipFragment extends Fragment implements Observer { public void startEipFromScratch() { wantsToConnect = false; saveStatus(true); - eipCommand(EIP_ACTION_START); + EipCommand.startVPN(getContext(), eipReceiver); } private void stop() { @@ -288,13 +317,7 @@ public class EipFragment extends Fragment implements Observer { protected void stopEipIfPossible() { //FIXME: no need to start a service here! - eipCommand(EIP_ACTION_STOP); - } - - private void downloadEIPServiceConfig() { - ProviderAPIResultReceiver provider_api_receiver = new ProviderAPIResultReceiver(new Handler(), Dashboard.dashboardReceiver); - if(eipReceiver != null) - ProviderAPICommand.execute(Bundle.EMPTY, ProviderAPI.DOWNLOAD_EIP_SERVICE, provider_api_receiver); + EipCommand.stopVPN(getContext(), eipReceiver); } protected void askToStopEIP() { @@ -316,25 +339,6 @@ public class EipFragment extends Fragment implements Observer { .show(); } - protected void updateEipService() { - eipCommand(EIP_ACTION_UPDATE); - } - - /** - * Send a command to EIP - * - * @param action A valid String constant from EIP class representing an Intent - * filter for the EIP class - */ - private void eipCommand(String action) { - Activity activity = getActivity(); - // TODO validate "action"...how do we get the list of intent-filters for a class via Android API? - Intent vpn_intent = new Intent(activity.getApplicationContext(), EIP.class); - vpn_intent.setAction(action); - vpn_intent.putExtra(EIP_RECEIVER, eipReceiver); - activity.startService(vpn_intent); - } - @Override public void update(Observable observable, Object data) { if (observable instanceof EipStatus) { @@ -407,58 +411,81 @@ public class EipFragment extends Fragment implements Observer { protected void onReceiveResult(int resultCode, Bundle resultData) { super.onReceiveResult(resultCode, resultData); - String request = resultData.getString(EIP_REQUEST); + handleEIPEvent(resultCode, resultData); + } + } + + private class EIPBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + Log.d(TAG, "received Broadcast"); - if (request == null) { + String action = intent.getAction(); + if (action == null || !action.equalsIgnoreCase(BROADCAST_EIP_EVENT)) { return; } - switch (request) { - case EIP_ACTION_START: - switch (resultCode) { - case Activity.RESULT_OK: - break; - case Activity.RESULT_CANCELED: - break; - } - break; - case EIP_ACTION_STOP: - switch (resultCode) { - case Activity.RESULT_OK: - stop(); - break; - case Activity.RESULT_CANCELED: - break; - } - break; - case EIP_NOTIFICATION: - switch (resultCode) { - case Activity.RESULT_OK: - break; - case Activity.RESULT_CANCELED: - break; - } - break; - case EIP_ACTION_CHECK_CERT_VALIDITY: - switch (resultCode) { - case Activity.RESULT_OK: - break; - case Activity.RESULT_CANCELED: - Dashboard.downloadVpnCertificate(); - break; - } - break; - case EIP_ACTION_UPDATE: - switch (resultCode) { - case Activity.RESULT_OK: - if (wantsToConnect) - startEipFromScratch(); - break; - case Activity.RESULT_CANCELED: - handleNewState(); - break; - } - } + int resultCode = intent.getIntExtra(BROADCAST_RESULT_KEY, -1); + Bundle resultData = intent.getParcelableExtra(BROADCAST_RESULT_KEY); + Log.d(TAG, "Broadcast resultCode: " + Integer.toString(resultCode)); + + handleEIPEvent(resultCode, resultData); + + } + } + + private void handleEIPEvent(int resultCode, Bundle resultData) { + String request = resultData.getString(EIP_REQUEST); + + if (request == null) { + return; + } + + switch (request) { + case EIP_ACTION_START: + switch (resultCode) { + case RESULT_OK: + break; + case Activity.RESULT_CANCELED: + break; + } + break; + case EIP_ACTION_STOP: + switch (resultCode) { + case RESULT_OK: + stop(); + break; + case Activity.RESULT_CANCELED: + break; + } + break; + case EIP_NOTIFICATION: + switch (resultCode) { + case RESULT_OK: + break; + case Activity.RESULT_CANCELED: + break; + } + break; + case EIP_ACTION_CHECK_CERT_VALIDITY: + switch (resultCode) { + case RESULT_OK: + break; + case Activity.RESULT_CANCELED: + downloadVpnCertificate(); + break; + } + break; + case EIP_ACTION_UPDATE: + switch (resultCode) { + case RESULT_OK: + if (wantsToConnect) + startEipFromScratch(); + break; + case Activity.RESULT_CANCELED: + handleNewState(); + break; + } } } @@ -480,4 +507,33 @@ public class EipFragment extends Fragment implements Observer { background.setImageAlpha(255); } + private class EipFragmentReceiver implements ProviderAPIResultReceiver.Receiver{ + + @Override + public void onReceiveResult(int resultCode, Bundle resultData) { + if (resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE) { + provider = resultData.getParcelable(PROVIDER_KEY); + EipCommand.updateEipService(getContext(), eipReceiver); + } else if (resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE) { + //dashboard.setResult(RESULT_CANCELED); + // TODO CATCH ME IF YOU CAN + } + } + } + + private void downloadVpnCertificate() { + boolean is_authenticated = User.loggedIn(); + boolean allowed_anon = preferences.getBoolean(PROVIDER_ALLOW_ANONYMOUS, false); + if (allowed_anon || is_authenticated) { + ProviderAPICommand.execute(getContext(), DOWNLOAD_CERTIFICATE, provider, providerAPIResultReceiver); + } + } + + private void setUpBroadcastReceiver() { + IntentFilter updateIntentFilter = new IntentFilter(BROADCAST_EIP_EVENT); + updateIntentFilter.addCategory(Intent.CATEGORY_DEFAULT); + getActivity().registerReceiver(eipBroadcastReceiver, updateIntentFilter); + Log.d(TAG, "broadcast registered"); + } + } diff --git a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java index 7629f0b7..d3865559 100644 --- a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java @@ -5,15 +5,17 @@ import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentTransaction; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import se.leap.bitmaskclient.drawer.NavigationDrawerFragment; -import se.leap.bitmaskclient.userstatus.SessionDialog; +import se.leap.bitmaskclient.eip.EipCommand; +import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_CONFIGURE_LEAP; +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; @@ -23,6 +25,8 @@ public class MainActivity extends AppCompatActivity { private static FragmentManagerEnhanced fragmentManager; private SharedPreferences preferences; + private NavigationDrawerFragment navigationDrawerFragment; + public final static String ACTION_SHOW_VPN_FRAGMENT = "action_show_vpn_fragment"; /** @@ -33,13 +37,14 @@ public class MainActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); - NavigationDrawerFragment navigationDrawerFragment = (NavigationDrawerFragment) + navigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_drawer); preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + provider = ConfigHelper.getSavedProviderFromSharedPreferences(preferences); + fragmentManager = new FragmentManagerEnhanced(getSupportFragmentManager()); // Set up the drawer. navigationDrawerFragment.setUp( @@ -49,15 +54,6 @@ public class MainActivity extends AppCompatActivity { handleIntentAction(getIntent()); } - public static void sessionDialog(Bundle resultData) { - try { - FragmentTransaction transaction = fragmentManager.removePreviousFragment(SessionDialog.TAG); - SessionDialog.getInstance(provider, resultData).show(transaction, SessionDialog.TAG); - } catch (IllegalStateException e) { - e.printStackTrace(); - } - } - @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); @@ -70,35 +66,54 @@ public class MainActivity extends AppCompatActivity { return; } - Fragment fragment = null; - switch (intent.getAction()) { case ACTION_SHOW_VPN_FRAGMENT: - fragment = new EipFragment(); + showEipFragment(); break; default: break; } - - if (fragment != null) { - fragmentManager.beginTransaction() - .replace(R.id.container, fragment) - .commit(); - } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); if (data == null) { return; } - if (requestCode == REQUEST_CODE_CONFIGURE_LEAP) { - if (resultCode == RESULT_OK && data.hasExtra(Provider.KEY)) { - Provider provider = data.getParcelableExtra(Provider.KEY); - ConfigHelper.storeProviderInPreferences(preferences, provider); + if (resultCode == RESULT_OK && data.hasExtra(Provider.KEY)) { + provider = data.getParcelableExtra(Provider.KEY); + + if (provider == null) { + return; } + + ConfigHelper.storeProviderInPreferences(preferences, provider); + navigationDrawerFragment.refresh(); + switch (requestCode) { + case REQUEST_CODE_SWITCH_PROVIDER: + EipCommand.stopVPN(this); + break; + case REQUEST_CODE_CONFIGURE_LEAP: + break; + case REQUEST_CODE_LOG_IN: + EipCommand.startVPN(this); + break; + + } + showEipFragment(); } } + private void showEipFragment() { + Fragment fragment = new EipFragment(); + Bundle arguments = new Bundle(); + arguments.putParcelable(PROVIDER_KEY, provider); + fragment.setArguments(arguments); + fragmentManager.beginTransaction() + .replace(R.id.container, fragment) + .commit(); + } + } diff --git a/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java b/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java index 1bf679f8..6d554b0e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java +++ b/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java @@ -59,11 +59,9 @@ import static se.leap.bitmaskclient.R.string.server_unreachable_message; public class OkHttpClientGenerator { - SharedPreferences preferences; Resources resources; public OkHttpClientGenerator(SharedPreferences preferences, Resources resources) { - this.preferences = preferences; this.resources = resources; } @@ -71,9 +69,8 @@ public class OkHttpClientGenerator { return initHttpClient(initError, null); } - public OkHttpClient initSelfSignedCAHttpClient(JSONObject initError) { - String certificate = preferences.getString(Provider.CA_CERT, ""); - return initHttpClient(initError, certificate); + public OkHttpClient initSelfSignedCAHttpClient(String caCert, JSONObject initError) { + return initHttpClient(initError, caCert); } public OkHttpClient initSelfSignedCAHttpClient(JSONObject initError, String certificate) { diff --git a/app/src/main/java/se/leap/bitmaskclient/Provider.java b/app/src/main/java/se/leap/bitmaskclient/Provider.java index 5ff1949c..9595b147 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Provider.java +++ b/app/src/main/java/se/leap/bitmaskclient/Provider.java @@ -16,18 +16,22 @@ */ package se.leap.bitmaskclient; -import android.content.SharedPreferences; -import android.os.*; +import android.os.Parcel; +import android.os.Parcelable; import com.google.gson.Gson; -import org.json.*; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; -import java.io.Serializable; -import java.net.*; -import java.util.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Arrays; +import java.util.Locale; -import static se.leap.bitmaskclient.Constants.PROVIDER_CONFIGURED; +import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOWED_REGISTERED; +import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; /** * @author Sean Leonard <meanderingcode@aetherislands.net> @@ -36,11 +40,17 @@ import static se.leap.bitmaskclient.Constants.PROVIDER_CONFIGURED; public final class Provider implements Parcelable { private JSONObject definition = new JSONObject(); // Represents our Provider's provider.json + private JSONObject eipServiceJson = new JSONObject(); // Represents our Provider's provider.json private DefaultedURL mainUrl = new DefaultedURL(); private DefaultedURL apiUrl = new DefaultedURL(); private String certificatePin = ""; private String certificatePinEncoding = ""; private String caCert = ""; + private String caCertFingerprint = ""; + private String apiVerson = ""; + + private boolean allowAnonymous; + private boolean allowRegistered; final public static String API_URL = "api_uri", @@ -69,6 +79,14 @@ public final class Provider implements Parcelable { public Provider() { } + public Provider(String mainUrl) { + try { + this.mainUrl.setUrl(new URL(mainUrl)); + } catch (MalformedURLException e) { + this.mainUrl = new DefaultedURL(); + } + } + public Provider(URL mainUrl) { this.mainUrl.setUrl(mainUrl); } @@ -108,12 +126,20 @@ public final class Provider implements Parcelable { !caCert.isEmpty(); } - protected void setUrl(URL url) { + public void setMainUrl(URL url) { mainUrl.setUrl(url); } - protected void define(JSONObject provider_json) { - definition = provider_json; + public void setMainUrl(String url) { + try { + mainUrl.setUrl(new URL(url)); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + } + + public void define(JSONObject providerJson) { + definition = providerJson; parseDefinition(definition); } @@ -141,6 +167,19 @@ public final class Provider implements Parcelable { return apiUrl; } + protected String getApiUrlWithVersion() { + return getApiUrlString() + "/" + getApiVersion(); + } + + + protected String getApiUrlString() { + return getApiUrl().toString(); + } + + public String getApiVersion() { + return apiVerson; + } + protected String certificatePin() { return certificatePin; } protected boolean hasCertificatePin() { @@ -160,6 +199,10 @@ public final class Provider implements Parcelable { return caCert; } + public String getCaCertFingerprint() { + return caCertFingerprint; + } + public String getName() { // Should we pass the locale in, or query the system here? String lang = Locale.getDefault().getLanguage(); @@ -200,26 +243,7 @@ public final class Provider implements Parcelable { } protected boolean hasEIP() { - try { - JSONArray services = definition.getJSONArray(API_TERM_SERVICES); // returns ["openvpn"] - for (int i = 0; i < API_EIP_TYPES.length + 1; i++) { - try { - // Walk the EIP types array looking for matches in provider's service definitions - if (Arrays.asList(API_EIP_TYPES).contains(services.getString(i))) - return true; - } catch (NullPointerException e) { - e.printStackTrace(); - return false; - } catch (JSONException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - return false; - } - } - } catch (Exception e) { - // TODO: handle exception - } - return false; + return getEipServiceJson() != null && getEipServiceJson().length() > 0; } public boolean allowsRegistration() { @@ -237,12 +261,11 @@ public final class Provider implements Parcelable { @Override public void writeToParcel(Parcel parcel, int i) { - if(mainUrl != null) - parcel.writeString(mainUrl.toString()); - if (definition != null) - parcel.writeString(definition.toString()); - if (caCert != null) - parcel.writeString(caCert); + parcel.writeString(getMainUrlString()); + parcel.writeString(getDefinitionString()); + parcel.writeString(getCaCert()); + parcel.writeString(getCaCertFingerprint()); + parcel.writeString(getEipServiceJsonString()); } @Override @@ -289,6 +312,14 @@ public final class Provider implements Parcelable { if (!caCert.isEmpty()) { this.caCert = caCert; } + String caCertFingerprint = in.readString(); + if (!caCertFingerprint.isEmpty()) { + this.caCertFingerprint = caCertFingerprint; + } + String eipServiceJson = in.readString(); + if (!eipServiceJson.isEmpty()) { + this.setEipServiceJson(new JSONObject(eipServiceJson)); + } } catch (MalformedURLException | JSONException e) { e.printStackTrace(); } @@ -300,15 +331,41 @@ public final class Provider implements Parcelable { this.certificatePin = pin.split(":")[1].trim(); this.certificatePinEncoding = pin.split(":")[0].trim(); this.apiUrl.setUrl(new URL(definition.getString(API_URL))); + this.allowAnonymous = definition.getJSONObject(Provider.SERVICE).getBoolean(PROVIDER_ALLOW_ANONYMOUS); + this.allowRegistered = definition.getJSONObject(Provider.SERVICE).getBoolean(PROVIDER_ALLOWED_REGISTERED); + this.apiVerson = getDefinition().getString(Provider.API_VERSION); } catch (JSONException | ArrayIndexOutOfBoundsException | MalformedURLException e) { e.printStackTrace(); } } - public void setCACert(String cert) { + public void setCaCert(String cert) { this.caCert = cert; } + public void setCaCertFingerprint(String certFingerprint) { + this.caCertFingerprint = certFingerprint; + } + + public boolean allowsAnonymous() { + return allowAnonymous; + } + + public boolean allowsRegistered() { + return allowRegistered; + } + + public void setEipServiceJson(JSONObject eipServiceJson) { + this.eipServiceJson = eipServiceJson; + } + + public JSONObject getEipServiceJson() { + return eipServiceJson; + } + + public String getEipServiceJsonString() { + return getEipServiceJson().toString(); + } public boolean isDefault() { return getMainUrl().isDefault() && getApiUrl().isDefault() && diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java index ccc71a67..73f5c530 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java @@ -40,22 +40,18 @@ public class ProviderAPI extends IntentService implements ProviderApiManagerBase TAG = ProviderAPI.class.getSimpleName(), SET_UP_PROVIDER = "setUpProvider", UPDATE_PROVIDER_DETAILS = "updateProviderDetails", - DOWNLOAD_NEW_PROVIDER_DOTJSON = "downloadNewProviderDotJSON", SIGN_UP = "srpRegister", LOG_IN = "srpAuth", LOG_OUT = "logOut", DOWNLOAD_CERTIFICATE = "downloadUserAuthedCertificate", PARAMETERS = "parameters", - RESULT_KEY = "result", - RESULT_CODE = "RESULT CODE", RECEIVER_KEY = "receiver", ERRORS = "errors", ERRORID = "errorId", UPDATE_PROGRESSBAR = "update_progressbar", CURRENT_PROGRESS = "current_progress", DOWNLOAD_EIP_SERVICE = TAG + ".DOWNLOAD_EIP_SERVICE", - PROVIDER_SET_UP = TAG + ".PROVIDER_SET_UP", - PROVIDER_API_EVENT = "PROVIDER_API_EVENT"; + PROVIDER_SET_UP = TAG + ".PROVIDER_SET_UP"; final public static int SUCCESSFUL_LOGIN = 3, @@ -85,16 +81,6 @@ public class ProviderAPI extends IntentService implements ProviderApiManagerBase } //TODO: refactor me, please! - public static boolean caCertDownloaded() { - return ProviderApiManager.caCertDownloaded(); - } - - //TODO: refactor me, please! - public static String lastProviderMainUrl() { - return ProviderApiManager.lastProviderMainUrl(); - } - - //TODO: refactor me, please! //used in insecure flavor only @SuppressLint("unused") public static boolean lastDangerOn() { diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderAPICommand.java b/app/src/main/java/se/leap/bitmaskclient/ProviderAPICommand.java index 0e4cfe8a..65d01b22 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderAPICommand.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderAPICommand.java @@ -6,40 +6,74 @@ import android.os.*; import org.jetbrains.annotations.*; public class ProviderAPICommand { - private static Context context; + private Context context; - private static String action; - private static Bundle parameters; - private static ResultReceiver result_receiver; + private String action; + private Bundle parameters; + private ResultReceiver resultReceiver; + private Provider provider; - public static void initialize(Context context) { - ProviderAPICommand.context = context; + private ProviderAPICommand(@NotNull Context context, @NotNull String action, @NotNull Provider provider, ResultReceiver resultReceiver) { + this(context, action, Bundle.EMPTY, provider, resultReceiver); + } + private ProviderAPICommand(@NotNull Context context, @NotNull String action, @NotNull Provider provider) { + this(context, action, Bundle.EMPTY, provider); } - private static boolean isInitialized() { - return context != null; + private ProviderAPICommand(@NotNull Context context, @NotNull String action, @NotNull Bundle parameters, @NotNull Provider provider) { + this(context, action, parameters, provider, null); } - public static void execute(Bundle parameters, @NotNull String action, @NotNull ResultReceiver result_receiver) throws IllegalStateException { - if(!isInitialized()) throw new IllegalStateException(); + private ProviderAPICommand(@NotNull Context context, @NotNull String action, @NotNull Bundle parameters, @NotNull Provider provider, @Nullable ResultReceiver resultReceiver) { + super(); + this.context = context; + this.action = action; + this.parameters = parameters; + this.resultReceiver = resultReceiver; + this.provider = provider; + } - ProviderAPICommand.action = action; - ProviderAPICommand.parameters = parameters; - ProviderAPICommand.result_receiver = result_receiver; + private boolean isInitialized() { + return context != null; + } - Intent intent = setUpIntent(); - context.startService(intent); + private void execute() { + if (isInitialized()) { + Intent intent = setUpIntent(); + context.startService(intent); + } } - private static Intent setUpIntent() { + private Intent setUpIntent() { Intent command = new Intent(context, ProviderAPI.class); command.setAction(action); command.putExtra(ProviderAPI.PARAMETERS, parameters); - command.putExtra(ProviderAPI.RECEIVER_KEY, result_receiver); + if (resultReceiver != null) { + command.putExtra(ProviderAPI.RECEIVER_KEY, resultReceiver); + } + command.putExtra(Constants.PROVIDER_KEY, provider); return command; } + public static void execute(Context context, String action, Provider provider) { + ProviderAPICommand command = new ProviderAPICommand(context, action, provider); + command.execute(); + } + public static void execute(Context context, String action, Bundle parameters, Provider provider) { + ProviderAPICommand command = new ProviderAPICommand(context, action, parameters, provider); + command.execute(); + } + + public static void execute(Context context, String action, Bundle parameters, Provider provider, ResultReceiver resultReceiver) { + ProviderAPICommand command = new ProviderAPICommand(context, action, parameters, provider, resultReceiver); + command.execute(); + } + + public static void execute(Context context, String action, Provider provider, ResultReceiver resultReceiver) { + ProviderAPICommand command = new ProviderAPICommand(context, action, provider, resultReceiver); + command.execute(); + } } diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java index 8117fb99..000dd164 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java @@ -24,6 +24,7 @@ import android.os.Bundle; import android.os.ResultReceiver; import android.support.annotation.NonNull; import android.util.Base64; +import android.util.Log; import android.util.Pair; import org.json.JSONException; @@ -49,19 +50,20 @@ import java.util.List; import javax.net.ssl.SSLHandshakeException; import okhttp3.OkHttpClient; -import se.leap.bitmaskclient.userstatus.SessionDialog; -import se.leap.bitmaskclient.userstatus.User; -import se.leap.bitmaskclient.userstatus.UserStatus; +import se.leap.bitmaskclient.Constants.CREDENTIAL_ERRORS; import static se.leap.bitmaskclient.ConfigHelper.getFingerprintFromCertificate; -import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOWED_REGISTERED; -import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; +import static se.leap.bitmaskclient.Constants.BROADCAST_PROVIDER_API_EVENT; +import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; +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.Constants.PROVIDER_PRIVATE_KEY; import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_CERTIFICATE_PINNING; import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_CORRUPTED_PROVIDER_JSON; import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_INVALID_CERTIFICATE; -import static se.leap.bitmaskclient.Provider.MAIN_URL; import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE; import static se.leap.bitmaskclient.ProviderAPI.CURRENT_PROGRESS; @@ -73,17 +75,14 @@ import static se.leap.bitmaskclient.ProviderAPI.FAILED_LOGIN; import static se.leap.bitmaskclient.ProviderAPI.FAILED_SIGNUP; import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE; -import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_API_EVENT; -import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_SET_UP; import static se.leap.bitmaskclient.ProviderAPI.LOGOUT_FAILED; import static se.leap.bitmaskclient.ProviderAPI.LOG_IN; import static se.leap.bitmaskclient.ProviderAPI.LOG_OUT; import static se.leap.bitmaskclient.ProviderAPI.PARAMETERS; import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_NOK; import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_OK; +import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_SET_UP; import static se.leap.bitmaskclient.ProviderAPI.RECEIVER_KEY; -import static se.leap.bitmaskclient.ProviderAPI.RESULT_CODE; -import static se.leap.bitmaskclient.ProviderAPI.RESULT_KEY; import static se.leap.bitmaskclient.ProviderAPI.SET_UP_PROVIDER; import static se.leap.bitmaskclient.ProviderAPI.SIGN_UP; import static se.leap.bitmaskclient.ProviderAPI.SUCCESSFUL_LOGIN; @@ -109,36 +108,24 @@ import static se.leap.bitmaskclient.R.string.warning_expired_provider_cert; public abstract class ProviderApiManagerBase { + private final static String TAG = ProviderApiManagerBase.class.getName(); + public interface ProviderApiServiceCallback { void broadcastEvent(Intent intent); } private ProviderApiServiceCallback serviceCallback; - protected static volatile boolean - CA_CERT_DOWNLOADED = false, - PROVIDER_JSON_DOWNLOADED = false, - EIP_SERVICE_JSON_DOWNLOADED = false; - - protected static String lastProviderMainUrl; - protected static boolean go_ahead = true; - protected static SharedPreferences preferences; - protected static String providerApiUrl; - protected static String providerCaCertFingerprint; - protected static String providerCaCert; - protected static JSONObject providerDefinition; + static boolean go_ahead = true; + protected SharedPreferences preferences; protected Resources resources; - protected OkHttpClientGenerator clientGenerator; + OkHttpClientGenerator clientGenerator; public static void stop() { go_ahead = false; } - public static String lastProviderMainUrl() { - return lastProviderMainUrl; - } - - public ProviderApiManagerBase(SharedPreferences preferences, Resources resources, OkHttpClientGenerator clientGenerator, ProviderApiServiceCallback callback) { + ProviderApiManagerBase(SharedPreferences preferences, Resources resources, OkHttpClientGenerator clientGenerator, ProviderApiServiceCallback callback) { this.preferences = preferences; this.resources = resources; this.serviceCallback = callback; @@ -150,96 +137,88 @@ public abstract class ProviderApiManagerBase { String action = command.getAction(); Bundle parameters = command.getBundleExtra(PARAMETERS); - if (providerApiUrl == null && preferences.contains(Provider.KEY)) { - try { - JSONObject provider_json = new JSONObject(preferences.getString(Provider.KEY, "")); - providerApiUrl = provider_json.getString(Provider.API_URL) + "/" + provider_json.getString(Provider.API_VERSION); - go_ahead = true; - } catch (JSONException e) { - go_ahead = false; - } + Provider provider = command.getParcelableExtra(PROVIDER_KEY); + + if (provider == null) { + Log.e(TAG, action +" called without provider!"); + return; + } + if (action == null) { + Log.e(TAG, "Intent without action sent!"); + return; } if (action.equals(UPDATE_PROVIDER_DETAILS)) { - resetProviderDetails(); + resetProviderDetails(provider); Bundle task = new Bundle(); - String mainUrl = parameters.getString(Provider.MAIN_URL); - if (mainUrl == null) { - mainUrl = lastProviderMainUrl; - } - task.putString(MAIN_URL, mainUrl); - Bundle result = setUpProvider(task); - if (result.getBoolean(RESULT_KEY)) { - sendToReceiverOrBroadcast(receiver, PROVIDER_OK, result); + Bundle result = setUpProvider(provider, task); + if (result.getBoolean(BROADCAST_RESULT_KEY)) { + sendToReceiverOrBroadcast(receiver, PROVIDER_OK, result, provider); } else { - sendToReceiverOrBroadcast(receiver, PROVIDER_NOK, result); + sendToReceiverOrBroadcast(receiver, PROVIDER_NOK, result, provider); } } else if (action.equalsIgnoreCase(SET_UP_PROVIDER)) { - Bundle result = setUpProvider(parameters); + Bundle result = setUpProvider(provider, parameters); if (go_ahead) { - if (result.getBoolean(RESULT_KEY)) { - sendToReceiverOrBroadcast(receiver, PROVIDER_OK, result); + if (result.getBoolean(BROADCAST_RESULT_KEY)) { + sendToReceiverOrBroadcast(receiver, PROVIDER_OK, result, provider); } else { - sendToReceiverOrBroadcast(receiver, PROVIDER_NOK, result); + sendToReceiverOrBroadcast(receiver, PROVIDER_NOK, result, provider); } } } else if (action.equalsIgnoreCase(SIGN_UP)) { - UserStatus.updateStatus(UserStatus.SessionStatus.SIGNING_UP, resources); Bundle result = tryToRegister(parameters); - if (result.getBoolean(RESULT_KEY)) { - sendToReceiverOrBroadcast(receiver, SUCCESSFUL_SIGNUP, result); + if (result.getBoolean(BROADCAST_RESULT_KEY)) { + sendToReceiverOrBroadcast(receiver, SUCCESSFUL_SIGNUP, result, provider); } else { - sendToReceiverOrBroadcast(receiver, FAILED_SIGNUP, result); + sendToReceiverOrBroadcast(receiver, FAILED_SIGNUP, result, provider); } } else if (action.equalsIgnoreCase(LOG_IN)) { - UserStatus.updateStatus(UserStatus.SessionStatus.LOGGING_IN, resources); - Bundle result = tryToAuthenticate(parameters); - if (result.getBoolean(RESULT_KEY)) { - sendToReceiverOrBroadcast(receiver, SUCCESSFUL_LOGIN, result); - UserStatus.updateStatus(UserStatus.SessionStatus.LOGGED_IN, resources); + Bundle result = tryToAuthenticate(provider, parameters); + if (result.getBoolean(BROADCAST_RESULT_KEY)) { + sendToReceiverOrBroadcast(receiver, SUCCESSFUL_LOGIN, result, provider); } else { - sendToReceiverOrBroadcast(receiver, FAILED_LOGIN, result); - UserStatus.updateStatus(UserStatus.SessionStatus.NOT_LOGGED_IN, resources); + sendToReceiverOrBroadcast(receiver, FAILED_LOGIN, result, provider); } } else if (action.equalsIgnoreCase(LOG_OUT)) { - UserStatus.updateStatus(UserStatus.SessionStatus.LOGGING_OUT, resources); - if (logOut()) { - sendToReceiverOrBroadcast(receiver, SUCCESSFUL_LOGOUT, Bundle.EMPTY); - UserStatus.updateStatus(UserStatus.SessionStatus.LOGGED_OUT, resources); + if (logOut(provider)) { + sendToReceiverOrBroadcast(receiver, SUCCESSFUL_LOGOUT, Bundle.EMPTY, provider); } else { - sendToReceiverOrBroadcast(receiver, LOGOUT_FAILED, Bundle.EMPTY); - UserStatus.updateStatus(UserStatus.SessionStatus.DIDNT_LOG_OUT, resources); + sendToReceiverOrBroadcast(receiver, LOGOUT_FAILED, Bundle.EMPTY, provider); } } else if (action.equalsIgnoreCase(DOWNLOAD_CERTIFICATE)) { - if (updateVpnCertificate()) { - sendToReceiverOrBroadcast(receiver, CORRECTLY_DOWNLOADED_CERTIFICATE, Bundle.EMPTY); + if (updateVpnCertificate(provider)) { + sendToReceiverOrBroadcast(receiver, CORRECTLY_DOWNLOADED_CERTIFICATE, Bundle.EMPTY, provider); } else { - sendToReceiverOrBroadcast(receiver, INCORRECTLY_DOWNLOADED_CERTIFICATE, Bundle.EMPTY); + sendToReceiverOrBroadcast(receiver, INCORRECTLY_DOWNLOADED_CERTIFICATE, Bundle.EMPTY, provider); } } else if (action.equalsIgnoreCase(DOWNLOAD_EIP_SERVICE)) { - Bundle result = getAndSetEipServiceJson(); - if (result.getBoolean(RESULT_KEY)) { - sendToReceiverOrBroadcast(receiver, CORRECTLY_DOWNLOADED_EIP_SERVICE, result); + Bundle result = getAndSetEipServiceJson(provider); + if (result.getBoolean(BROADCAST_RESULT_KEY)) { + sendToReceiverOrBroadcast(receiver, CORRECTLY_DOWNLOADED_EIP_SERVICE, result, provider); } else { - sendToReceiverOrBroadcast(receiver, INCORRECTLY_DOWNLOADED_EIP_SERVICE, result); + sendToReceiverOrBroadcast(receiver, INCORRECTLY_DOWNLOADED_EIP_SERVICE, result, provider); } } else if (action.equalsIgnoreCase(PROVIDER_SET_UP)) { - if(EIP_SERVICE_JSON_DOWNLOADED && CA_CERT_DOWNLOADED && PROVIDER_JSON_DOWNLOADED ) { + if(provider.hasEIP() && provider.hasCaCert() && provider.hasDefinition()) { if(receiver!= null) { - sendToReceiverOrBroadcast(receiver, PROVIDER_OK, Bundle.EMPTY); + Bundle result = new Bundle(); + result.putParcelable(PROVIDER_KEY, provider); + receiver.send(PROVIDER_OK, result); } } } } - protected void resetProviderDetails() { - CA_CERT_DOWNLOADED = PROVIDER_JSON_DOWNLOADED = false; - deleteProviderDetailsFromPreferences(providerDefinition); - providerCaCert = ""; - providerDefinition = new JSONObject(); + void resetProviderDetails(Provider provider) { + provider.setCaCert(""); + provider.define(new JSONObject()); + provider.setEipServiceJson(new JSONObject()); + + deleteProviderDetailsFromPreferences(provider.getDomain()); } - protected String formatErrorMessage(final int toastStringId) { + String formatErrorMessage(final int toastStringId) { return formatErrorMessage(resources.getString(toastStringId)); } @@ -256,7 +235,7 @@ public abstract class ProviderApiManagerBase { } } - protected void addErrorMessageToJson(JSONObject jsonObject, String errorMessage) { + private void addErrorMessageToJson(JSONObject jsonObject, String errorMessage) { try { jsonObject.put(ERRORS, errorMessage); } catch (JSONException e) { @@ -264,7 +243,7 @@ public abstract class ProviderApiManagerBase { } } - protected void addErrorMessageToJson(JSONObject jsonObject, String errorMessage, String errorId) { + private void addErrorMessageToJson(JSONObject jsonObject, String errorMessage, String errorId) { try { jsonObject.put(ERRORS, errorMessage); jsonObject.put(ERRORID, errorId); @@ -278,32 +257,37 @@ public abstract class ProviderApiManagerBase { private Bundle tryToRegister(Bundle task) { Bundle result = new Bundle(); - int progress = 0; - String username = User.userName(); - String password = task.getString(SessionDialog.PASSWORD); + String username = task.getString(CREDENTIALS_USERNAME); + String password = task.getString(CREDENTIALS_PASSWORD); + Provider provider = task.getParcelable(PROVIDER_KEY); + + if(provider == null) { + result.putBoolean(BROADCAST_RESULT_KEY, false); + Log.e(TAG, "no provider when trying to register"); + return result; + } if (validUserLoginData(username, password)) { - result = register(username, password); - broadcastProgress(progress++); + result = register(provider, username, password); } else { if (!wellFormedPassword(password)) { - result.putBoolean(RESULT_KEY, false); - result.putString(SessionDialog.USERNAME, username); - result.putBoolean(SessionDialog.ERRORS.PASSWORD_INVALID_LENGTH.toString(), true); + result.putBoolean(BROADCAST_RESULT_KEY, false); + result.putString(CREDENTIALS_USERNAME, username); + result.putBoolean(CREDENTIAL_ERRORS.PASSWORD_INVALID_LENGTH.toString(), true); } if (!validUsername(username)) { - result.putBoolean(RESULT_KEY, false); - result.putBoolean(SessionDialog.ERRORS.USERNAME_MISSING.toString(), true); + result.putBoolean(BROADCAST_RESULT_KEY, false); + result.putBoolean(CREDENTIAL_ERRORS.USERNAME_MISSING.toString(), true); } } return result; } - private Bundle register(String username, String password) { + private Bundle register(Provider provider, String username, String password) { JSONObject stepResult = null; - OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(stepResult); + OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), stepResult); if (okHttpClient == null) { return authFailedNotification(stepResult, username); } @@ -313,15 +297,15 @@ public abstract class ProviderApiManagerBase { BigInteger password_verifier = client.calculateV(username, password, salt); - JSONObject api_result = sendNewUserDataToSRPServer(providerApiUrl, username, new BigInteger(1, salt).toString(16), password_verifier.toString(16), okHttpClient); + JSONObject api_result = sendNewUserDataToSRPServer(provider.getApiUrlString(), username, new BigInteger(1, salt).toString(16), password_verifier.toString(16), okHttpClient); Bundle result = new Bundle(); if (api_result.has(ERRORS)) result = authFailedNotification(api_result, username); else { - result.putString(SessionDialog.USERNAME, username); - result.putString(SessionDialog.PASSWORD, password); - result.putBoolean(RESULT_KEY, true); + result.putString(CREDENTIALS_USERNAME, username); + result.putString(CREDENTIALS_PASSWORD, password); + result.putBoolean(BROADCAST_RESULT_KEY, true); } return result; @@ -330,37 +314,39 @@ public abstract class ProviderApiManagerBase { /** * Starts the authentication process using SRP protocol. * - * @param task containing: username, password and api url. - * @return a bundle with a boolean value mapped to a key named RESULT_KEY, and which is true if authentication was successful. + * @param task containing: username, password and provider + * @return a bundle with a boolean value mapped to a key named BROADCAST_RESULT_KEY, and which is true if authentication was successful. */ - private Bundle tryToAuthenticate(Bundle task) { + private Bundle tryToAuthenticate(Provider provider, Bundle task) { Bundle result = new Bundle(); - int progress = 0; - String username = User.userName(); - String password = task.getString(SessionDialog.PASSWORD); + String username = task.getString(CREDENTIALS_USERNAME); + String password = task.getString(CREDENTIALS_PASSWORD); + if (validUserLoginData(username, password)) { - result = authenticate(username, password); - broadcastProgress(progress++); + result = authenticate(provider, username, password); } else { if (!wellFormedPassword(password)) { - result.putBoolean(RESULT_KEY, false); - result.putString(SessionDialog.USERNAME, username); - result.putBoolean(SessionDialog.ERRORS.PASSWORD_INVALID_LENGTH.toString(), true); + result.putBoolean(BROADCAST_RESULT_KEY, false); + result.putString(CREDENTIALS_USERNAME, username); + result.putBoolean(CREDENTIAL_ERRORS.PASSWORD_INVALID_LENGTH.toString(), true); } if (!validUsername(username)) { - result.putBoolean(RESULT_KEY, false); - result.putBoolean(SessionDialog.ERRORS.USERNAME_MISSING.toString(), true); + result.putBoolean(BROADCAST_RESULT_KEY, false); + result.putBoolean(CREDENTIAL_ERRORS.USERNAME_MISSING.toString(), true); } } return result; } - private Bundle authenticate(String username, String password) { + private Bundle authenticate(Provider provider, String username, String password) { Bundle result = new Bundle(); JSONObject stepResult = new JSONObject(); - OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(stepResult); + + String providerApiUrl = provider.getApiUrlWithVersion(); + + OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), stepResult); if (okHttpClient == null) { return authFailedNotification(stepResult, username); } @@ -378,13 +364,13 @@ public abstract class ProviderApiManagerBase { setTokenIfAvailable(step_result); byte[] M2 = new BigInteger(step_result.getString(LeapSRPSession.M2), 16).toByteArray(); if (client.verify(M2)) { - result.putBoolean(RESULT_KEY, true); + result.putBoolean(BROADCAST_RESULT_KEY, true); } else { authFailedNotification(step_result, username); } } else { - result.putBoolean(RESULT_KEY, false); - result.putString(SessionDialog.USERNAME, username); + 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)); } } catch (JSONException e) { @@ -428,38 +414,29 @@ public abstract class ProviderApiManagerBase { } if (!username.isEmpty()) - userNotificationBundle.putString(SessionDialog.USERNAME, username); - userNotificationBundle.putBoolean(RESULT_KEY, false); + userNotificationBundle.putString(CREDENTIALS_USERNAME, username); + userNotificationBundle.putBoolean(BROADCAST_RESULT_KEY, false); return userNotificationBundle; } - void sendToReceiverOrBroadcast(ResultReceiver receiver, int resultCode, Bundle resultData) { + void sendToReceiverOrBroadcast(ResultReceiver receiver, int resultCode, Bundle resultData, Provider provider) { + if (resultData == null || resultData == Bundle.EMPTY) { + resultData = new Bundle(); + } + resultData.putParcelable(PROVIDER_KEY, provider); if (receiver != null) { receiver.send(resultCode, resultData); } else { - broadcastEvent(PROVIDER_API_EVENT, resultCode, resultData); + broadcastEvent(resultCode, resultData); } } - /** - * Sets up an intent with the progress value passed as a parameter - * and sends it as a broadcast. - * - * @param progress - */ - void broadcastProgress(int progress) { - Intent intentUpdate = new Intent(UPDATE_PROGRESSBAR); - intentUpdate.addCategory(Intent.CATEGORY_DEFAULT); - intentUpdate.putExtra(CURRENT_PROGRESS, progress); - serviceCallback.broadcastEvent(intentUpdate); - } - - void broadcastEvent(String action, int resultCode , Bundle resultData) { - Intent intentUpdate = new Intent(action); + private void broadcastEvent(int resultCode , Bundle resultData) { + Intent intentUpdate = new Intent(BROADCAST_PROVIDER_API_EVENT); intentUpdate.addCategory(Intent.CATEGORY_DEFAULT); - intentUpdate.putExtra(RESULT_CODE, resultCode); - intentUpdate.putExtra(RESULT_KEY, resultData); + intentUpdate.putExtra(BROADCAST_RESULT_CODE, resultCode); + intentUpdate.putExtra(BROADCAST_RESULT_KEY, resultData); serviceCallback.broadcastEvent(intentUpdate); } @@ -627,29 +604,25 @@ public abstract class ProviderApiManagerBase { /** * Downloads a provider.json from a given URL, adding a new provider using the given name. * - * @param task containing a boolean meaning if the provider is custom or not, another boolean meaning if the user completely trusts this provider, the provider name and its provider.json url. - * @return a bundle with a boolean value mapped to a key named RESULT_KEY, and which is true if the update was successful. + * @param task containing a boolean meaning if the provider is custom or not, another boolean meaning if the user completely trusts this provider + * @return a bundle with a boolean value mapped to a key named BROADCAST_RESULT_KEY, and which is true if the update was successful. */ - protected abstract Bundle setUpProvider(Bundle task); + protected abstract Bundle setUpProvider(Provider provider, Bundle task); /** * Downloads the eip-service.json from a given URL, and saves eip service capabilities including the offered gateways - * @return a bundle with a boolean value mapped to a key named RESULT_KEY, and which is true if the download was successful. + * @return a bundle with a boolean value mapped to a key named BROADCAST_RESULT_KEY, and which is true if the download was successful. */ - protected abstract Bundle getAndSetEipServiceJson(); + protected abstract Bundle getAndSetEipServiceJson(Provider provider); /** * Downloads a new OpenVPN certificate, attaching authenticated cookie for authenticated certificate. * * @return true if certificate was downloaded correctly, false if provider.json is not present in SharedPreferences, or if the certificate url could not be parsed as a URI, or if there was an SSL error. */ - protected abstract boolean updateVpnCertificate(); + protected abstract boolean updateVpnCertificate(Provider provider); - protected static boolean caCertDownloaded() { - return CA_CERT_DOWNLOADED; - } - protected boolean isValidJson(String jsonString) { try { new JSONObject(jsonString); @@ -662,19 +635,19 @@ public abstract class ProviderApiManagerBase { } } - protected boolean validCertificate(String cert_string) { + protected boolean validCertificate(Provider provider, String certString) { boolean result = false; - if (!ConfigHelper.checkErroneousDownload(cert_string)) { - X509Certificate certificate = ConfigHelper.parseX509CertificateFromString(cert_string); + if (!ConfigHelper.checkErroneousDownload(certString)) { + X509Certificate certificate = ConfigHelper.parseX509CertificateFromString(certString); try { if (certificate != null) { - JSONObject provider_json = new JSONObject(preferences.getString(Provider.KEY, "")); - String fingerprint = provider_json.getString(Provider.CA_CERT_FINGERPRINT); + JSONObject providerJson = provider.getDefinition(); + String fingerprint = providerJson.getString(Provider.CA_CERT_FINGERPRINT); String encoding = fingerprint.split(":")[0]; - String expected_fingerprint = fingerprint.split(":")[1]; - String real_fingerprint = getFingerprintFromCertificate(certificate, encoding); + String expectedFingerprint = fingerprint.split(":")[1]; + String realFingerprint = getFingerprintFromCertificate(certificate, encoding); - result = real_fingerprint.trim().equalsIgnoreCase(expected_fingerprint.trim()); + result = realFingerprint.trim().equalsIgnoreCase(expectedFingerprint.trim()); } else result = false; } catch (JSONException | NoSuchAlgorithmException | CertificateEncodingException e) { @@ -685,50 +658,41 @@ public abstract class ProviderApiManagerBase { return result; } - protected void checkPersistedProviderUpdates() { - String providerDomain = getDomainFromMainURL(lastProviderMainUrl); + protected void getPersistedProviderUpdates(Provider provider) { + String providerDomain = getDomainFromMainURL(provider.getMainUrlString()); if (hasUpdatedProviderDetails(providerDomain)) { - providerCaCert = getPersistedProviderCA(providerDomain); - providerDefinition = getPersistedProviderDefinition(providerDomain); - providerCaCertFingerprint = getPersistedCaCertFingerprint(providerDomain); - providerApiUrl = getApiUrlWithVersion(providerDefinition); + provider.setCaCert(getPersistedProviderCA(providerDomain)); + provider.define(getPersistedProviderDefinition(providerDomain)); + provider.setCaCertFingerprint(getPersistedCaCertFingerprint(providerDomain)); } } - protected Bundle validateProviderDetails() { - Bundle result = validateCertificateForProvider(providerCaCert, providerDefinition, lastProviderMainUrl); + Bundle validateProviderDetails(Provider provider) { + Bundle result = validateCertificateForProvider(provider); //invalid certificate or no certificate - if (result.containsKey(ERRORS) || (result.containsKey(RESULT_KEY) && !result.getBoolean(RESULT_KEY)) ) { + if (result.containsKey(ERRORS) || (result.containsKey(BROADCAST_RESULT_KEY) && !result.getBoolean(BROADCAST_RESULT_KEY)) ) { return result; } - //valid certificate: skip download, save loaded provider CA cert and provider definition directly - try { - preferences.edit().putString(Provider.KEY, providerDefinition.toString()). - putBoolean(PROVIDER_ALLOW_ANONYMOUS, providerDefinition.getJSONObject(Provider.SERVICE).getBoolean(PROVIDER_ALLOW_ANONYMOUS)). - putBoolean(PROVIDER_ALLOWED_REGISTERED, providerDefinition.getJSONObject(Provider.SERVICE).getBoolean(PROVIDER_ALLOWED_REGISTERED)). - putString(Provider.CA_CERT, providerCaCert).commit(); - CA_CERT_DOWNLOADED = true; - PROVIDER_JSON_DOWNLOADED = true; - result.putBoolean(RESULT_KEY, true); - } catch (JSONException e) { - e.printStackTrace(); - setErrorResult(result, warning_corrupted_provider_details, ERROR_CORRUPTED_PROVIDER_JSON.toString()); - } + result.putBoolean(BROADCAST_RESULT_KEY, true); return result; } - protected Bundle validateCertificateForProvider(String cert_string, JSONObject providerDefinition, String mainUrl) { + protected Bundle validateCertificateForProvider(Provider provider) { Bundle result = new Bundle(); - result.putBoolean(RESULT_KEY, false); + result.putBoolean(BROADCAST_RESULT_KEY, false); + + String caCert = provider.getCaCert(); + JSONObject providerDefinition = provider.getDefinition(); + String mainUrl = provider.getMainUrlString(); - if (ConfigHelper.checkErroneousDownload(cert_string)) { + if (ConfigHelper.checkErroneousDownload(caCert)) { return result; } - X509Certificate certificate = ConfigHelper.parseX509CertificateFromString(cert_string); + X509Certificate certificate = ConfigHelper.parseX509CertificateFromString(caCert); if (certificate == null) { return setErrorResult(result, warning_corrupted_provider_cert, ERROR_INVALID_CERTIFICATE.toString()); } @@ -736,9 +700,9 @@ public abstract class ProviderApiManagerBase { certificate.checkValidity(); String fingerprint = getCaCertFingerprint(providerDefinition); String encoding = fingerprint.split(":")[0]; - String expected_fingerprint = fingerprint.split(":")[1]; - String real_fingerprint = getFingerprintFromCertificate(certificate, encoding); - if (!real_fingerprint.trim().equalsIgnoreCase(expected_fingerprint.trim())) { + String expectedFingerprint = fingerprint.split(":")[1]; + String realFingerprint = getFingerprintFromCertificate(certificate, encoding); + if (!realFingerprint.trim().equalsIgnoreCase(expectedFingerprint.trim())) { return setErrorResult(result, warning_corrupted_provider_cert, ERROR_CERTIFICATE_PINNING.toString()); } @@ -747,7 +711,7 @@ public abstract class ProviderApiManagerBase { return setErrorResult(result, warning_corrupted_provider_details, ERROR_CORRUPTED_PROVIDER_JSON.toString()); } - if (!canConnect(cert_string, providerDefinition, result)) { + if (!canConnect(caCert, providerDefinition, result)) { return result; } } catch (NoSuchAlgorithmException e ) { @@ -758,11 +722,11 @@ public abstract class ProviderApiManagerBase { return setErrorResult(result, warning_expired_provider_cert, ERROR_INVALID_CERTIFICATE.toString()); } - result.putBoolean(RESULT_KEY, true); + result.putBoolean(BROADCAST_RESULT_KEY, true); return result; } - protected Bundle setErrorResult(Bundle result, int errorMessageId, String errorId) { + Bundle setErrorResult(Bundle result, int errorMessageId, String errorId) { JSONObject errorJson = new JSONObject(); if (errorId != null) { addErrorMessageToJson(errorJson, resources.getString(errorMessageId), errorId); @@ -770,7 +734,7 @@ public abstract class ProviderApiManagerBase { addErrorMessageToJson(errorJson, resources.getString(errorMessageId)); } result.putString(ERRORS, errorJson.toString()); - result.putBoolean(RESULT_KEY, false); + result.putBoolean(BROADCAST_RESULT_KEY, false); return result; } @@ -811,17 +775,7 @@ public abstract class ProviderApiManagerBase { return ""; } - protected String getApiUrlWithVersion(JSONObject providerDefinition) { - try { - return providerDefinition.getString(Provider.API_URL) + "/" + providerDefinition.getString(Provider.API_VERSION); - } catch (JSONException e) { - e.printStackTrace(); - } - return ""; - } - - protected void deleteProviderDetailsFromPreferences(JSONObject providerDefinition) { - String providerDomain = getProviderDomain(providerDefinition); + protected void deleteProviderDetailsFromPreferences(String providerDomain) { if (preferences.contains(Provider.KEY + "." + providerDomain)) { preferences.edit().remove(Provider.KEY + "." + providerDomain).apply(); @@ -907,17 +861,15 @@ public abstract class ProviderApiManagerBase { return headerArgs; } - private boolean logOut() { - OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(new JSONObject()); + private boolean logOut(Provider provider) { + OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), new JSONObject()); if (okHttpClient == null) { return false; } - String deleteUrl = providerApiUrl + "/logout"; - int progress = 0; + String deleteUrl = provider.getApiUrlString() + "/logout"; if (ProviderApiConnector.delete(okHttpClient, deleteUrl)) { - broadcastProgress(progress++); LeapSRPSession.setToken(""); return true; } diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java index 25dca4e0..a309bdf9 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java @@ -20,17 +20,20 @@ import android.widget.TextView; import butterknife.InjectView; import butterknife.OnClick; -import se.leap.bitmaskclient.userstatus.SessionDialog; -import se.leap.bitmaskclient.userstatus.SessionDialog.ERRORS; +import se.leap.bitmaskclient.Constants.CREDENTIAL_ERRORS; import se.leap.bitmaskclient.userstatus.User; import static android.view.View.GONE; import static android.view.View.VISIBLE; import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE; -import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_API_EVENT; -import static se.leap.bitmaskclient.ProviderAPI.RESULT_CODE; -import static se.leap.bitmaskclient.ProviderAPI.RESULT_KEY; -import static se.leap.bitmaskclient.userstatus.SessionDialog.USERNAME; +import static se.leap.bitmaskclient.Constants.BROADCAST_PROVIDER_API_EVENT; +import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; +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.ProviderAPI.DOWNLOAD_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.LOG_IN; +import static se.leap.bitmaskclient.ProviderAPI.SIGN_UP; /** * Base Activity for activities concerning a provider interaction @@ -84,7 +87,7 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc setContentView(R.layout.a_provider_credentials); providerAPIBroadcastReceiver = new ProviderAPIBroadcastReceiver(); - IntentFilter updateIntentFilter = new IntentFilter(PROVIDER_API_EVENT); + IntentFilter updateIntentFilter = new IntentFilter(BROADCAST_PROVIDER_API_EVENT); updateIntentFilter.addCategory(Intent.CATEGORY_DEFAULT); registerReceiver(providerAPIBroadcastReceiver, updateIntentFilter); @@ -178,34 +181,27 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc void login(String username, String password) { User.setUserName(username); - Intent providerAPICommand = new Intent(this, ProviderAPI.class); - Bundle parameters = bundlePassword(password); - providerAPICommand.setAction(ProviderAPI.LOG_IN); - providerAPICommand.putExtra(ProviderAPI.PARAMETERS, parameters); - startService(providerAPICommand); + Bundle parameters = bundleUsernameAndPassword(username, password); + ProviderAPICommand.execute(this, LOG_IN, parameters, provider); } public void signUp(String username, String password) { User.setUserName(username); - Intent providerAPICommand = new Intent(this, ProviderAPI.class); - Bundle parameters = bundlePassword(password); - providerAPICommand.setAction(ProviderAPI.SIGN_UP); - providerAPICommand.putExtra(ProviderAPI.PARAMETERS, parameters); - startService(providerAPICommand); + Bundle parameters = bundleUsernameAndPassword(username, password); + ProviderAPICommand.execute(this, SIGN_UP, parameters, provider); } void downloadVpnCertificate() { - Intent providerAPICommand = new Intent(this, ProviderAPI.class); - providerAPICommand.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE); - providerAPICommand.putExtra(ProviderAPI.PARAMETERS, Bundle.EMPTY); - startService(providerAPICommand); + ProviderAPICommand.execute(this, DOWNLOAD_CERTIFICATE, provider); } - protected Bundle bundlePassword(String password) { + protected Bundle bundleUsernameAndPassword(String username, String password) { Bundle parameters = new Bundle(); + if (!username.isEmpty()) + parameters.putString(CREDENTIALS_USERNAME, username); if (!password.isEmpty()) - parameters.putString(SessionDialog.PASSWORD, password); + parameters.putString(CREDENTIALS_PASSWORD, password); return parameters; } @@ -316,17 +312,17 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc } private void handleReceivedErrors(Bundle arguments) { - if (arguments.containsKey(ERRORS.PASSWORD_INVALID_LENGTH.toString())) + if (arguments.containsKey(CREDENTIAL_ERRORS.PASSWORD_INVALID_LENGTH.toString())) passwordError.setError(getString(R.string.error_not_valid_password_user_message)); - else if (arguments.containsKey(ERRORS.RISEUP_WARNING.toString())) { + else if (arguments.containsKey(CREDENTIAL_ERRORS.RISEUP_WARNING.toString())) { userMessage.setVisibility(VISIBLE); userMessage.setText(R.string.login_riseup_warning); } - if (arguments.containsKey(USERNAME)) { - String username = arguments.getString(USERNAME); + if (arguments.containsKey(CREDENTIALS_USERNAME)) { + String username = arguments.getString(CREDENTIALS_USERNAME); usernameField.setText(username); } - if (arguments.containsKey(ERRORS.USERNAME_MISSING.toString())) { + if (arguments.containsKey(CREDENTIAL_ERRORS.USERNAME_MISSING.toString())) { usernameError.setError(getString(R.string.username_ask)); } if (arguments.containsKey(getString(R.string.user_message))) { @@ -356,11 +352,11 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc Log.d(TAG, "received Broadcast"); String action = intent.getAction(); - if (action == null || !action.equalsIgnoreCase(PROVIDER_API_EVENT)) { + if (action == null || !action.equalsIgnoreCase(BROADCAST_PROVIDER_API_EVENT)) { return; } - int resultCode = intent.getIntExtra(RESULT_CODE, -1); + int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, -1); switch (resultCode) { case ProviderAPI.SUCCESSFUL_SIGNUP: case ProviderAPI.SUCCESSFUL_LOGIN: @@ -368,7 +364,7 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc break; case ProviderAPI.FAILED_LOGIN: case ProviderAPI.FAILED_SIGNUP: - handleReceivedErrors((Bundle) intent.getParcelableExtra(RESULT_KEY)); + handleReceivedErrors((Bundle) intent.getParcelableExtra(BROADCAST_RESULT_KEY)); break; case ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE: diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java index aee00765..c89e7a49 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java @@ -32,6 +32,7 @@ import android.widget.ListView; import com.pedrogomez.renderers.Renderer; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.json.JSONException; import org.json.JSONObject; @@ -46,18 +47,19 @@ import se.leap.bitmaskclient.fragments.AboutFragment; import static android.view.View.GONE; import static se.leap.bitmaskclient.Constants.APP_ACTION_QUIT; -import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; +import static se.leap.bitmaskclient.Constants.BROADCAST_PROVIDER_API_EVENT; +import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; +import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_CONFIGURE_LEAP; import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.ERRORS; import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE; -import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_API_EVENT; import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_NOK; import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_OK; import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_SET_UP; -import static se.leap.bitmaskclient.ProviderAPI.RESULT_CODE; -import static se.leap.bitmaskclient.ProviderAPI.RESULT_KEY; +import static se.leap.bitmaskclient.ProviderAPI.UPDATE_PROVIDER_DETAILS; /** * abstract base Activity that builds and shows the list of known available providers. @@ -100,7 +102,7 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity private boolean isActivityShowing; private String reasonToFail; - public abstract void retrySetUpProvider(); + public abstract void retrySetUpProvider(Provider provider); protected abstract void onItemSelectedLogic(); @@ -116,7 +118,7 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity @Override protected void onSaveInstanceState(@NotNull Bundle outState) { outState.putString(ACTIVITY_STATE, mConfigState.getAction()); - outState.putParcelable(Provider.KEY, provider); + outState.putParcelable(PROVIDER_KEY, provider); DialogFragment dialogFragment = (DialogFragment) fragmentManager.findFragmentByTag(DownloadFailedDialog.TAG); if (dialogFragment != null) { @@ -139,7 +141,6 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity if (savedInstanceState != null) restoreState(savedInstanceState); - setUpProviderAPIResultReceiver(); } private void restoreState(Bundle savedInstanceState) { @@ -163,6 +164,7 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity protected void onResume() { Log.d(TAG, "resuming with ConfigState: " + mConfigState.getAction()); super.onResume(); + setUpProviderAPIResultReceiver(); hideProgressBar(); isActivityShowing = true; if (SETTING_UP_PROVIDER.equals(mConfigState.getAction())) { @@ -185,13 +187,13 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity protected void onPause() { super.onPause(); isActivityShowing = false; + if (providerAPIBroadcastReceiver != null) + unregisterReceiver(providerAPIBroadcastReceiver); } @Override protected void onDestroy() { super.onDestroy(); - if (providerAPIBroadcastReceiver != null) - unregisterReceiver(providerAPIBroadcastReceiver); providerAPIResultReceiver = null; } @@ -208,25 +210,17 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity private void setUpProviderAPIResultReceiver() { providerAPIResultReceiver = new ProviderAPIResultReceiver(new Handler(), this); providerAPIBroadcastReceiver = new ProviderAPIBroadcastReceiver(); - IntentFilter updateIntentFilter = new IntentFilter(PROVIDER_API_EVENT); + + IntentFilter updateIntentFilter = new IntentFilter(BROADCAST_PROVIDER_API_EVENT); updateIntentFilter.addCategory(Intent.CATEGORY_DEFAULT); registerReceiver(providerAPIBroadcastReceiver, updateIntentFilter); } - void handleProviderSetUp() { - try { - String providerJsonString = preferences.getString(Provider.KEY, ""); - if (!providerJsonString.isEmpty()) - provider.define(new JSONObject(providerJsonString)); - String caCert = preferences.getString(Provider.CA_CERT, ""); - provider.setCACert(caCert); - } catch (JSONException e) { - e.printStackTrace(); - } + void handleProviderSetUp(Provider handledProvider) { + this.provider = handledProvider; - if (preferences.getBoolean(PROVIDER_ALLOW_ANONYMOUS, false)) { + if (provider.allowsAnonymous()) { mConfigState.putExtra(SERVICES_RETRIEVED, true); - downloadVpnCertificate(); } else { showProviderDetails(); @@ -235,7 +229,6 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity void handleProviderSetupFailed(Bundle resultData) { mConfigState.setAction(PROVIDER_NOT_SET); - preferences.edit().remove(Provider.KEY).apply(); setResult(RESULT_CANCELED, mConfigState); @@ -256,7 +249,8 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity @Override public void onReceiveResult(int resultCode, Bundle resultData) { if (resultCode == ProviderAPI.PROVIDER_OK) { - handleProviderSetUp(); + Provider provider = resultData.getParcelable(PROVIDER_KEY); + handleProviderSetUp(provider); } else if (resultCode == AboutFragment.VIEWED) { // Do nothing, right now // I need this for CW to wait for the About activity to end before going back to Dashboard. @@ -304,27 +298,20 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity public void cancelSettingUpProvider() { hideProgressBar(); mConfigState.setAction(PROVIDER_NOT_SET); - preferences.edit().remove(Provider.KEY).remove(PROVIDER_ALLOW_ANONYMOUS).remove(PROVIDER_KEY).apply(); } @Override public void updateProviderDetails() { mConfigState.setAction(SETTING_UP_PROVIDER); - Intent providerAPICommand = new Intent(this, ProviderAPI.class); - providerAPICommand.setAction(ProviderAPI.UPDATE_PROVIDER_DETAILS); Bundle parameters = new Bundle(); parameters.putString(Provider.MAIN_URL, provider.getMainUrl().toString()); - providerAPICommand.putExtra(ProviderAPI.PARAMETERS, parameters); - startService(providerAPICommand); + ProviderAPICommand.execute(this, UPDATE_PROVIDER_DETAILS, parameters, provider); } public void checkProviderSetUp() { - Intent providerAPICommand = new Intent(this, ProviderAPI.class); - providerAPICommand.setAction(PROVIDER_SET_UP); - providerAPICommand.putExtra(ProviderAPI.RECEIVER_KEY, providerAPIResultReceiver); - startService(providerAPICommand); + ProviderAPICommand.execute(this, PROVIDER_SET_UP, provider, providerAPIResultReceiver); } private void askDashboardToQuitApp() { @@ -337,29 +324,30 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity * Asks ProviderApiService to download an anonymous (anon) VPN certificate. */ private void downloadVpnCertificate() { - Intent providerAPICommand = new Intent(this, ProviderAPI.class); - providerAPICommand.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE); - startService(providerAPICommand); + ProviderAPICommand.execute(this, DOWNLOAD_CERTIFICATE, provider); } /** * Open the new provider dialog */ public void addAndSelectNewProvider() { - FragmentTransaction fragmentTransaction = fragmentManager.removePreviousFragment(NewProviderDialog.TAG); - new NewProviderDialog().show(fragmentTransaction, NewProviderDialog.TAG); + addAndSelectNewProvider(null); } /** - * Open the new provider dialog with data + * Open the new provider dialog + * @param mainUrl - the main url of the provider to add - if null add a new provider */ - public void addAndSelectNewProvider(String main_url) { + public void addAndSelectNewProvider(@Nullable String mainUrl) { FragmentTransaction fragmentTransaction = fragmentManager.removePreviousFragment(NewProviderDialog.TAG); DialogFragment newFragment = new NewProviderDialog(); - Bundle data = new Bundle(); - data.putString(Provider.MAIN_URL, main_url); - newFragment.setArguments(data); + + if (mainUrl != null) { + Bundle data = new Bundle(); + data.putString(Provider.MAIN_URL, mainUrl); + newFragment.setArguments(data); + } newFragment.show(fragmentTransaction, NewProviderDialog.TAG); } @@ -422,51 +410,33 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity Log.d(TAG, "received Broadcast"); String action = intent.getAction(); - if (action == null || !action.equalsIgnoreCase(PROVIDER_API_EVENT)) { + if (action == null || !action.equalsIgnoreCase(BROADCAST_PROVIDER_API_EVENT)) { return; } if (mConfigState.getAction() != null && mConfigState.getAction().equalsIgnoreCase(SETTING_UP_PROVIDER)) { - int resultCode = intent.getIntExtra(RESULT_CODE, -1); + int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, -1); Log.d(TAG, "Broadcast resultCode: " + Integer.toString(resultCode)); - Bundle resultData = intent.getParcelableExtra(RESULT_KEY); - String handledProvider = resultData.getString(Provider.KEY); - - String providerName = ConfigHelper.getProviderName(handledProvider); - String providerDomain = ConfigHelper.getProviderDomain(handledProvider); - - //FIXME: remove that lines as soon as Provider gets sent via broadcast - // and make sure providers are the same - remove providersMatch - if (resultCode == PROVIDER_OK && handledProvider == null) { - providerName = ConfigHelper.getProviderName(preferences); - providerDomain = ConfigHelper.getProviderDomain(preferences); - } - boolean providersMatch = true; - if (providerDomain != null) { - providersMatch = providerDomain.equalsIgnoreCase(provider.getDomain()); - } - if (providerName != null && !providersMatch) { - providersMatch = providerName.equalsIgnoreCase(provider.getName()); - } - + Bundle resultData = intent.getParcelableExtra(BROADCAST_RESULT_KEY); + Provider handledProvider = resultData.getParcelable(PROVIDER_KEY); - switch (resultCode) { - case PROVIDER_OK: - if (providersMatch) - handleProviderSetUp(); - break; - case PROVIDER_NOK: - if(providersMatch) + if (handledProvider != null && handledProvider.getDomain().equalsIgnoreCase(provider.getDomain())) { + switch (resultCode) { + case PROVIDER_OK: + handleProviderSetUp(handledProvider); + break; + case PROVIDER_NOK: handleProviderSetupFailed(resultData); - break; - case CORRECTLY_DOWNLOADED_CERTIFICATE: - handleCorrectlyDownloadedCertificate(); - break; - case INCORRECTLY_DOWNLOADED_CERTIFICATE: - handleIncorrectlyDownloadedCertificate(); - break; + break; + case CORRECTLY_DOWNLOADED_CERTIFICATE: + handleCorrectlyDownloadedCertificate(); + break; + case INCORRECTLY_DOWNLOADED_CERTIFICATE: + handleIncorrectlyDownloadedCertificate(); + break; + } } } } diff --git a/app/src/main/java/se/leap/bitmaskclient/StartActivity.java b/app/src/main/java/se/leap/bitmaskclient/StartActivity.java index f5991538..616264f9 100644 --- a/app/src/main/java/se/leap/bitmaskclient/StartActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/StartActivity.java @@ -69,7 +69,6 @@ public class StartActivity extends Activity { } // initialize app necessities - ProviderAPICommand.initialize(getApplicationContext()); VpnStatus.initLogCache(getApplicationContext().getCacheDir()); User.init(getString(R.string.default_username)); diff --git a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java index 090e8d26..64d1bb72 100644 --- a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java @@ -1,7 +1,6 @@ package se.leap.bitmaskclient.drawer; -import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; @@ -137,19 +136,11 @@ public class NavigationDrawerFragment extends Fragment { } }); - - accountListAdapter = new ArrayAdapter<>(actionBar.getThemedContext(), android.R.layout.simple_list_item_activated_1, android.R.id.text1); - String providerName = ConfigHelper.getProviderName(preferences); - if (providerName == null) { - //TODO: ADD A header to the ListView containing a useful message. - //TODO 2: disable switchProvider - } else { - accountListAdapter.add(providerName); - } + createListAdapterData(); mDrawerAccountsListView.setAdapter(accountListAdapter); @@ -226,16 +217,6 @@ public class NavigationDrawerFragment extends Fragment { } @Override - public void onAttach(Context context) { - super.onAttach(context); - } - - @Override - public void onDetach() { - super.onDetach(); - } - - @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); } @@ -301,10 +282,7 @@ public class NavigationDrawerFragment extends Fragment { Log.d("Drawer", String.format("Selected position %d", position)); switch (position) { case 0: - // TODO STOP VPN - // if (provider.hasEIP()) eip_fragment.stopEipIfPossible(); - preferences.edit().clear().apply(); - startActivityForResult(new Intent(getActivity(), ProviderListActivity.class), REQUEST_CODE_SWITCH_PROVIDER); + getActivity().startActivityForResult(new Intent(getActivity(), ProviderListActivity.class), REQUEST_CODE_SWITCH_PROVIDER); break; case 1: mTitle = getString(R.string.log_fragment_title); @@ -337,4 +315,21 @@ public class NavigationDrawerFragment extends Fragment { } + public void refresh() { + createListAdapterData(); + accountListAdapter.notifyDataSetChanged(); + mDrawerAccountsListView.setAdapter(accountListAdapter); + } + + private void createListAdapterData() { + accountListAdapter.clear(); + String providerName = ConfigHelper.getProviderName(preferences); + if (providerName == null) { + //TODO: ADD A header to the ListView containing a useful message. + //TODO 2: disable switchProvider + } else { + accountListAdapter.add(providerName); + } + } + } 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 eca5b881..e53d81d9 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -30,6 +30,9 @@ import org.json.JSONObject; import de.blinkt.openvpn.LaunchVPN; import se.leap.bitmaskclient.OnBootReceiver; +import static se.leap.bitmaskclient.Constants.BROADCAST_EIP_EVENT; +import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; +import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; import static se.leap.bitmaskclient.Constants.EIP_ACTION_CHECK_CERT_VALIDITY; import static se.leap.bitmaskclient.Constants.EIP_ACTION_IS_RUNNING; import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; @@ -123,9 +126,9 @@ public final class EIP extends IntentService { gateway = gatewaysManager.select(); if (gateway != null && gateway.getProfile() != null) { launchActiveGateway(); - tellToReceiver(EIP_ACTION_START, Activity.RESULT_OK); + tellToReceiverOrBroadcast(EIP_ACTION_START, Activity.RESULT_OK); } else - tellToReceiver(EIP_ACTION_START, Activity.RESULT_CANCELED); + tellToReceiverOrBroadcast(EIP_ACTION_START, Activity.RESULT_CANCELED); } /** @@ -173,7 +176,7 @@ public final class EIP extends IntentService { if (eipStatus.isConnected() || eipStatus.isConnecting()) resultCode = Activity.RESULT_OK; - tellToReceiver(EIP_ACTION_STOP, resultCode); + tellToReceiverOrBroadcast(EIP_ACTION_STOP, resultCode); } /** @@ -186,7 +189,7 @@ public final class EIP extends IntentService { int resultCode = (eipStatus.isConnected()) ? Activity.RESULT_OK : Activity.RESULT_CANCELED; - tellToReceiver(EIP_ACTION_IS_RUNNING, resultCode); + tellToReceiverOrBroadcast(EIP_ACTION_IS_RUNNING, resultCode); } /** @@ -197,7 +200,7 @@ public final class EIP extends IntentService { eipDefinition = eipDefinitionFromPreferences(); if (eipDefinition.length() > 0) updateGateways(); - tellToReceiver(EIP_ACTION_UPDATE, Activity.RESULT_OK); + tellToReceiverOrBroadcast(EIP_ACTION_UPDATE, Activity.RESULT_OK); } private JSONObject eipDefinitionFromPreferences() { @@ -237,14 +240,26 @@ public final class EIP extends IntentService { int resultCode = validator.isValid() ? Activity.RESULT_OK : Activity.RESULT_CANCELED; - tellToReceiver(EIP_ACTION_CHECK_CERT_VALIDITY, resultCode); + tellToReceiverOrBroadcast(EIP_ACTION_CHECK_CERT_VALIDITY, resultCode); } - private void tellToReceiver(String action, int resultCode) { + private void tellToReceiverOrBroadcast(String action, int resultCode) { + Bundle resultData = new Bundle(); + resultData.putString(EIP_REQUEST, action); if (mReceiver != null) { - Bundle resultData = new Bundle(); - resultData.putString(EIP_REQUEST, action); mReceiver.send(resultCode, resultData); + } else { + broadcastEvent(resultCode, resultData); } } + + private void broadcastEvent(int resultCode , Bundle resultData) { + Intent intentUpdate = new Intent(BROADCAST_EIP_EVENT); + intentUpdate.addCategory(Intent.CATEGORY_DEFAULT); + intentUpdate.putExtra(BROADCAST_RESULT_CODE, resultCode); + intentUpdate.putExtra(BROADCAST_RESULT_KEY, resultData); + Log.d(TAG, "sending broadcast"); + sendBroadcast(intentUpdate); + } + } diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java new file mode 100644 index 00000000..35599ab4 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java @@ -0,0 +1,66 @@ +package se.leap.bitmaskclient.eip; + +import android.content.Context; +import android.content.Intent; +import android.os.ResultReceiver; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_UPDATE; +import static se.leap.bitmaskclient.Constants.EIP_RECEIVER; + +/** + * Use this class to send commands to EIP + */ + +public class EipCommand { + + public static void execute(@NotNull Context context, @NotNull String action) { + execute(context, action, null); + } + + /** + * Send a command to EIP + * @param context the context to start the command from + * @param action A valid String constant from EIP class representing an Intent + * filter for the EIP class + * @param resultReceiver The resultreceiver to reply to + */ + public static void execute(@NotNull Context context, @NotNull String action, @Nullable ResultReceiver resultReceiver) { + // TODO validate "action"...how do we get the list of intent-filters for a class via Android API? + Intent vpnIntent = new Intent(context.getApplicationContext(), EIP.class); + vpnIntent.setAction(action); + if (resultReceiver != null) + vpnIntent.putExtra(EIP_RECEIVER, resultReceiver); + context.startService(vpnIntent); + } + + public static void updateEipService(Context context, ResultReceiver resultReceiver) { + execute(context, EIP_ACTION_UPDATE, resultReceiver); + } + + public static void updateEipService(Context context) { + execute(context, EIP_ACTION_UPDATE); + } + + public static void startVPN(Context context) { + execute(context, EIP_ACTION_START); + } + + public static void startVPN(Context context, ResultReceiver resultReceiver) { + execute(context, EIP_ACTION_START, resultReceiver); + } + + + public static void stopVPN(Context context) { + execute(context, EIP_ACTION_STOP); + } + + public static void stopVPN(Context context, ResultReceiver resultReceiver) { + execute(context, EIP_ACTION_STOP, resultReceiver); + } + +} diff --git a/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java b/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java deleted file mode 100644 index 29d4f01d..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java +++ /dev/null @@ -1,179 +0,0 @@ -/** - * Copyright (c) 2013 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.userstatus; - -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.os.Bundle; -import android.support.v4.app.DialogFragment; -import android.support.v7.app.AppCompatActivity; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.EditText; -import android.widget.TextView; - -import butterknife.ButterKnife; -import butterknife.InjectView; -import se.leap.bitmaskclient.Provider; -import se.leap.bitmaskclient.R; - -import static android.view.View.VISIBLE; - -/** - * Implements the log in dialog, currently without progress dialog. - * <p/> - * It returns to the previous fragment when finished, and sends username and password to the authenticate method. - * <p/> - * It also notifies the user if the password is not valid. - * - * @author parmegv - */ -public class SessionDialog extends DialogFragment { - - - final public static String TAG = SessionDialog.class.getSimpleName(); - - final public static String USERNAME = "username"; - final public static String PASSWORD = "password"; - - public enum ERRORS { - USERNAME_MISSING, - PASSWORD_INVALID_LENGTH, - RISEUP_WARNING - } - - @InjectView(R.id.user_message) - TextView userMessage; - @InjectView(R.id.username_entered) - EditText usernameField; - @InjectView(R.id.password_entered) - EditText passwordField; - - public static SessionDialog getInstance(Provider provider, Bundle arguments) { - SessionDialog dialog = new SessionDialog(); - if (provider.getName().equalsIgnoreCase("riseup")) { - arguments = - arguments == Bundle.EMPTY ? - new Bundle() : arguments; - arguments.putBoolean(SessionDialog.ERRORS.RISEUP_WARNING.toString(), true); - } - if (arguments != null && !arguments.isEmpty()) { - dialog.setArguments(arguments); - } - return dialog; - } - - public AlertDialog onCreateDialog(Bundle savedInstanceState) { - - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - LayoutInflater inflater = getActivity().getLayoutInflater(); - View view = inflater.inflate(R.layout.session_dialog, null); - ButterKnife.inject(this, view); - - Bundle arguments = getArguments(); - if (arguments != Bundle.EMPTY && arguments != null) { - setUp(arguments); - } - - builder.setView(view) - .setPositiveButton(R.string.login_button, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - String username = getEnteredUsername(); - String password = getEnteredPassword(); - dialog.dismiss(); - interface_with_Dashboard.logIn(username, password); - } - }) - .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }) - .setNeutralButton(R.string.signup_button, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - String username = getEnteredUsername(); - String password = getEnteredPassword(); - dialog.dismiss(); - interface_with_Dashboard.signUp(username, password); - } - }); - - return builder.create(); - } - - private void setUp(Bundle arguments) { - if (arguments.containsKey(ERRORS.PASSWORD_INVALID_LENGTH.toString())) - passwordField.setError(getString(R.string.error_not_valid_password_user_message)); - else if (arguments.containsKey(ERRORS.RISEUP_WARNING.toString())) { - userMessage.setVisibility(VISIBLE); - userMessage.setText(R.string.login_riseup_warning); - } - if (arguments.containsKey(USERNAME)) { - String username = arguments.getString(USERNAME); - usernameField.setText(username); - } - if (arguments.containsKey(ERRORS.USERNAME_MISSING.toString())) { - usernameField.setError(getString(R.string.username_ask)); - } - if (arguments.containsKey(getString(R.string.user_message))) { - userMessage.setText(arguments.getString(getString(R.string.user_message))); - userMessage.setVisibility(VISIBLE); - } else if (userMessage.getVisibility() != VISIBLE) - userMessage.setVisibility(View.GONE); - - if (!usernameField.getText().toString().isEmpty() && passwordField.isFocusable()) - passwordField.requestFocus(); - - } - - private String getEnteredUsername() { - return usernameField.getText().toString(); - } - - private String getEnteredPassword() { - return passwordField.getText().toString(); - } - - - /** - * Interface used to communicate SessionDialog with Dashboard. - * - * @author parmegv - */ - public interface SessionDialogInterface { - void logIn(String username, String password); - - void signUp(String username, String password); - - } - - SessionDialogInterface interface_with_Dashboard; - - @Override - public void onAttach(Context context) { - super.onAttach(context); - - try { - interface_with_Dashboard = (SessionDialogInterface) ((AppCompatActivity) context).getSupportFragmentManager().getFragments().get(0); - } catch (ClassCastException e) { - throw new ClassCastException(context.toString() - + " must implement LogInDialogListener"); - } - } - -} diff --git a/app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatusFragment.java b/app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatusFragment.java index 0f1d0cdb..70da20ac 100644 --- a/app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatusFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatusFragment.java @@ -17,18 +17,19 @@ import java.util.Observer; import butterknife.ButterKnife; import butterknife.InjectView; import butterknife.OnClick; -import se.leap.bitmaskclient.MainActivity; import se.leap.bitmaskclient.Provider; import se.leap.bitmaskclient.ProviderAPI; import se.leap.bitmaskclient.ProviderAPICommand; import se.leap.bitmaskclient.ProviderAPIResultReceiver; import se.leap.bitmaskclient.R; -public class UserStatusFragment extends Fragment implements Observer, SessionDialog.SessionDialogInterface { +public class UserStatusFragment extends Fragment implements Observer { public static String TAG = UserStatusFragment.class.getSimpleName(); private ProviderAPIResultReceiver providerAPI_result_receiver; + private Provider provider; + @InjectView(R.id.user_status_username) TextView username; @InjectView(R.id.user_status_icon) @@ -87,8 +88,8 @@ public class UserStatusFragment extends Fragment implements Observer, SessionDia android.util.Log.d(TAG, status.toString()); if(status.isLoggedIn()) logOut(); - else if(status.isLoggedOut()) - MainActivity.sessionDialog(Bundle.EMPTY); + //else if(status.isLoggedOut()) + //MainActivity.sessionDialog(Bundle.EMPTY); else if(status.inProgress()) cancelLoginOrSignup(); } @@ -143,33 +144,13 @@ public class UserStatusFragment extends Fragment implements Observer, SessionDia } - @Override - public void signUp(String username, String password) { - User.setUserName(username); - Bundle parameters = bundlePassword(password); - ProviderAPICommand.execute(parameters, ProviderAPI.SIGN_UP, providerAPI_result_receiver); - } - - @Override - public void logIn(String username, String password) { - User.setUserName(username); - Bundle parameters = bundlePassword(password); - ProviderAPICommand.execute(parameters, ProviderAPI.LOG_IN, providerAPI_result_receiver); - } - public void logOut() { android.util.Log.d(TAG, "Log out"); - ProviderAPICommand.execute(Bundle.EMPTY, ProviderAPI.LOG_OUT, providerAPI_result_receiver); + ProviderAPICommand.execute(getActivity(), ProviderAPI.LOG_OUT, provider, providerAPI_result_receiver); } public void cancelLoginOrSignup() { //EipStatus.getInstance().setConnectedOrDisconnected(); } - private Bundle bundlePassword(String password) { - Bundle parameters = new Bundle(); - if (!password.isEmpty()) - parameters.putString(SessionDialog.PASSWORD, password); - return parameters; - } } diff --git a/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java b/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java index a30c9615..a4b3e491 100644 --- a/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java +++ b/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java @@ -33,13 +33,10 @@ import okhttp3.OkHttpClient; import se.leap.bitmaskclient.eip.EIP; import static android.text.TextUtils.isEmpty; -import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOWED_REGISTERED; -import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; -import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; +import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_CERTIFICATE_PINNING; import static se.leap.bitmaskclient.ProviderAPI.ERRORS; -import static se.leap.bitmaskclient.ProviderAPI.RESULT_KEY; import static se.leap.bitmaskclient.R.string.malformed_url; import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_cert; @@ -66,76 +63,42 @@ public class ProviderApiManager extends ProviderApiManagerBase { * Downloads a provider.json from a given URL, adding a new provider using the given name. * * @param task containing a boolean meaning if the provider is custom or not, another boolean meaning if the user completely trusts this provider, the provider name and its provider.json url. - * @return a bundle with a boolean value mapped to a key named RESULT_KEY, and which is true if the update was successful. + * @return a bundle with a boolean value mapped to a key named BROADCAST_RESULT_KEY, and which is true if the update was successful. */ @Override - protected Bundle setUpProvider(Bundle task) { + protected Bundle setUpProvider(Provider provider, Bundle task) { int progress = 0; Bundle currentDownload = new Bundle(); - if (task != null) { - //FIXME: this should be refactored in order to avoid static variables all over here - lastProviderMainUrl = task.containsKey(Provider.MAIN_URL) ? - task.getString(Provider.MAIN_URL) : - ""; - - if (isEmpty(lastProviderMainUrl)) { - currentDownload.putBoolean(RESULT_KEY, false); - setErrorResult(currentDownload, malformed_url, null); - return currentDownload; - } - - //TODO: remove that - providerCaCertFingerprint = task.containsKey(Provider.CA_CERT_FINGERPRINT) ? - task.getString(Provider.CA_CERT_FINGERPRINT) : - ""; - providerCaCert = task.containsKey(Provider.CA_CERT) ? - task.getString(Provider.CA_CERT) : - ""; + if (isEmpty(provider.getMainUrlString()) || provider.getMainUrl().isDefault()) { + currentDownload.putBoolean(BROADCAST_RESULT_KEY, false); + setErrorResult(currentDownload, malformed_url, null); + return currentDownload; + } - try { - providerDefinition = task.containsKey(Provider.KEY) ? - new JSONObject(task.getString(Provider.KEY)) : - new JSONObject(); - } catch (JSONException e) { - e.printStackTrace(); - providerDefinition = new JSONObject(); - } - providerApiUrl = getApiUrlWithVersion(providerDefinition); + getPersistedProviderUpdates(provider); + currentDownload = validateProviderDetails(provider); - checkPersistedProviderUpdates(); - currentDownload = validateProviderDetails(); + //provider details invalid + if (currentDownload.containsKey(ERRORS)) { + return currentDownload; + } - //provider details invalid - if (currentDownload.containsKey(ERRORS)) { - return currentDownload; - } + //no provider certificate available + if (currentDownload.containsKey(BROADCAST_RESULT_KEY) && !currentDownload.getBoolean(BROADCAST_RESULT_KEY)) { + resetProviderDetails(provider); + } - //no provider certificate available - if (currentDownload.containsKey(RESULT_KEY) && !currentDownload.getBoolean(RESULT_KEY)) { - resetProviderDetails(); - } + go_ahead = true; - EIP_SERVICE_JSON_DOWNLOADED = false; - go_ahead = true; + if (!provider.hasDefinition()) { + currentDownload = getAndSetProviderJson(provider); } - - if (!PROVIDER_JSON_DOWNLOADED) - currentDownload = getAndSetProviderJson(lastProviderMainUrl, providerCaCert, providerDefinition); - if (PROVIDER_JSON_DOWNLOADED || (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY))) { - broadcastProgress(++progress); - PROVIDER_JSON_DOWNLOADED = true; - - if (!CA_CERT_DOWNLOADED) - currentDownload = downloadCACert(); - if (CA_CERT_DOWNLOADED || (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY))) { - broadcastProgress(++progress); - CA_CERT_DOWNLOADED = true; - currentDownload = getAndSetEipServiceJson(); - if (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY)) { - broadcastProgress(++progress); - EIP_SERVICE_JSON_DOWNLOADED = true; - } + if (provider.hasDefinition() || (currentDownload.containsKey(BROADCAST_RESULT_KEY) && currentDownload.getBoolean(BROADCAST_RESULT_KEY))) { + if (!provider.hasCaCert()) + currentDownload = downloadCACert(provider); + if (provider.hasCaCert() || (currentDownload.containsKey(BROADCAST_RESULT_KEY) && currentDownload.getBoolean(BROADCAST_RESULT_KEY))) { + currentDownload = getAndSetEipServiceJson(provider); } } @@ -143,14 +106,18 @@ public class ProviderApiManager extends ProviderApiManagerBase { } - private Bundle getAndSetProviderJson(String providerMainUrl, String caCert, JSONObject providerDefinition) { + private Bundle getAndSetProviderJson(Provider provider) { Bundle result = new Bundle(); + String caCert = provider.getCaCert(); + JSONObject providerDefinition = provider.getDefinition(); + if (go_ahead) { String providerDotJsonString; - if(providerDefinition.length() == 0 || caCert.isEmpty()) - providerDotJsonString = downloadWithCommercialCA(providerMainUrl + "/provider.json"); - else { + if(providerDefinition.length() == 0 || caCert.isEmpty()) { + String providerJsonUrl = provider.getMainUrlString() + "/provider.json"; + providerDotJsonString = downloadWithCommercialCA(providerJsonUrl, provider); + } else { providerDotJsonString = downloadFromApiUrlWithProviderCA("/provider.json", caCert, providerDefinition); } @@ -161,20 +128,13 @@ public class ProviderApiManager extends ProviderApiManagerBase { try { JSONObject providerJson = new JSONObject(providerDotJsonString); - String providerDomain = getDomainFromMainURL(lastProviderMainUrl); - providerApiUrl = getApiUrlWithVersion(providerJson); - //String name = providerJson.getString(Provider.NAME); - //TODO setProviderName(name); - - preferences.edit().putString(Provider.KEY, providerJson.toString()). - putBoolean(PROVIDER_ALLOW_ANONYMOUS, providerJson.getJSONObject(Provider.SERVICE).getBoolean(PROVIDER_ALLOW_ANONYMOUS)). - putBoolean(PROVIDER_ALLOWED_REGISTERED, providerJson.getJSONObject(Provider.SERVICE).getBoolean(PROVIDER_ALLOWED_REGISTERED)). - putString(Provider.KEY + "." + providerDomain, providerJson.toString()).commit(); - result.putBoolean(RESULT_KEY, true); + provider.define(providerJson); + + result.putBoolean(BROADCAST_RESULT_KEY, true); } catch (JSONException e) { String reason_to_fail = pickErrorMessage(providerDotJsonString); result.putString(ERRORS, reason_to_fail); - result.putBoolean(RESULT_KEY, false); + result.putBoolean(BROADCAST_RESULT_KEY, false); } } return result; @@ -182,27 +142,27 @@ public class ProviderApiManager extends ProviderApiManagerBase { /** * Downloads the eip-service.json from a given URL, and saves eip service capabilities including the offered gateways - * @return a bundle with a boolean value mapped to a key named RESULT_KEY, and which is true if the download was successful. + * @return a bundle with a boolean value mapped to a key named BROADCAST_RESULT_KEY, and which is true if the download was successful. */ @Override - protected Bundle getAndSetEipServiceJson() { + protected Bundle getAndSetEipServiceJson(Provider provider) { Bundle result = new Bundle(); - String eip_service_json_string = ""; + String eipServiceJsonString = ""; if (go_ahead) { try { - JSONObject provider_json = new JSONObject(preferences.getString(Provider.KEY, "")); - String eip_service_url = provider_json.getString(Provider.API_URL) + "/" + provider_json.getString(Provider.API_VERSION) + "/" + EIP.SERVICE_API_PATH; - eip_service_json_string = downloadWithProviderCA(eip_service_url); - JSONObject eip_service_json = new JSONObject(eip_service_json_string); - eip_service_json.getInt(Provider.API_RETURN_SERIAL); + JSONObject provider_json = provider.getDefinition(); + String eipServiceUrl = provider_json.getString(Provider.API_URL) + "/" + provider_json.getString(Provider.API_VERSION) + "/" + EIP.SERVICE_API_PATH; + eipServiceJsonString = downloadWithProviderCA(provider.getCaCert(), eipServiceUrl); + JSONObject eipServiceJson = new JSONObject(eipServiceJsonString); + eipServiceJson.getInt(Provider.API_RETURN_SERIAL); - preferences.edit().putString(PROVIDER_KEY, eip_service_json.toString()).commit(); + provider.setEipServiceJson(eipServiceJson); - result.putBoolean(RESULT_KEY, true); + result.putBoolean(BROADCAST_RESULT_KEY, true); } catch (NullPointerException | JSONException e) { - String reason_to_fail = pickErrorMessage(eip_service_json_string); + String reason_to_fail = pickErrorMessage(eipServiceJsonString); result.putString(ERRORS, reason_to_fail); - result.putBoolean(RESULT_KEY, false); + result.putBoolean(BROADCAST_RESULT_KEY, false); } } return result; @@ -214,14 +174,13 @@ public class ProviderApiManager extends ProviderApiManagerBase { * @return true if certificate was downloaded correctly, false if provider.json is not present in SharedPreferences, or if the certificate url could not be parsed as a URI, or if there was an SSL error. */ @Override - protected boolean updateVpnCertificate() { + protected boolean updateVpnCertificate(Provider provider) { try { - JSONObject provider_json = new JSONObject(preferences.getString(Provider.KEY, "")); + JSONObject providerJson = provider.getDefinition(); + String provider_main_url = providerJson.getString(Provider.API_URL); + URL newCertStringUrl = new URL(provider_main_url + "/" + providerJson.getString(Provider.API_VERSION) + "/" + PROVIDER_VPN_CERTIFICATE); - String provider_main_url = provider_json.getString(Provider.API_URL); - URL new_cert_string_url = new URL(provider_main_url + "/" + provider_json.getString(Provider.API_VERSION) + "/" + PROVIDER_VPN_CERTIFICATE); - - String cert_string = downloadWithProviderCA(new_cert_string_url.toString()); + String cert_string = downloadWithProviderCA(provider.getCaCert(), newCertStringUrl.toString()); if (ConfigHelper.checkErroneousDownload(cert_string)) return false; @@ -238,23 +197,22 @@ public class ProviderApiManager extends ProviderApiManagerBase { } } - private Bundle downloadCACert() { + private Bundle downloadCACert(Provider provider) { Bundle result = new Bundle(); try { - JSONObject providerJson = new JSONObject(preferences.getString(Provider.KEY, "")); - String caCertUrl = providerJson.getString(Provider.CA_CERT_URI); - String providerDomain = getDomainFromMainURL(lastProviderMainUrl); - String cert_string = downloadWithCommercialCA(caCertUrl); - - if (validCertificate(cert_string) && go_ahead) { - preferences.edit().putString(Provider.CA_CERT, cert_string).commit(); - preferences.edit().putString(Provider.CA_CERT + "." + providerDomain, cert_string).commit(); - result.putBoolean(RESULT_KEY, true); + String caCertUrl = provider.getDefinition().getString(Provider.CA_CERT_URI); + String providerDomain = getDomainFromMainURL(provider.getMainUrlString()); + String certString = downloadWithCommercialCA(caCertUrl, provider); + + if (validCertificate(provider, certString) && go_ahead) { + provider.setCaCert(certString); + preferences.edit().putString(Provider.CA_CERT + "." + providerDomain, certString).apply(); + result.putBoolean(BROADCAST_RESULT_KEY, true); } else { setErrorResult(result, warning_corrupted_provider_cert, ERROR_CERTIFICATE_PINNING.toString()); } } catch (JSONException e) { - setErrorResult(result, malformed_url, null); + e.printStackTrace(); } return result; @@ -263,10 +221,8 @@ public class ProviderApiManager extends ProviderApiManagerBase { /** * Tries to download the contents of the provided url using commercially validated CA certificate from chosen provider. * - * @param string_url - * @return */ - private String downloadWithCommercialCA(String string_url) { + private String downloadWithCommercialCA(String stringUrl, Provider provider) { String responseString; JSONObject errorJson = new JSONObject(); @@ -277,14 +233,14 @@ public class ProviderApiManager extends ProviderApiManagerBase { List<Pair<String, String>> headerArgs = getAuthorizationHeader(); - responseString = sendGetStringToServer(string_url, headerArgs, okHttpClient); + responseString = sendGetStringToServer(stringUrl, headerArgs, okHttpClient); if (responseString != null && responseString.contains(ERRORS)) { try { // try to download with provider CA on certificate error JSONObject responseErrorJson = new JSONObject(responseString); if (responseErrorJson.getString(ERRORS).equals(resources.getString(R.string.certificate_error))) { - responseString = downloadWithProviderCA(string_url); + responseString = downloadWithProviderCA(provider.getCaCert(), stringUrl); } } catch (JSONException e) { e.printStackTrace(); @@ -324,11 +280,11 @@ public class ProviderApiManager extends ProviderApiManagerBase { * @param urlString as a string * @return an empty string if it fails, the url content if not. */ - private String downloadWithProviderCA(String urlString) { + private String downloadWithProviderCA(String caCert, String urlString) { JSONObject initError = new JSONObject(); String responseString; - OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(initError); + OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(caCert, initError); if (okHttpClient == null) { return initError.toString(); } diff --git a/app/src/production/java/se/leap/bitmaskclient/ProviderListActivity.java b/app/src/production/java/se/leap/bitmaskclient/ProviderListActivity.java index 8403b046..67c04f13 100644 --- a/app/src/production/java/se/leap/bitmaskclient/ProviderListActivity.java +++ b/app/src/production/java/se/leap/bitmaskclient/ProviderListActivity.java @@ -22,6 +22,8 @@ import android.os.Bundle; import java.net.MalformedURLException; import java.net.URL; +import static se.leap.bitmaskclient.ProviderAPI.SET_UP_PROVIDER; + /** * Activity that builds and shows the list of known available providers. * <p/> @@ -61,41 +63,18 @@ public class ProviderListActivity extends ProviderListBaseActivity { */ public void setUpProvider() { mConfigState.setAction(SETTING_UP_PROVIDER); - Intent providerApiCommand = new Intent(this, ProviderAPI.class); - Bundle parameters = new Bundle(); - parameters.putString(Provider.MAIN_URL, provider.getMainUrl().toString()); - if (provider.hasCertificatePin()){ - parameters.putString(Provider.CA_CERT_FINGERPRINT, provider.certificatePin()); - } - if (provider.hasCaCert()) { - parameters.putString(Provider.CA_CERT, provider.getCaCert()); - } - if (provider.hasDefinition()) { - parameters.putString(Provider.KEY, provider.getDefinition().toString()); - } - - providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); - providerApiCommand.putExtra(ProviderAPI.PARAMETERS, parameters); - - startService(providerApiCommand); + ProviderAPICommand.execute(this, SET_UP_PROVIDER, provider); } @Override - public void retrySetUpProvider() { + public void retrySetUpProvider(Provider provider) { cancelSettingUpProvider(); - if (!ProviderAPI.caCertDownloaded()) { - addAndSelectNewProvider(ProviderAPI.lastProviderMainUrl()); + if (!provider.hasCaCert()) { + addAndSelectNewProvider(provider.getMainUrlString()); } else { showProgressBar(); - Intent providerApiCommand = new Intent(this, ProviderAPI.class); - providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); - providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, providerAPIResultReceiver); - Bundle parameters = new Bundle(); - parameters.putString(Provider.MAIN_URL, provider.getMainUrl().toString()); - providerApiCommand.putExtra(ProviderAPI.PARAMETERS, parameters); - - startService(providerApiCommand); + ProviderAPICommand.execute(this, SET_UP_PROVIDER, provider); } } diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java index c23e4f49..3ebf6201 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java @@ -24,6 +24,8 @@ import android.content.res.Resources; import android.os.Bundle; import android.text.TextUtils; +import org.json.JSONException; +import org.json.JSONObject; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -34,6 +36,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import java.io.IOException; +import java.net.URL; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateEncodingException; @@ -45,13 +48,13 @@ import se.leap.bitmaskclient.ProviderApiManager; import se.leap.bitmaskclient.ProviderApiManagerBase; import se.leap.bitmaskclient.testutils.MockSharedPreferences; +import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; +import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.ProviderAPI.ERRORS; import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_NOK; import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_OK; -import static se.leap.bitmaskclient.ProviderAPI.RESULT_KEY; import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.ERROR_CASE_UPDATED_CERTIFICATE; import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.NO_ERROR; -import static se.leap.bitmaskclient.testutils.TestSetupHelper.getInputAsString; import static se.leap.bitmaskclient.testutils.MockHelper.mockBundle; import static se.leap.bitmaskclient.testutils.MockHelper.mockClientGenerator; import static se.leap.bitmaskclient.testutils.MockHelper.mockFingerprintForCertificate; @@ -60,6 +63,7 @@ import static se.leap.bitmaskclient.testutils.MockHelper.mockProviderApiConnecto import static se.leap.bitmaskclient.testutils.MockHelper.mockResources; import static se.leap.bitmaskclient.testutils.MockHelper.mockResultReceiver; import static se.leap.bitmaskclient.testutils.MockHelper.mockTextUtils; +import static se.leap.bitmaskclient.testutils.TestSetupHelper.getInputAsString; /** @@ -103,235 +107,273 @@ public class ProviderApiManagerTest { mockResources = mockResources(getClass().getClassLoader().getResourceAsStream("error_messages.json")); } + private Provider getConfiguredProvider() throws IOException, JSONException { + return getProvider(null, null, null); + } + + private Provider getProvider(String domain, String caCertFile, String jsonFile) { + if (domain == null) + domain = "https://riseup.net"; + if (caCertFile == null) + caCertFile = "riseup.net.pem"; + if (jsonFile == null) + jsonFile = "riseup.net.json"; + + try { + return new Provider( + new URL(domain), + getInputAsString(getClass().getClassLoader().getResourceAsStream(caCertFile)), + getInputAsString(getClass().getClassLoader().getResourceAsStream(jsonFile)) + + ); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + @Test - public void test_handleIntentSetupProvider_noProviderMainURL() { + public void test_handleIntentSetupProvider_noProviderMainURL() throws IOException, JSONException { + Provider provider = new Provider(""); + providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); Bundle expectedResult = mockBundle(); - expectedResult.putBoolean(RESULT_KEY, false); - expectedResult.putString(ERRORS, "{\"errors\":\"It doesn't seem to be a Bitmask provider.\"}"); - Intent provider_API_command = mockIntent(); - Bundle parameters = mockBundle(); - parameters.putString(Provider.MAIN_URL, ""); + expectedResult.putBoolean(BROADCAST_RESULT_KEY, false); + expectedResult.putString(ERRORS, "{\"errors\":\"It doesn't seem to be a Bitmask provider.\"}"); + expectedResult.putParcelable(PROVIDER_KEY, provider); - provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); - provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); - provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); + Intent providerApiCommand = mockIntent(); + providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); + providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); + providerApiCommand.putExtra(PROVIDER_KEY, provider); - providerApiManager.handleIntent(provider_API_command); + providerApiManager.handleIntent(providerApiCommand); } @Test - public void test_handleIntentSetupProvider_happyPath_preseededProviderAndCA() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { + public void test_handleIntentSetupProvider_happyPath_preseededProviderAndCA() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { + Provider provider = getConfiguredProvider(); + mockFingerprintForCertificate(" a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494"); mockProviderApiConnector(NO_ERROR); providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); Bundle expectedResult = mockBundle(); - expectedResult.putBoolean(RESULT_KEY, true); - Intent provider_API_command = mockIntent(); - Bundle parameters = mockBundle(); - parameters.putString(Provider.MAIN_URL, "https://riseup.net"); - parameters.putString(Provider.CA_CERT, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"))); - parameters.putString(Provider.KEY, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))); + expectedResult.putBoolean(BROADCAST_RESULT_KEY, true); + expectedResult.putParcelable(PROVIDER_KEY, provider); + + Intent providerApiCommand = mockIntent(); - provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); - provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); - provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_OK, expectedResult)); + providerApiCommand.putExtra(PROVIDER_KEY, provider); + providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); + providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_OK, expectedResult)); - providerApiManager.handleIntent(provider_API_command); + providerApiManager.handleIntent(providerApiCommand); } @Test - public void test_handleIntentSetupProvider_happyPath_no_preseededProviderAndCA() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { + public void test_handleIntentSetupProvider_happyPath_no_preseededProviderAndCA() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { + Provider provider = new Provider("https://riseup.net"); + mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494"); mockProviderApiConnector(NO_ERROR); providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); Bundle expectedResult = mockBundle(); - expectedResult.putBoolean(RESULT_KEY, true); - Intent provider_API_command = mockIntent(); - Bundle parameters = mockBundle(); - parameters.putString(Provider.MAIN_URL, "https://riseup.net"); + expectedResult.putBoolean(BROADCAST_RESULT_KEY, true); + expectedResult.putParcelable(PROVIDER_KEY, provider); + + Intent providerApiCommand = mockIntent(); - provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); - provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); - provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_OK, expectedResult)); + providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); + providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_OK, expectedResult)); + providerApiCommand.putExtra(PROVIDER_KEY, provider); - providerApiManager.handleIntent(provider_API_command); + providerApiManager.handleIntent(providerApiCommand); } @Test - public void test_handleIntentSetupProvider_happyPath_storedProviderAndCAFromPreviousSetup() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { + public void test_handleIntentSetupProvider_happyPath_storedProviderAndCAFromPreviousSetup() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { + Provider provider = new Provider("https://riseup.net"); mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494"); mockProviderApiConnector(NO_ERROR); mockPreferences.edit().putString(Provider.KEY + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))).apply(); mockPreferences.edit().putString(Provider.CA_CERT + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"))).apply(); providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); + Bundle expectedResult = mockBundle(); - expectedResult.putBoolean(RESULT_KEY, true); + expectedResult.putBoolean(BROADCAST_RESULT_KEY, true); + expectedResult.putParcelable(PROVIDER_KEY, provider); - Intent provider_API_command = mockIntent(); - Bundle parameters = mockBundle(); - parameters.putString(Provider.MAIN_URL, "https://riseup.net"); + Intent providerApiCommand = mockIntent(); + providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); + providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_OK, expectedResult)); - provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); - provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); - provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_OK, expectedResult)); + providerApiCommand.putExtra(PROVIDER_KEY, provider); - providerApiManager.handleIntent(provider_API_command); + providerApiManager.handleIntent(providerApiCommand); } @Test - public void test_handleIntentSetupProvider_preseededProviderAndCA_failedCAPinning() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { + public void test_handleIntentSetupProvider_preseededProviderAndCA_failedCAPinning() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { + Provider provider = getConfiguredProvider(); mockFingerprintForCertificate(" a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29495"); mockProviderApiConnector(NO_ERROR); providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); Bundle expectedResult = mockBundle(); - expectedResult.putBoolean(RESULT_KEY, false); + expectedResult.putBoolean(BROADCAST_RESULT_KEY, false); expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_CERTIFICATE_PINNING\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}"); + expectedResult.putParcelable(PROVIDER_KEY, provider); - Intent provider_API_command = mockIntent(); - Bundle parameters = mockBundle(); - parameters.putString(Provider.MAIN_URL, "https://riseup.net"); - parameters.putString(Provider.CA_CERT, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"))); - parameters.putString(Provider.KEY, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))); + Intent providerApiCommand = mockIntent(); - provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); - provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); - provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); + providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); + providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); - providerApiManager.handleIntent(provider_API_command); + providerApiCommand.putExtra(PROVIDER_KEY, provider); + + providerApiManager.handleIntent(providerApiCommand); } @Test - public void test_handleIntentSetupProvider_no_preseededProviderAndCA_failedPinning() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { + public void test_handleIntentSetupProvider_no_preseededProviderAndCA_failedPinning() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { + Provider provider = new Provider("https://riseup.net"); mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29495"); mockProviderApiConnector(NO_ERROR); providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); + Bundle expectedResult = mockBundle(); - expectedResult.putBoolean(RESULT_KEY, false); + expectedResult.putBoolean(BROADCAST_RESULT_KEY, false); expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_CERTIFICATE_PINNING\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}"); + expectedResult.putParcelable(PROVIDER_KEY, provider); - Intent provider_API_command = mockIntent(); - Bundle parameters = mockBundle(); - parameters.putString(Provider.MAIN_URL, "https://riseup.net"); + Intent providerApiCommand = mockIntent(); - provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); - provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); - provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); + providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); + providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); - providerApiManager.handleIntent(provider_API_command); + providerApiCommand.putExtra(PROVIDER_KEY, provider); + + providerApiManager.handleIntent(providerApiCommand); } @Test public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_failedPinning() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { + Provider provider = new Provider("https://riseup.net"); + mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29495"); mockProviderApiConnector(NO_ERROR); mockPreferences.edit().putString(Provider.KEY + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))).apply(); mockPreferences.edit().putString(Provider.CA_CERT + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"))).apply(); providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); + Bundle expectedResult = mockBundle(); - expectedResult.putBoolean(RESULT_KEY, false); + expectedResult.putBoolean(BROADCAST_RESULT_KEY, false); expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_CERTIFICATE_PINNING\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}"); + expectedResult.putParcelable(PROVIDER_KEY, provider); + + Intent providerApiCommand = mockIntent(); - Intent provider_API_command = mockIntent(); - Bundle parameters = mockBundle(); - parameters.putString(Provider.MAIN_URL, "https://riseup.net"); + providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); + providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); - provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); - provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); - provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); + providerApiCommand.putExtra(PROVIDER_KEY, provider); - providerApiManager.handleIntent(provider_API_command); + providerApiManager.handleIntent(providerApiCommand); } @Test - public void test_handleIntentSetupProvider_preseededProviderAndCA_outdatedCertificate() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { + public void test_handleIntentSetupProvider_preseededProviderAndCA_outdatedCertificate() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { + Provider provider = getProvider(null ,"outdated_cert.pem", null); mockProviderApiConnector(NO_ERROR); providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); + Bundle expectedResult = mockBundle(); - expectedResult.putBoolean(RESULT_KEY, false); + expectedResult.putBoolean(BROADCAST_RESULT_KEY, false); expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_INVALID_CERTIFICATE\",\"errors\":\"Stored provider certificate is expired. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}"); + expectedResult.putParcelable(PROVIDER_KEY, provider); + + Intent providerApiCommand = mockIntent(); - Intent provider_API_command = mockIntent(); - Bundle parameters = mockBundle(); - parameters.putString(Provider.MAIN_URL, "https://riseup.net"); - parameters.putString(Provider.CA_CERT, getInputAsString(getClass().getClassLoader().getResourceAsStream("outdated_cert.pem"))); - parameters.putString(Provider.KEY, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))); + providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); + providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); - provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); - provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); - provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); + providerApiCommand.putExtra(PROVIDER_KEY, provider); - providerApiManager.handleIntent(provider_API_command); + providerApiManager.handleIntent(providerApiCommand); } @Test - public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_outdatedCertificate() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { + public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_outdatedCertificate() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { + Provider provider = new Provider("https://riseup.net"); mockProviderApiConnector(NO_ERROR); mockPreferences.edit().putString(Provider.KEY + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))).apply(); mockPreferences.edit().putString(Provider.CA_CERT + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("outdated_cert.pem"))).apply(); providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); + Bundle expectedResult = mockBundle(); - expectedResult.putBoolean(RESULT_KEY, false); + expectedResult.putBoolean(BROADCAST_RESULT_KEY, false); expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_INVALID_CERTIFICATE\",\"errors\":\"Stored provider certificate is expired. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}"); + expectedResult.putParcelable(PROVIDER_KEY, provider); + + Intent providerApiCommand = mockIntent(); - Intent provider_API_command = mockIntent(); - Bundle parameters = mockBundle(); - parameters.putString(Provider.MAIN_URL, "https://riseup.net"); + providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); + providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); - provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); - provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); - provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); + providerApiCommand.putExtra(PROVIDER_KEY, provider); - providerApiManager.handleIntent(provider_API_command); + providerApiManager.handleIntent(providerApiCommand); } @Test - public void test_handleIntentSetupProvider_preseededProviderAndCA_ValidCertificateButUpdatedCertificateOnServerSide() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { + public void test_handleIntentSetupProvider_preseededProviderAndCA_ValidCertificateButUpdatedCertificateOnServerSide() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { + Provider provider = getConfiguredProvider(); + mockFingerprintForCertificate(" a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494"); mockProviderApiConnector(ERROR_CASE_UPDATED_CERTIFICATE); providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); Bundle expectedResult = mockBundle(); - expectedResult.putBoolean(RESULT_KEY, false); + expectedResult.putBoolean(BROADCAST_RESULT_KEY, false); expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_INVALID_CERTIFICATE\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}"); + expectedResult.putParcelable(PROVIDER_KEY, provider); + + Intent providerApiCommand = mockIntent(); - Intent provider_API_command = mockIntent(); - Bundle parameters = mockBundle(); - parameters.putString(Provider.MAIN_URL, "https://riseup.net"); - parameters.putString(Provider.CA_CERT, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"))); - parameters.putString(Provider.KEY, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))); + providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); + providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); - provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); - provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); - provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); + providerApiCommand.putExtra(PROVIDER_KEY, provider); - providerApiManager.handleIntent(provider_API_command); + providerApiManager.handleIntent(providerApiCommand); } @Test - public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_ValidCertificateButUpdatedCertificateOnServerSide() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { + public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_ValidCertificateButUpdatedCertificateOnServerSide() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { + Provider provider = new Provider("https://riseup.net"); + mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494"); mockProviderApiConnector(ERROR_CASE_UPDATED_CERTIFICATE); mockPreferences.edit().putString(Provider.KEY + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))).apply(); mockPreferences.edit().putString(Provider.CA_CERT + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"))).apply(); providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); + Bundle expectedResult = mockBundle(); - expectedResult.putBoolean(RESULT_KEY, false); + expectedResult.putBoolean(BROADCAST_RESULT_KEY, false); expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_INVALID_CERTIFICATE\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}"); + expectedResult.putParcelable(PROVIDER_KEY, provider); + + Intent providerApiCommand = mockIntent(); - Intent provider_API_command = mockIntent(); - Bundle parameters = mockBundle(); - parameters.putString(Provider.MAIN_URL, "https://riseup.net"); + providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); + providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); - provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); - provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); - provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); + providerApiCommand.putExtra(PROVIDER_KEY, provider); - providerApiManager.handleIntent(provider_API_command); + providerApiManager.handleIntent(providerApiCommand); } } 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 8372c9bc..ea7f9aae 100644 --- a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java +++ b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java @@ -353,7 +353,7 @@ public class MockHelper { OkHttpClientGenerator mockClientGenerator = mock(OkHttpClientGenerator.class); OkHttpClient mockedOkHttpClient = mock(OkHttpClient.class); when(mockClientGenerator.initCommercialCAHttpClient(any(JSONObject.class))).thenReturn(mockedOkHttpClient); - when(mockClientGenerator.initSelfSignedCAHttpClient(any(JSONObject.class))).thenReturn(mockedOkHttpClient); + when(mockClientGenerator.initSelfSignedCAHttpClient(anyString(), any(JSONObject.class))).thenReturn(mockedOkHttpClient); when(mockClientGenerator.initSelfSignedCAHttpClient(any(JSONObject.class), anyString())).thenReturn(mockedOkHttpClient); return mockClientGenerator; } |