diff options
Diffstat (limited to 'app')
28 files changed, 942 insertions, 1170 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..86250a6c 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)) { + if (isEmpty(provider.getMainUrlString())) { setErrorResult(currentDownload, malformed_url, null); + currentDownload.putParcelable(PROVIDER_KEY, provider); 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); - - checkPersistedProviderUpdates(); - currentDownload = validateProviderDetails(); + getPersistedProviderUpdates(provider); + currentDownload = validateProviderDetails(provider); //provider details invalid if (currentDownload.containsKey(ERRORS)) { + currentDownload.putParcelable(PROVIDER_KEY, provider); return currentDownload; } //no provider certificate available - if (currentDownload.containsKey(RESULT_KEY) && !currentDownload.getBoolean(RESULT_KEY)) { - resetProviderDetails(); + if (currentDownload.containsKey(BROADCAST_RESULT_KEY) && !currentDownload.getBoolean(BROADCAST_RESULT_KEY)) { + resetProviderDetails(provider); } - EIP_SERVICE_JSON_DOWNLOADED = false; go_ahead = true; } - 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; - } + 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,57 @@ 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); + +// 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(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 +201,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 +226,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()); } @@ -344,11 +320,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(); } 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 034c9752..2fdb5b02 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,46 +88,26 @@ 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(); adapter.hideAllBut(adapter.indexOf(provider)); - 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..cdb314aa 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,54 @@ 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)); - // 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)); - } + setProviderHeaderText(provider.getName()); + description.setText(provider.getDescription()); - 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 + // 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 +97,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..9ed7a178 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. @@ -273,7 +277,7 @@ public class ConfigHelper { try { provider.setUrl(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..e02ce6a9 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)); @@ -159,7 +146,7 @@ public class Dashboard extends ButterKnifeActivity { try { provider.setUrl(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 527ce1a7..9d413a8d 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 414b1f2a..9726ebf2 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 @@ -132,6 +156,8 @@ public class EipFragment extends Fragment implements Observer { eipStatus = EipStatus.getInstance(); eipStatus.addObserver(this); 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 @@ -199,8 +228,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 @@ -250,7 +279,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() { @@ -282,13 +311,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() { @@ -310,25 +333,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) { @@ -401,58 +405,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; + } } } @@ -474,4 +501,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 ae329cd1..ca28eacd 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", @@ -141,6 +151,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 +183,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 +227,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 +245,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 +296,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,13 +315,39 @@ 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(); + } } 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 cfc6e49d..7e4acf44 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,92 +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(); - task.putString(MAIN_URL, lastProviderMainUrl); - Bundle result = setUpProvider(task); - if (result.getBoolean(RESULT_KEY)) { - receiver.send(PROVIDER_OK, result); + Bundle result = setUpProvider(provider, task); + if (result.getBoolean(BROADCAST_RESULT_KEY)) { + sendToReceiverOrBroadcast(receiver, PROVIDER_OK, result, provider); } else { - receiver.send(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) { - receiver.send(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)); } @@ -252,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) { @@ -260,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); @@ -274,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); } @@ -309,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; @@ -326,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); } @@ -374,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) { @@ -424,17 +414,21 @@ 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); } } @@ -451,11 +445,11 @@ public abstract class ProviderApiManagerBase { 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); } @@ -624,27 +618,23 @@ 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. + * @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 { @@ -658,19 +648,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) { @@ -681,50 +671,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()); } @@ -732,9 +713,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()); } @@ -743,7 +724,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 ) { @@ -754,11 +735,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); @@ -766,7 +747,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; } @@ -807,17 +788,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(); @@ -903,17 +874,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 2b0c72db..ceb7a3c4 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java @@ -33,6 +33,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; @@ -47,18 +48,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. @@ -101,7 +103,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(); @@ -117,7 +119,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) { @@ -140,7 +142,6 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity if (savedInstanceState != null) restoreState(savedInstanceState); - setUpProviderAPIResultReceiver(); } private void restoreState(Bundle savedInstanceState) { @@ -164,6 +165,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())) { @@ -187,13 +189,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; } @@ -211,25 +213,16 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity 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(); @@ -238,7 +231,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); @@ -259,7 +251,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. @@ -303,27 +296,20 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity hideProgressBar(); mConfigState.setAction(PROVIDER_NOT_SET); adapter.showAllProviders(); - 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() { @@ -336,29 +322,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); } @@ -436,40 +423,26 @@ 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); + Bundle resultData = intent.getParcelableExtra(BROADCAST_RESULT_KEY); + Provider handledProvider = resultData.getParcelable(PROVIDER_KEY); - //FIXME: remove that lines as soon as Provider gets sent via broadcast - if (resultCode == PROVIDER_OK && handledProvider == null) { - providerName = ConfigHelper.getProviderName(preferences); - providerDomain = ConfigHelper.getProviderDomain(preferences); - } - - if (providerName != null && providerName.equalsIgnoreCase(provider.getName()) && - providerDomain != null && - providerDomain.equalsIgnoreCase(provider.getDomain())) { + if (handledProvider != null && handledProvider.getDomain().equalsIgnoreCase(provider.getDomain())) { switch (resultCode) { case PROVIDER_OK: - handleProviderSetUp(); + handleProviderSetUp(handledProvider); break; case PROVIDER_NOK: handleProviderSetupFailed(resultData); break; - } - } else { - switch (resultCode) { case CORRECTLY_DOWNLOADED_CERTIFICATE: handleCorrectlyDownloadedCertificate(); 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..ac58d005 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,45 +63,23 @@ 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); + String mainUrlString = provider.getMainUrlString(); + if (isEmpty(mainUrlString)) { + currentDownload.putBoolean(BROADCAST_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) : - ""; - - 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); - - checkPersistedProviderUpdates(); - currentDownload = validateProviderDetails(); + getPersistedProviderUpdates(provider); + currentDownload = validateProviderDetails(provider); //provider details invalid if (currentDownload.containsKey(ERRORS)) { @@ -112,29 +87,26 @@ public class ProviderApiManager extends ProviderApiManagerBase { } //no provider certificate available - if (currentDownload.containsKey(RESULT_KEY) && !currentDownload.getBoolean(RESULT_KEY)) { - resetProviderDetails(); + if (currentDownload.containsKey(BROADCAST_RESULT_KEY) && !currentDownload.getBoolean(BROADCAST_RESULT_KEY)) { + resetProviderDetails(provider); } - EIP_SERVICE_JSON_DOWNLOADED = false; go_ahead = true; } - if (!PROVIDER_JSON_DOWNLOADED) - currentDownload = getAndSetProviderJson(lastProviderMainUrl, providerCaCert, providerDefinition); - if (PROVIDER_JSON_DOWNLOADED || (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY))) { + if (!provider.hasDefinition()) { + currentDownload = getAndSetProviderJson(provider); + } + if (provider.hasDefinition() || (currentDownload.containsKey(BROADCAST_RESULT_KEY) && currentDownload.getBoolean(BROADCAST_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))) { + if (!provider.hasCaCert()) + currentDownload = downloadCACert(provider); + if (provider.hasCaCert() || (currentDownload.containsKey(BROADCAST_RESULT_KEY) && currentDownload.getBoolean(BROADCAST_RESULT_KEY))) { broadcastProgress(++progress); - CA_CERT_DOWNLOADED = true; - currentDownload = getAndSetEipServiceJson(); - if (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY)) { + currentDownload = getAndSetEipServiceJson(provider); + if (currentDownload.containsKey(BROADCAST_RESULT_KEY) && currentDownload.getBoolean(BROADCAST_RESULT_KEY)) { broadcastProgress(++progress); - EIP_SERVICE_JSON_DOWNLOADED = true; } } } @@ -143,13 +115,16 @@ 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"); + providerDotJsonString = downloadWithCommercialCA(provider); else { providerDotJsonString = downloadFromApiUrlWithProviderCA("/provider.json", caCert, providerDefinition); } @@ -161,20 +136,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 +150,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 +182,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, "")); - - 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); + 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 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 +205,17 @@ 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); - } else { - setErrorResult(result, warning_corrupted_provider_cert, ERROR_CERTIFICATE_PINNING.toString()); - } - } catch (JSONException e) { - setErrorResult(result, malformed_url, null); + String providerDomain = getDomainFromMainURL(provider.getMainUrlString()); + String certString = downloadWithCommercialCA(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()); } return result; @@ -263,10 +224,9 @@ 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(Provider provider) { + String stringUrl = provider.getMainUrlString() + "/provider.json"; String responseString; JSONObject errorJson = new JSONObject(); @@ -277,14 +237,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 +284,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 8c008024..921b42e1 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,43 +63,19 @@ 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(); adapter.hideAllBut(adapter.indexOf(provider)); - - 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..aeb7e8c1 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java @@ -38,6 +38,7 @@ import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateEncodingException; import se.leap.bitmaskclient.ConfigHelper; +import se.leap.bitmaskclient.Constants; import se.leap.bitmaskclient.Provider; import se.leap.bitmaskclient.ProviderAPI; import se.leap.bitmaskclient.ProviderApiConnector; @@ -45,10 +46,10 @@ 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.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; @@ -108,18 +109,18 @@ public class ProviderApiManagerTest { public void test_handleIntentSetupProvider_noProviderMainURL() { 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, "{\"errors\":\"It doesn't seem to be a Bitmask provider.\"}"); - Intent provider_API_command = mockIntent(); + Intent providerApiCommand = mockIntent(); Bundle parameters = mockBundle(); parameters.putString(Provider.MAIN_URL, ""); - 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.PARAMETERS, parameters); + providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); - providerApiManager.handleIntent(provider_API_command); + providerApiManager.handleIntent(providerApiCommand); } @Test @@ -128,7 +129,7 @@ public class ProviderApiManagerTest { mockProviderApiConnector(NO_ERROR); providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); Bundle expectedResult = mockBundle(); - expectedResult.putBoolean(RESULT_KEY, true); + expectedResult.putBoolean(BROADCAST_RESULT_KEY, true); Intent provider_API_command = mockIntent(); Bundle parameters = mockBundle(); @@ -149,7 +150,7 @@ public class ProviderApiManagerTest { mockProviderApiConnector(NO_ERROR); providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); Bundle expectedResult = mockBundle(); - expectedResult.putBoolean(RESULT_KEY, true); + expectedResult.putBoolean(BROADCAST_RESULT_KEY, true); Intent provider_API_command = mockIntent(); Bundle parameters = mockBundle(); @@ -170,7 +171,7 @@ public class ProviderApiManagerTest { 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); Intent provider_API_command = mockIntent(); Bundle parameters = mockBundle(); @@ -189,7 +190,7 @@ public class ProviderApiManagerTest { 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.\"}"); Intent provider_API_command = mockIntent(); @@ -211,7 +212,7 @@ public class ProviderApiManagerTest { 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.\"}"); Intent provider_API_command = mockIntent(); @@ -233,7 +234,7 @@ public class ProviderApiManagerTest { 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.\"}"); Intent provider_API_command = mockIntent(); @@ -253,7 +254,7 @@ public class ProviderApiManagerTest { 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.\"}"); Intent provider_API_command = mockIntent(); @@ -276,7 +277,7 @@ public class ProviderApiManagerTest { 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.\"}"); Intent provider_API_command = mockIntent(); @@ -297,7 +298,7 @@ public class ProviderApiManagerTest { 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.\"}"); Intent provider_API_command = mockIntent(); @@ -321,7 +322,7 @@ public class ProviderApiManagerTest { 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.\"}"); Intent provider_API_command = mockIntent(); 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; } |