diff options
38 files changed, 1684 insertions, 1641 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..1c5247c0 100644 --- a/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java +++ b/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java @@ -48,22 +48,25 @@ 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.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_CORRUPTED_PROVIDER_JSON;  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; +import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_details;  /**   * Created by cyberta on 04.01.18.   */  public class ProviderApiManager extends ProviderApiManagerBase { + +    private static final String TAG = ProviderApiManagerBase.class.getName(); +      protected static boolean lastDangerOn = true; @@ -79,142 +82,116 @@ public class ProviderApiManager extends ProviderApiManagerBase {       * Downloads a provider.json from a given URL, adding a new provider using the given name.       *       * @param task containing a boolean meaning if the provider is custom or not, another boolean meaning if the user completely trusts this provider, the provider name and its provider.json url. -     * @return a bundle with a boolean value mapped to a key named RESULT_KEY, and which is true if the update was successful. +     * @return a bundle with a boolean value mapped to a key named BROADCAST_RESULT_KEY, and which is true if the update was successful.       */      @Override -    protected Bundle setUpProvider(Bundle task) { -        int progress = 0; +    protected Bundle setUpProvider(Provider provider, Bundle task) {          Bundle currentDownload = new Bundle();          if (task != null) {              lastDangerOn = task.containsKey(ProviderListContent.ProviderItem.DANGER_ON) && task.getBoolean(ProviderListContent.ProviderItem.DANGER_ON); -            lastProviderMainUrl = task.containsKey(Provider.MAIN_URL) ? -                    task.getString(Provider.MAIN_URL) : -                    ""; - -            if (isEmpty(lastProviderMainUrl)) { -                setErrorResult(currentDownload, malformed_url, null); -                return currentDownload; -            } - -            providerCaCertFingerprint = task.containsKey(Provider.CA_CERT_FINGERPRINT) ? -                    task.getString(Provider.CA_CERT_FINGERPRINT) : -                    ""; -            providerCaCert = task.containsKey(Provider.CA_CERT) ? -                    task.getString(Provider.CA_CERT) : -                    ""; - -            try { -                providerDefinition = task.containsKey(Provider.KEY) ? -                        new JSONObject(task.getString(Provider.KEY)) : -                        new JSONObject(); -            } catch (JSONException e) { -                e.printStackTrace(); -                providerDefinition = new JSONObject(); -            } -            providerApiUrl = getApiUrlWithVersion(providerDefinition); +        } -            checkPersistedProviderUpdates(); -            currentDownload = validateProviderDetails(); +        if (isEmpty(provider.getMainUrlString()) || provider.getMainUrl().isDefault()) { +            setErrorResult(currentDownload, malformed_url, null); +            currentDownload.putParcelable(PROVIDER_KEY, provider); +            return currentDownload; +        } -            //provider details invalid -            if (currentDownload.containsKey(ERRORS)) { -                return currentDownload; -            } +        getPersistedProviderUpdates(provider); +        currentDownload = validateProviderDetails(provider); -            //no provider certificate available -            if (currentDownload.containsKey(RESULT_KEY) && !currentDownload.getBoolean(RESULT_KEY)) { -                resetProviderDetails(); -            } +        //provider details invalid +        if (currentDownload.containsKey(ERRORS)) { +            currentDownload.putParcelable(PROVIDER_KEY, provider); +            return currentDownload; +        } -            EIP_SERVICE_JSON_DOWNLOADED = false; -            go_ahead = true; +        //no provider certificate available +        if (currentDownload.containsKey(BROADCAST_RESULT_KEY) && !currentDownload.getBoolean(BROADCAST_RESULT_KEY)) { +            resetProviderDetails(provider);          } -        if (!PROVIDER_JSON_DOWNLOADED) -            currentDownload = getAndSetProviderJson(lastProviderMainUrl, lastDangerOn, providerCaCert, providerDefinition); -        if (PROVIDER_JSON_DOWNLOADED || (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY))) { -            broadcastProgress(progress++); -            PROVIDER_JSON_DOWNLOADED = true; - -            if (!CA_CERT_DOWNLOADED) -                currentDownload = downloadCACert(lastDangerOn); -            if (CA_CERT_DOWNLOADED || (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY))) { -                broadcastProgress(progress++); -                CA_CERT_DOWNLOADED = true; -                currentDownload = getAndSetEipServiceJson(); -                if (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY)) { -                    broadcastProgress(progress++); -                    EIP_SERVICE_JSON_DOWNLOADED = true; -                } +        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(); -        if (go_ahead) { -            String providerDotJsonString; -            if(providerDefinition.length() == 0 || caCert.isEmpty()) -                providerDotJsonString = downloadWithCommercialCA(providerMainUrl + "/provider.json", dangerOn); -            else -                providerDotJsonString = downloadFromApiUrlWithProviderCA("/provider.json", caCert, providerDefinition, dangerOn); +        JSONObject providerDefinition = provider.getDefinition(); +        String caCert = provider.getCaCert(); +        String providerMainUrl = provider.getMainUrlString(); -            if (!isValidJson(providerDotJsonString)) { -                result.putString(ERRORS, resources.getString(malformed_url)); -                result.putBoolean(RESULT_KEY, false); -                return result; -            } +        String providerDotJsonString; +        if(providerDefinition.length() == 0 || caCert.isEmpty()) +            providerDotJsonString = downloadWithCommercialCA(providerMainUrl + "/provider.json", dangerOn); +        else +            providerDotJsonString = downloadFromApiUrlWithProviderCA("/provider.json", caCert, providerDefinition, dangerOn); -            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); -            } catch (JSONException e) { -                String reason_to_fail = pickErrorMessage(providerDotJsonString); -                result.putString(ERRORS, reason_to_fail); -                result.putBoolean(RESULT_KEY, false); +        if (!isValidJson(providerDotJsonString)) { +            result.putString(ERRORS, resources.getString(malformed_url)); +            result.putBoolean(BROADCAST_RESULT_KEY, false); +            return result; +        } + +        try { +            JSONObject providerJson = new JSONObject(providerDotJsonString); + +            if (provider.define(providerJson)) { +                result.putBoolean(BROADCAST_RESULT_KEY, true); +            } else { +                return setErrorResult(result, warning_corrupted_provider_details, ERROR_CORRUPTED_PROVIDER_JSON.toString());              } + +            result.putBoolean(BROADCAST_RESULT_KEY, true); +        } catch (JSONException e) { +            String reason_to_fail = pickErrorMessage(providerDotJsonString); +            result.putString(ERRORS, reason_to_fail); +            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 = ""; -        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); - -                preferences.edit().putString(PROVIDER_KEY, eip_service_json.toString()).commit(); - -                result.putBoolean(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 eipServiceJsonString = ""; +        try { +            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); + +            if (eipServiceJson.has(ERRORS)) { +                String reasonToFail = pickErrorMessage(eipServiceJsonString); +                result.putString(ERRORS, reasonToFail); +                result.putBoolean(BROADCAST_RESULT_KEY, false); +            } else{ +                provider.setEipServiceJson(eipServiceJson); +                result.putBoolean(BROADCAST_RESULT_KEY, true);              } +        } catch (NullPointerException | JSONException e) { +            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(provider, 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)) { +                provider.setCaCert(certString); +                preferences.edit().putString(Provider.CA_CERT + "." + providerDomain, certString).apply(); +                result.putBoolean(BROADCAST_RESULT_KEY, true);              } else {                  setErrorResult(result, warning_corrupted_provider_cert, ERROR_CERTIFICATE_PINNING.toString());              } @@ -275,13 +251,13 @@ public class ProviderApiManager extends ProviderApiManagerBase {      /**       * Tries to download the contents of the provided url using commercially validated CA certificate from chosen provider.       * <p/> -     * If danger_on flag is true, SSL exceptions will be managed by futher methods that will try to use some bypass methods. +     * If dangerOn flag is true, SSL exceptions will be managed by futher methods that will try to use some bypass methods.       * -     * @param string_url -     * @param danger_on  if the user completely trusts this provider +     * @param stringUrl +     * @param dangerOn  if the user completely trusts this provider       * @return       */ -    private String downloadWithCommercialCA(String string_url, boolean danger_on) { +    private String downloadWithCommercialCA(String stringUrl, boolean dangerOn) {          String responseString;          JSONObject errorJson = new JSONObject(); @@ -292,14 +268,14 @@ public class ProviderApiManager extends ProviderApiManagerBase {          List<Pair<String, String>> headerArgs = getAuthorizationHeader(); -        responseString = sendGetStringToServer(string_url, headerArgs, okHttpClient); +        responseString = sendGetStringToServer(stringUrl, headerArgs, okHttpClient);          if (responseString != null && responseString.contains(ERRORS)) {              try {                  // try to download with provider CA on certificate error                  JSONObject responseErrorJson = new JSONObject(responseString); -                if (danger_on && responseErrorJson.getString(ERRORS).equals(resources.getString(R.string.certificate_error))) { -                    responseString = downloadWithoutCA(string_url); +                if (dangerOn && responseErrorJson.getString(ERRORS).equals(resources.getString(R.string.certificate_error))) { +                    responseString = downloadWithoutCA(stringUrl);                  }              } catch (JSONException e) {                  e.printStackTrace(); @@ -313,7 +289,7 @@ public class ProviderApiManager extends ProviderApiManagerBase {          String responseString;          JSONObject errorJson = new JSONObject();          String baseUrl = getApiUrl(providerDefinition); -        OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(errorJson, caCert); +        OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(caCert, errorJson);          if (okHttpClient == null) {              return errorJson.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();          } @@ -376,7 +352,7 @@ public class ProviderApiManager extends ProviderApiManagerBase {       * Downloads the string that's in the url with any certificate.       */      // This method is totally insecure anyways. So no need to refactor that in order to use okHttpClient, force modern TLS etc.. DO NOT USE IN PRODUCTION! -    private String downloadWithoutCA(String url_string) { +    private String downloadWithoutCA(String urlString) {          String string = "";          try { @@ -406,7 +382,7 @@ public class ProviderApiManager extends ProviderApiManagerBase {              SSLContext context = SSLContext.getInstance("TLS");              context.init(new KeyManager[0], new TrustManager[]{new DefaultTrustManager()}, new SecureRandom()); -            URL url = new URL(url_string); +            URL url = new URL(urlString);              HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();              urlConnection.setSSLSocketFactory(context.getSocketFactory());              urlConnection.setHostnameVerifier(hostnameVerifier); diff --git a/app/src/insecure/java/se/leap/bitmaskclient/ProviderDetailActivity.java b/app/src/insecure/java/se/leap/bitmaskclient/ProviderDetailActivity.java index 6977753f..cf9ee5f5 100644 --- a/app/src/insecure/java/se/leap/bitmaskclient/ProviderDetailActivity.java +++ b/app/src/insecure/java/se/leap/bitmaskclient/ProviderDetailActivity.java @@ -2,15 +2,12 @@ package se.leap.bitmaskclient;  import android.content.SharedPreferences; -import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; -import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; -  public class ProviderDetailActivity extends AbstractProviderDetailActivity {      @Override      public void onBackPressed() {          SharedPreferences.Editor editor = preferences.edit(); -        editor.remove(Provider.KEY).remove(ProviderListContent.ProviderItem.DANGER_ON).remove(PROVIDER_ALLOW_ANONYMOUS).remove(PROVIDER_KEY).apply(); +        editor.remove(ProviderListContent.ProviderItem.DANGER_ON).apply();          super.onBackPressed();      } diff --git a/app/src/insecure/java/se/leap/bitmaskclient/ProviderListActivity.java b/app/src/insecure/java/se/leap/bitmaskclient/ProviderListActivity.java index 823b5635..5ad7ea44 100644 --- a/app/src/insecure/java/se/leap/bitmaskclient/ProviderListActivity.java +++ b/app/src/insecure/java/se/leap/bitmaskclient/ProviderListActivity.java @@ -16,8 +16,8 @@   */  package se.leap.bitmaskclient; -import android.content.Intent;  import android.os.Bundle; +import android.support.annotation.NonNull;  import android.support.v4.app.DialogFragment;  import android.support.v4.app.FragmentTransaction; @@ -26,6 +26,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 +53,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 +89,19 @@ 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() { -        cancelSettingUpProvider(); -        if (!ProviderAPI.caCertDownloaded()) { -            addAndSelectNewProvider(ProviderAPI.lastProviderMainUrl(), ProviderAPI.lastDangerOn()); -        } else { -            showProgressBar(); - -            Intent providerAPICommand = new Intent(this, ProviderAPI.class); - -            providerAPICommand.setAction(ProviderAPI.SET_UP_PROVIDER); -            Bundle parameters = new Bundle(); -            parameters.putString(Provider.MAIN_URL, provider.getMainUrl().toString()); -            providerAPICommand.putExtra(ProviderAPI.PARAMETERS, parameters); - -            startService(providerAPICommand); -        } +    public void retrySetUpProvider(@NonNull Provider provider) { +        ProviderAPICommand.execute(this, SET_UP_PROVIDER, provider);      }  } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 029e9fdb..f60a2db6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,7 +21,7 @@      <uses-sdk          android:minSdkVersion="16" -        android:targetSdkVersion="26" /> +        android:targetSdkVersion="27" />      <uses-permission android:name="android.permission.INTERNET" />      <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> diff --git a/app/src/main/java/se/leap/bitmaskclient/AbstractProviderDetailActivity.java b/app/src/main/java/se/leap/bitmaskclient/AbstractProviderDetailActivity.java index ebfc1909..6738a6bb 100644 --- a/app/src/main/java/se/leap/bitmaskclient/AbstractProviderDetailActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/AbstractProviderDetailActivity.java @@ -1,7 +1,6 @@  package se.leap.bitmaskclient;  import android.content.Intent; -import android.content.SharedPreferences;  import android.os.Bundle;  import android.support.annotation.Nullable;  import android.support.v7.widget.AppCompatTextView; @@ -12,14 +11,10 @@ import android.widget.ArrayAdapter;  import android.widget.ListView;  import android.widget.TextView; -import org.json.JSONException; -import org.json.JSONObject; -  import java.util.ArrayList;  import butterknife.InjectView; -import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS;  import static se.leap.bitmaskclient.Constants.PROVIDER_KEY;  import static se.leap.bitmaskclient.Constants.REQUEST_CODE_CONFIGURE_LEAP; @@ -36,56 +31,58 @@ public abstract class AbstractProviderDetailActivity extends ConfigWizardBaseAct      @Override      protected void onCreate(@Nullable Bundle savedInstanceState) {          super.onCreate(savedInstanceState); + +        provider = getIntent().getParcelableExtra(PROVIDER_KEY);          setContentView(R.layout.a_provider_detail); -        try { -            JSONObject providerJson = new JSONObject(preferences.getString(Provider.KEY, "")); -            setProviderHeaderText(ConfigHelper.getProviderName(preferences)); -            description.setText(ConfigHelper.getDescription(preferences)); +        if (provider == null) { +            return; +        } -            // Show only the options allowed by the provider -            ArrayList<String> optionsList = new ArrayList<>(); -            if (registrationAllowed(providerJson)) { -                optionsList.add(getString(R.string.login_to_profile)); -                optionsList.add(getString(R.string.create_profile)); -            } -            if (anonAllowed(providerJson)) { -                optionsList.add(getString(R.string.use_anonymously_button)); -            } -            options.setAdapter(new ArrayAdapter<>( -                    this, -                    R.layout.single_list_item, -                    android.R.id.text1, -                    optionsList.toArray(new String[optionsList.size()]) -            )); -            options.setOnItemClickListener(new AdapterView.OnItemClickListener() { -                @Override -                public void onItemClick(AdapterView<?> parent, View view, int position, long id) { -                    String text = ((TextView) view).getText().toString(); -                    Intent intent; -                    if (text.equals(getString(R.string.login_to_profile))) { -                        Log.d(TAG, "login selected"); -                        intent = new Intent(getApplicationContext(), LoginActivity.class); -                    } else if (text.equals(getString(R.string.create_profile))) { -                        Log.d(TAG, "signup selected"); -                        intent = new Intent(getApplicationContext(), SignupActivity.class); -                    } else { -                        Log.d(TAG, "use anonymously selected"); -                        intent = new Intent(); -                        intent.putExtra(Provider.KEY, provider); -                        setResult(RESULT_OK, intent); -                        finish(); -                        return; -                    } -                    intent.putExtra(PROVIDER_KEY, provider); -                    intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); -                    startActivityForResult(intent, REQUEST_CODE_CONFIGURE_LEAP); -                } -            }); -        } catch (JSONException e) { -            // TODO show error and return +        setProviderHeaderText(provider.getName()); +        description.setText(provider.getDescription()); + +        // Show only the options allowed by the provider +        ArrayList<String> optionsList = new ArrayList<>(); +        if (provider.allowsRegistered()) { +            optionsList.add(getString(R.string.login_to_profile)); +            optionsList.add(getString(R.string.create_profile)); +        } +        if (provider.allowsAnonymous()) { +            optionsList.add(getString(R.string.use_anonymously_button));          } + +        options.setAdapter(new ArrayAdapter<>( +                this, +                R.layout.single_list_item, +                android.R.id.text1, +                optionsList.toArray(new String[optionsList.size()]) +        )); +        options.setOnItemClickListener(new AdapterView.OnItemClickListener() { +            @Override +            public void onItemClick(AdapterView<?> parent, View view, int position, long id) { +                String text = ((TextView) view).getText().toString(); +                Intent intent; +                if (text.equals(getString(R.string.login_to_profile))) { +                    Log.d(TAG, "login selected"); +                    intent = new Intent(getApplicationContext(), LoginActivity.class); +                } else if (text.equals(getString(R.string.create_profile))) { +                    Log.d(TAG, "signup selected"); +                    intent = new Intent(getApplicationContext(), SignupActivity.class); +                } else { +                    Log.d(TAG, "use anonymously selected"); +                    intent = new Intent(); +                    intent.putExtra(Provider.KEY, provider); +                    setResult(RESULT_OK, intent); +                    finish(); +                    return; +                } +                intent.putExtra(PROVIDER_KEY, provider); +                intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); +                startActivityForResult(intent, REQUEST_CODE_CONFIGURE_LEAP); +            } +        });      }      @Override @@ -104,29 +101,4 @@ public abstract class AbstractProviderDetailActivity extends ConfigWizardBaseAct          }      } -    private boolean anonAllowed(JSONObject providerJson) { -        try { -            JSONObject serviceDescription = providerJson.getJSONObject(Provider.SERVICE); -            return serviceDescription.has(PROVIDER_ALLOW_ANONYMOUS) && serviceDescription.getBoolean(PROVIDER_ALLOW_ANONYMOUS); -        } catch (JSONException e) { -            return false; -        } -    } - -    private boolean registrationAllowed(JSONObject providerJson) { -        try { -            JSONObject serviceDescription = providerJson.getJSONObject(Provider.SERVICE); -            return serviceDescription.has(Provider.ALLOW_REGISTRATION) && serviceDescription.getBoolean(Provider.ALLOW_REGISTRATION); -        } catch (JSONException e) { -            return false; -        } -    } - -    @Override -    public void onBackPressed() { -        SharedPreferences.Editor editor = preferences.edit(); -        editor.remove(Provider.KEY).remove(PROVIDER_ALLOW_ANONYMOUS).remove(PROVIDER_KEY).apply(); -        super.onBackPressed(); -    } -  } diff --git a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java index 22965252..7b2accd6 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java @@ -46,10 +46,17 @@ 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_EIP_DEFINITION; +import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY; +import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE;  /**   * Stores constants, and implements auxiliary methods used across all Bitmask Android classes. @@ -97,14 +104,14 @@ public class ConfigHelper {          return ret;      } -    public static X509Certificate parseX509CertificateFromString(String certificate_string) { +    public static X509Certificate parseX509CertificateFromString(String certificateString) {          java.security.cert.Certificate certificate = null;          CertificateFactory cf;          try {              cf = CertificateFactory.getInstance("X.509"); -            certificate_string = certificate_string.replaceFirst("-----BEGIN CERTIFICATE-----", "").replaceFirst("-----END CERTIFICATE-----", "").trim(); -            byte[] cert_bytes = Base64.decode(certificate_string); +            certificateString = certificateString.replaceFirst("-----BEGIN CERTIFICATE-----", "").replaceFirst("-----END CERTIFICATE-----", "").trim(); +            byte[] cert_bytes = Base64.decode(certificateString);              InputStream caInput = new ByteArrayInputStream(cert_bytes);              try {                  certificate = cf.generateCertificate(caInput); @@ -270,9 +277,11 @@ public class ConfigHelper {      public static Provider getSavedProviderFromSharedPreferences(@NonNull SharedPreferences preferences) {          Provider provider = new Provider();          try { -            provider.setUrl(new URL(preferences.getString(Provider.MAIN_URL, ""))); +            provider.setMainUrl(new URL(preferences.getString(Provider.MAIN_URL, "")));              provider.define(new JSONObject(preferences.getString(Provider.KEY, ""))); -            provider.setCACert(preferences.getString(Provider.CA_CERT, "")); +            provider.setCaCert(preferences.getString(Provider.CA_CERT, "")); +            provider.setVpnCertificate(preferences.getString(PROVIDER_VPN_CERTIFICATE, "")); +            provider.setPrivateKey(preferences.getString(PROVIDER_PRIVATE_KEY, ""));          } catch (MalformedURLException | JSONException e) {              e.printStackTrace();          } @@ -280,6 +289,10 @@ public class ConfigHelper {          return provider;      } +    public static String getFromPersistedProvider(String toFetch, String providerDomain, SharedPreferences preferences) { +        return preferences.getString(toFetch + "." + providerDomain, ""); +    } +      public static String getProviderName(String provider) {          return getProviderName(null, provider);      } @@ -343,11 +356,73 @@ public class ConfigHelper {          }      } +    // TODO: replace commit with apply after refactoring EIP +    //FIXME: don't save private keys in shared preferences! use the keystore      public static void storeProviderInPreferences(SharedPreferences preferences, Provider provider) {          preferences.edit().putBoolean(PROVIDER_CONFIGURED, true).                  putString(Provider.MAIN_URL, provider.getMainUrlString()).                  putString(Provider.KEY, provider.getDefinitionString()).                  putString(Provider.CA_CERT, provider.getCaCert()). +                putString(PROVIDER_EIP_DEFINITION, provider.getEipServiceJsonString()). +                putString(PROVIDER_PRIVATE_KEY, provider.getPrivateKey()). +                putString(PROVIDER_VPN_CERTIFICATE, provider.getVpnCertificate()). +                commit(); + +        String providerDomain = provider.getDomain(); +        preferences.edit().putBoolean(PROVIDER_CONFIGURED, true). +                putString(Provider.MAIN_URL + "." + providerDomain, provider.getMainUrlString()). +                putString(Provider.KEY + "." + providerDomain, provider.getDefinitionString()). +                putString(Provider.CA_CERT + "." + providerDomain, provider.getCaCert()). +                putString(PROVIDER_EIP_DEFINITION + "." + providerDomain, provider.getEipServiceJsonString()). +                putString(PROVIDER_PRIVATE_KEY + "." + providerDomain, provider.getPrivateKey()). +                putString(PROVIDER_VPN_CERTIFICATE + "." + providerDomain, provider.getVpnCertificate()).                  apply();      } + + +    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(); +        } +    } + +    public static void deleteProviderDetailsFromPreferences(@NonNull SharedPreferences preferences, String providerDomain) { +            preferences.edit(). +                    remove(Provider.KEY + "." + providerDomain). +                    remove(Provider.CA_CERT + "." + providerDomain). +                    remove(Provider.CA_CERT_FINGERPRINT + "." + providerDomain). +                    remove(Provider.MAIN_URL + "." + providerDomain). +                    remove(Provider.KEY + "." + providerDomain). +                    remove(Provider.CA_CERT + "." + providerDomain). +                    remove(PROVIDER_EIP_DEFINITION + "." + providerDomain). +                    remove(PROVIDER_PRIVATE_KEY + "." + providerDomain). +                    remove(PROVIDER_VPN_CERTIFICATE + "." + providerDomain). +                    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 f197c2ed..fb2655e3 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;      ////////////////////////////////////////////// @@ -45,16 +46,41 @@ public interface Constants {      String EIP_REQUEST = "EIP.REQUEST";      String EIP_RESTART_ON_BOOT = "EIP.RESTART_ON_BOOT";      String EIP_IS_ALWAYS_ON = "EIP.EIP_IS_ALWAYS_ON"; -    String EIP_TRIGGERED_FROM_UI = "EIP.TRIGGERED_FROM_UI"; +    String EIP_EARLY_ROUTES = "EIP.EARLY_ROUTES";      //////////////////////////////////////////////      // 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"; +    String PROVIDER_EIP_DEFINITION = "Constants.EIP_DEFINITION"; + +    ////////////////////////////////////////////// +    // 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 78ec3fd2..48dce1c2 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,10 +46,8 @@ 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.PROVIDER_EIP_DEFINITION;  import static se.leap.bitmaskclient.Constants.REQUEST_CODE_CONFIGURE_LEAP;  import static se.leap.bitmaskclient.Constants.REQUEST_CODE_KEY;  import static se.leap.bitmaskclient.Constants.REQUEST_CODE_SWITCH_PROVIDER; @@ -91,20 +84,15 @@ public class Dashboard extends ButterKnifeActivity {      private UserStatusFragment user_status_fragment;      private static Provider provider = new Provider(); -    public static ProviderAPIResultReceiver providerAPI_result_receiver; -    private static boolean switching_provider;      private boolean handledVersion; -    public static DashboardReceiver dashboardReceiver;      @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState); -        dashboardReceiver = new DashboardReceiver(this);          preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);          fragment_manager = new FragmentManagerEnhanced(getSupportFragmentManager()); -        providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler(), dashboardReceiver);          if (!handledVersion) {              handleVersion(); @@ -112,7 +100,6 @@ public class Dashboard extends ButterKnifeActivity {          }          // initialize app necessities -        ProviderAPICommand.initialize(this);          VpnStatus.initLogCache(getApplicationContext().getCacheDir());          User.init(getString(R.string.default_username)); @@ -157,9 +144,9 @@ public class Dashboard extends ButterKnifeActivity {      private Provider getSavedProviderFromSharedPreferences() {          Provider provider = new Provider();          try { -            provider.setUrl(new URL(preferences.getString(Provider.MAIN_URL, ""))); +            provider.setMainUrl(new URL(preferences.getString(Provider.MAIN_URL, "")));              provider.define(new JSONObject(preferences.getString(Provider.KEY, ""))); -            provider.setCACert(preferences.getString(Provider.CA_CERT, "")); +            provider.setCaCert(preferences.getString(Provider.CA_CERT, ""));          } catch (MalformedURLException | JSONException e) {              e.printStackTrace();          } @@ -174,8 +161,8 @@ public class Dashboard extends ButterKnifeActivity {              switch (versionCode) {                  case 91: // 0.6.0 without Bug #5999                  case 101: // 0.8.0 -                    if (!preferences.getString(PROVIDER_KEY, "").isEmpty()) -                        eip_fragment.updateEipService(); +                    if (!preferences.getString(PROVIDER_EIP_DEFINITION, "").isEmpty()) +                        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/DefaultedURL.java b/app/src/main/java/se/leap/bitmaskclient/DefaultedURL.java index 57ff1fd8..0cbb0d72 100644 --- a/app/src/main/java/se/leap/bitmaskclient/DefaultedURL.java +++ b/app/src/main/java/se/leap/bitmaskclient/DefaultedURL.java @@ -36,4 +36,13 @@ public class DefaultedURL {      public String toString() {          return url.toString();      } + +    @Override +    public boolean equals(Object o) { +        if (o instanceof DefaultedURL) { +            return url.equals(((DefaultedURL) o).getUrl()); +        } +        return false; +    } +  } diff --git a/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java b/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java index dde71642..8a6d981d 100644 --- a/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java @@ -21,6 +21,7 @@ import android.app.Dialog;  import android.content.Context;  import android.content.DialogInterface;  import android.os.Bundle; +import android.support.annotation.NonNull;  import android.support.v4.app.DialogFragment;  import org.json.JSONObject; @@ -38,8 +39,11 @@ import static se.leap.bitmaskclient.ProviderAPI.ERRORS;  public class DownloadFailedDialog extends DialogFragment {      public static String TAG = "downloaded_failed_dialog"; -    private String reason_to_fail; +    private String reasonToFail;      private DOWNLOAD_ERRORS downloadError = DEFAULT; + +    private Provider provider; +      public enum DOWNLOAD_ERRORS {          DEFAULT,          ERROR_CORRUPTED_PROVIDER_JSON, @@ -50,52 +54,55 @@ public class DownloadFailedDialog extends DialogFragment {      /**       * @return a new instance of this DialogFragment.       */ -    public static DialogFragment newInstance(String reason_to_fail) { -        DownloadFailedDialog dialog_fragment = new DownloadFailedDialog(); -        dialog_fragment.reason_to_fail = reason_to_fail; -        return dialog_fragment; +    public static DialogFragment newInstance(Provider provider, String reasonToFail) { +        DownloadFailedDialog dialogFragment = new DownloadFailedDialog(); +        dialogFragment.reasonToFail = reasonToFail; +        dialogFragment.provider = provider; +        return dialogFragment;      }      /**       * @return a new instance of this DialogFragment.       */ -    public static DialogFragment newInstance(JSONObject errorJson) { -        DownloadFailedDialog dialog_fragment = new DownloadFailedDialog(); +    public static DialogFragment newInstance(Provider provider, JSONObject errorJson) { +        DownloadFailedDialog dialogFragment = new DownloadFailedDialog(); +        dialogFragment.provider = provider;          try {              if (errorJson.has(ERRORS)) { -                dialog_fragment.reason_to_fail = errorJson.getString(ERRORS); +                dialogFragment.reasonToFail = errorJson.getString(ERRORS);              } else {                  //default error msg -                dialog_fragment.reason_to_fail = dialog_fragment.getString(R.string.error_io_exception_user_message); +                dialogFragment.reasonToFail = dialogFragment.getString(R.string.error_io_exception_user_message);              }              if (errorJson.has(ERRORID)) { -                dialog_fragment.downloadError = valueOf(errorJson.getString(ERRORID)); +                dialogFragment.downloadError = valueOf(errorJson.getString(ERRORID));              }          } catch (Exception e) {              e.printStackTrace(); -            dialog_fragment.reason_to_fail = dialog_fragment.getString(R.string.error_io_exception_user_message); +            dialogFragment.reasonToFail = dialogFragment.getString(R.string.error_io_exception_user_message);          } -        return dialog_fragment; +        return dialogFragment;      }      @Override +    @NonNull      public Dialog onCreateDialog(Bundle savedInstanceState) {          AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); -        builder.setMessage(reason_to_fail) +        builder.setMessage(reasonToFail)                  .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {              public void onClick(DialogInterface dialog, int id) { -                interface_with_ConfigurationWizard.cancelSettingUpProvider(); +                interfaceWithConfigurationWizard.cancelSettingUpProvider();                  dialog.dismiss();              }          }); -        switch (downloadError) { +switch (downloadError) {              case ERROR_CORRUPTED_PROVIDER_JSON:                  builder.setPositiveButton(R.string.update_provider_details, new DialogInterface.OnClickListener() {                      @Override                      public void onClick(DialogInterface dialog, int which) {                          dismiss(); -                        interface_with_ConfigurationWizard.updateProviderDetails(); +                        interfaceWithConfigurationWizard.updateProviderDetails();                      }                  });                  break; @@ -105,7 +112,7 @@ public class DownloadFailedDialog extends DialogFragment {                      @Override                      public void onClick(DialogInterface dialog, int which) {                          dismiss(); -                        interface_with_ConfigurationWizard.updateProviderDetails(); +                        interfaceWithConfigurationWizard.updateProviderDetails();                      }                  });                  break; @@ -113,7 +120,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(); +                                interfaceWithConfigurationWizard.retrySetUpProvider(provider);                              }                          });                  break; @@ -124,20 +131,20 @@ public class DownloadFailedDialog extends DialogFragment {      }      public interface DownloadFailedDialogInterface { -        void retrySetUpProvider(); +        void retrySetUpProvider(@NonNull Provider provider);          void cancelSettingUpProvider();          void updateProviderDetails();      } -    DownloadFailedDialogInterface interface_with_ConfigurationWizard; +    DownloadFailedDialogInterface interfaceWithConfigurationWizard;      @Override      public void onAttach(Context context) {          super.onAttach(context);          try { -            interface_with_ConfigurationWizard = (DownloadFailedDialogInterface) context; +            interfaceWithConfigurationWizard = (DownloadFailedDialogInterface) context;          } catch (ClassCastException e) {              throw new ClassCastException(context.toString()                      + " must implement NoticeDialogListener"); @@ -146,7 +153,7 @@ public class DownloadFailedDialog extends DialogFragment {      @Override      public void onCancel(DialogInterface dialog) { -        interface_with_ConfigurationWizard.cancelSettingUpProvider(); +        interfaceWithConfigurationWizard.cancelSettingUpProvider();          dialog.dismiss();      } diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java index 3c541e71..a082b047 100644 --- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java @@ -18,20 +18,22 @@ 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;  import android.graphics.ColorMatrixColorFilter;  import android.os.Bundle; -import android.os.Handler;  import android.os.IBinder;  import android.os.RemoteException; -import android.os.ResultReceiver; +import android.support.annotation.NonNull;  import android.support.v4.app.Fragment; +import android.support.v4.content.LocalBroadcastManager;  import android.support.v7.widget.AppCompatImageView;  import android.util.Log;  import android.view.LayoutInflater; @@ -50,28 +52,37 @@ 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.views.VpnStateImage; +import static android.app.Activity.RESULT_OK; +import static android.content.Intent.CATEGORY_DEFAULT;  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_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.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.EIP_TRIGGERED_FROM_UI; -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_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; +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.CORRECTLY_DOWNLOADED_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE; +import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE;  public class EipFragment extends Fragment implements Observer { @@ -83,6 +94,7 @@ public class EipFragment extends Fragment implements Observer {      private SharedPreferences preferences; +    private Provider provider;      @InjectView(R.id.background)      AppCompatImageView background; @@ -99,10 +111,11 @@ public class EipFragment extends Fragment implements Observer {      @InjectView(R.id.vpn_route)      TextView vpnRoute; -    private EIPReceiver eipReceiver;      private EipStatus eipStatus;      private boolean wantsToConnect; +    private EIPFragmentBroadcastReceiver eipFragmentBroadcastReceiver; +      private IOpenVPNServiceInternal mService;      private ServiceConnection openVpnConnection = new ServiceConnection() { @@ -125,19 +138,38 @@ public class EipFragment extends Fragment implements Observer {      @Override      public void onAttach(Context context) {          super.onAttach(context); -        downloadEIPServiceConfig(); +        Bundle arguments = getArguments(); +        Activity activity = getActivity(); +        if (activity != null) { +            if (arguments != null) { +                provider = arguments.getParcelable(PROVIDER_KEY); +                if (provider == null) { +                    activity.startActivityForResult(new Intent(activity, ProviderListActivity.class), REQUEST_CODE_SWITCH_PROVIDER); +                } else { +                    Log.d(TAG, provider.getName() + " configured as provider"); +                } +            } else { +                Log.e(TAG, "no provider given - starting ProviderListActivity"); +                activity.startActivityForResult(new Intent(activity, ProviderListActivity.class), REQUEST_CODE_SWITCH_PROVIDER); +            } +        }      }      @Override      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          eipStatus = EipStatus.getInstance(); -        eipReceiver = new EIPReceiver(new Handler()); -        preferences = getActivity().getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE); +        eipFragmentBroadcastReceiver = new EIPFragmentBroadcastReceiver(); +        Activity activity = getActivity(); +        if (activity != null) { +            preferences = getActivity().getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE); +        } else { +            Log.e(TAG, "activity is null in onCreate - no preferences set!"); +        }      }      @Override -    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { +    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {          eipStatus.addObserver(this);          View view = inflater.inflate(R.layout.eip_service_fragment, container, false);          ButterKnife.inject(this, view); @@ -154,6 +186,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();      } @@ -161,7 +194,13 @@ public class EipFragment extends Fragment implements Observer {      @Override      public void onPause() {          super.onPause(); -        getActivity().unbindService(openVpnConnection); + +        Activity activity = getActivity(); +        if (activity != null) { +            getActivity().unbindService(openVpnConnection); +            LocalBroadcastManager.getInstance(activity).unregisterReceiver(eipFragmentBroadcastReceiver); +        } +        Log.d(TAG, "broadcast unregistered");      }      @Override @@ -171,7 +210,7 @@ public class EipFragment extends Fragment implements Observer {      }      @Override -    public void onSaveInstanceState(Bundle outState) { +    public void onSaveInstanceState(@NonNull Bundle outState) {          outState.putBoolean(IS_CONNECTED, eipStatus.isConnected());          super.onSaveInstanceState(outState);      } @@ -198,29 +237,36 @@ public class EipFragment extends Fragment implements Observer {      }      private void handleSwitchOn() { -        if (canStartEIP()) +        Context context = getContext(); +        if (context == null) { +            Log.e(TAG, "context is null when switch turning on"); +            return; +        } + +        if (canStartEIP()) {              startEipFromScratch(); -        else if (canLogInToStartEIP()) { +        } else if (canLogInToStartEIP()) {              wantsToConnect = true; -            /*Bundle bundle = new Bundle(); -            seionDialogCallback.onSessionDialog(bundle);*/ -            Log.w(TAG, "TODO: implement login from here"); -            //FIXME: implement login from here +            Intent intent = new Intent(getContext(), LoginActivity.class); +            intent.putExtra(PROVIDER_KEY, provider); +            Activity activity = getActivity(); +            if (activity != null) { +                activity.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 -            // a user feedback. A button that does nothing on click is not a good option +            // provider has no VpnCertificate but user is logged in +            downloadVpnCertificate();          }      }      private boolean canStartEIP() { -        boolean certificateExists = !preferences.getString(PROVIDER_VPN_CERTIFICATE, "").isEmpty(); -        boolean isAllowedAnon = preferences.getBoolean(PROVIDER_ALLOW_ANONYMOUS, false); +        boolean certificateExists = provider.hasVpnCertificate(); +        boolean isAllowedAnon = provider.allowsAnonymous();          return (isAllowedAnon || certificateExists) && !eipStatus.isConnected() && !eipStatus.isConnecting();      }      private boolean canLogInToStartEIP() { -        boolean isAllowedRegistered = preferences.getBoolean(PROVIDER_ALLOWED_REGISTERED, false); +        boolean isAllowedRegistered = provider.allowsRegistered();          boolean isLoggedIn = !LeapSRPSession.getToken().isEmpty();          return isAllowedRegistered && !isLoggedIn && !eipStatus.isConnecting() && !eipStatus.isConnected();      } @@ -235,6 +281,10 @@ public class EipFragment extends Fragment implements Observer {      private void askPendingStartCancellation() {          Activity activity = getActivity(); +        if (activity == null) { +            Log.e(TAG, "activity is null when asking to cancel"); +            return; +        }          AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getActivity());          alertBuilder.setTitle(activity.getString(R.string.eip_cancel_connect_title))                  .setMessage(activity.getString(R.string.eip_cancel_connect_text)) @@ -250,12 +300,18 @@ public class EipFragment extends Fragment implements Observer {                      }                  })                  .show(); +      }      public void startEipFromScratch() { +        Context context = getContext(); +        if (context == null) { +            Log.e(TAG, "context is null when trying to start VPN"); +            return; +        }          wantsToConnect = false;          saveStatus(true); -        eipCommand(EIP_ACTION_START); +        EipCommand.startVPN(context, false);          vpnStateImage.showProgress();          routedText.setVisibility(GONE);          vpnRoute.setVisibility(GONE); @@ -273,6 +329,11 @@ public class EipFragment extends Fragment implements Observer {      private void stopBlockingVpn() {          Log.d(TAG, "stop VoidVpn!");          Activity activity = getActivity(); +        if (activity == null) { +            // TODO what to do if not stopping void vpn? +            Log.e(TAG, "activity is null when trying to stop blocking vpn"); +            return; +        }          Intent stopVoidVpnIntent = new Intent(activity, VoidVpnService.class);          stopVoidVpnIntent.setAction(EIP_ACTION_STOP_BLOCKING_VPN);          activity.startService(stopVoidVpnIntent); @@ -290,18 +351,19 @@ 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); +        Context context = getContext(); +        if (context == null) { +            Log.e(TAG, "context is null when trying to stop EIP"); +            return; +        } +        EipCommand.stopVPN(getContext());      }      protected void askToStopEIP() {          Activity activity = getActivity(); +        if (activity == null) { +            Log.e(TAG, "activity is null when asking to stop EIP"); +        }          AlertDialog.Builder alertBuilder = new AlertDialog.Builder(activity);          alertBuilder.setTitle(activity.getString(R.string.eip_cancel_connect_title))                  .setMessage(activity.getString(R.string.eip_warning_browser_inconsistency)) @@ -319,29 +381,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(); -        if (activity == null) { -            return; -        } -        // 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_TRIGGERED_FROM_UI, true); -        vpn_intent.putExtra(EIP_RECEIVER, eipReceiver); -        activity.startService(vpn_intent); -    } -      @Override      public void update(Observable observable, Object data) {          if (observable instanceof EipStatus) { @@ -363,6 +402,7 @@ public class EipFragment extends Fragment implements Observer {      private void handleNewState() {          Activity activity = getActivity();          if (activity == null) { +            Log.e(TAG, "activity is null while trying to handle new state");              return;          } @@ -406,76 +446,95 @@ public class EipFragment extends Fragment implements Observer {      private void bindOpenVpnService() {          Activity activity = getActivity(); +        if (activity == null) { +            Log.e(TAG, "activity is null when binding OpenVpn"); +            return; +        } +          Intent intent = new Intent(activity, OpenVPNService.class);          intent.setAction(OpenVPNService.START_SERVICE);          activity.bindService(intent, openVpnConnection, Context.BIND_AUTO_CREATE); -    } - -    protected class EIPReceiver extends ResultReceiver { -        EIPReceiver(Handler handler) { -            super(handler); -        } +    } +    private class EIPFragmentBroadcastReceiver extends BroadcastReceiver {          @Override -        protected void onReceiveResult(int resultCode, Bundle resultData) { -            super.onReceiveResult(resultCode, resultData); +        public void onReceive(Context context, Intent intent) { +            Log.d(TAG, "received Broadcast"); -            String request = resultData.getString(EIP_REQUEST); - -            if (request == null) { +            String action = intent.getAction(); +            if (action == null) {                  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; -                    } +            int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, -1); +            Bundle resultData = intent.getParcelableExtra(BROADCAST_RESULT_KEY); +            switch (action) { +                case BROADCAST_EIP_EVENT: +                    handleEIPEvent(resultCode, resultData);                      break; -                case EIP_ACTION_CHECK_CERT_VALIDITY: -                    switch (resultCode) { -                        case Activity.RESULT_OK: -                            break; -                        case Activity.RESULT_CANCELED: -                            Dashboard.downloadVpnCertificate(); -                            break; -                    } +                case BROADCAST_PROVIDER_API_EVENT: +                    handleProviderApiEvent(resultCode, resultData);                      break; -                case EIP_ACTION_UPDATE: -                    switch (resultCode) { -                        case Activity.RESULT_OK: -                            if (wantsToConnect) -                                startEipFromScratch(); -                            break; -                        case Activity.RESULT_CANCELED: -                            handleNewState(); -                            break; -                    }              }          }      } +    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; +                } +        } +    } +      private void greyscaleBackground() {          ColorMatrix matrix = new ColorMatrix();          matrix.setSaturation(0); @@ -494,4 +553,46 @@ public class EipFragment extends Fragment implements Observer {          background.setImageAlpha(210);      } +    public void handleProviderApiEvent(int resultCode, Bundle resultData) { +        Context context = getContext(); +        if (context == null) { +            return; +        } + +        // TODO call DOWNLOAD_EIP_SERVICES ore remove respective cases +        switch (resultCode) { +            case CORRECTLY_DOWNLOADED_EIP_SERVICE: +                provider = resultData.getParcelable(PROVIDER_KEY); +                EipCommand.updateEipService(context); +                break; +            case INCORRECTLY_DOWNLOADED_EIP_SERVICE: +                //dashboard.setResult(RESULT_CANCELED); +                // TODO CATCH ME IF YOU CAN - WHAT DO WE WANT TO DO? +                break; +            case CORRECTLY_DOWNLOADED_CERTIFICATE: +                startEipFromScratch(); +                break; +            case INCORRECTLY_DOWNLOADED_CERTIFICATE: +                // TODO CATCH ME IF YOU CAN - LOGIN? +                break; +        } +    } + +    private void downloadVpnCertificate() { +        ProviderAPICommand.execute(getContext(), DOWNLOAD_CERTIFICATE, provider); +    } + +    private void setUpBroadcastReceiver() { +        Activity activity = getActivity(); +        if (activity != null) { +            IntentFilter updateIntentFilter = new IntentFilter(BROADCAST_EIP_EVENT); +            updateIntentFilter.addAction(BROADCAST_PROVIDER_API_EVENT); +            updateIntentFilter.addCategory(CATEGORY_DEFAULT); +            LocalBroadcastManager.getInstance(activity).registerReceiver(eipFragmentBroadcastReceiver, updateIntentFilter); +            Log.d(TAG, "broadcast registered"); +        } else { +            Log.e(TAG, "activity null when setting up broadcast receiver"); +        } +    } +  } diff --git a/app/src/main/java/se/leap/bitmaskclient/FeatureVersionCode.java b/app/src/main/java/se/leap/bitmaskclient/FeatureVersionCode.java index b2a39c1a..969f006a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/FeatureVersionCode.java +++ b/app/src/main/java/se/leap/bitmaskclient/FeatureVersionCode.java @@ -2,4 +2,5 @@ package se.leap.bitmaskclient;  public interface FeatureVersionCode {      int MULTIPLE_PROFILES = 132; +    int RENAMED_EIP_IN_PREFERENCES = 132;  } diff --git a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java index 186c2928..c82cef3b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java @@ -5,23 +5,29 @@ 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;  import static se.leap.bitmaskclient.EipFragment.ASK_TO_CANCEL_VPN;  public class MainActivity extends AppCompatActivity { +    private static Provider provider = new Provider(); +    private static FragmentManagerEnhanced fragmentManager;      private SharedPreferences preferences; +    private NavigationDrawerFragment navigationDrawerFragment; +      public final static String ACTION_SHOW_VPN_FRAGMENT = "action_show_vpn_fragment";      /** @@ -32,13 +38,15 @@ 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(                  R.id.navigation_drawer, @@ -63,11 +71,12 @@ public class MainActivity extends AppCompatActivity {          switch (intent.getAction()) {              case ACTION_SHOW_VPN_FRAGMENT:                  fragment = new EipFragment(); +                Bundle bundle = new Bundle();                  if (intent.hasExtra(ASK_TO_CANCEL_VPN)) { -                    Bundle bundle = new Bundle();                      bundle.putBoolean(ASK_TO_CANCEL_VPN, true); -                    fragment.setArguments(bundle);                  } +                bundle.putParcelable(PROVIDER_KEY, provider); +                fragment.setArguments(bundle);                  break;              default:                  break; @@ -82,15 +91,39 @@ public class MainActivity extends AppCompatActivity {      @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, true); +                    break;              }          } + +        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..40b2ea7f 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,16 +69,10 @@ 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) { -        return initHttpClient(initError, certificate); -    } - -      private OkHttpClient initHttpClient(JSONObject initError, String certificate) {          try {              TLSCompatSocketFactory sslCompatFactory; diff --git a/app/src/main/java/se/leap/bitmaskclient/Provider.java b/app/src/main/java/se/leap/bitmaskclient/Provider.java index 5ff1949c..b3362409 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Provider.java +++ b/app/src/main/java/se/leap/bitmaskclient/Provider.java @@ -16,18 +16,21 @@   */  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.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.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; +import static se.leap.bitmaskclient.ProviderAPI.ERRORS;  /**   * @author Sean Leonard <meanderingcode@aetherislands.net> @@ -36,11 +39,18 @@ 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();      private DefaultedURL mainUrl = new DefaultedURL();      private DefaultedURL apiUrl = new DefaultedURL();      private String certificatePin = "";      private String certificatePinEncoding = "";      private String caCert = ""; +    private String apiVersion = ""; +    private String privateKey = ""; +    private String vpnCertificate = ""; + +    private boolean allowAnonymous; +    private boolean allowRegistered;      final public static String              API_URL = "api_uri", @@ -55,20 +65,20 @@ public final class Provider implements Parcelable {              NAME = "name",              DESCRIPTION = "description",              DOMAIN = "domain", -            MAIN_URL = "main_url", -            DOT_JSON_URL = "provider_json_url"; +            MAIN_URL = "main_url"; -    // Array of what API versions we understand -    protected static final String[] API_VERSIONS = {"1"};  // I assume we might encounter arbitrary version "numbers" -    // Some API pieces we want to know about -    private static final String API_TERM_SERVICES = "services";      private static final String API_TERM_NAME = "name"; -    private static final String API_TERM_DOMAIN = "domain"; -    private static final String API_TERM_DEFAULT_LANGUAGE = "default_language"; -    protected static final String[] API_EIP_TYPES = {"openvpn"};      public Provider() { } +    public Provider(String mainUrl) { +        try { +            this.mainUrl.setUrl(new URL(mainUrl)); +        } catch (MalformedURLException e) { +            this.mainUrl = new DefaultedURL(); +        } +    } +      public Provider(URL mainUrl) {          this.mainUrl.setUrl(mainUrl);      } @@ -108,16 +118,42 @@ public final class Provider implements Parcelable {                  !caCert.isEmpty();      } -    protected void setUrl(URL url) { +    public void setMainUrl(URL url) {          mainUrl.setUrl(url);      } -    protected void define(JSONObject provider_json) { -        definition = provider_json; -        parseDefinition(definition); +    public void setMainUrl(String url) { +        try { +            mainUrl.setUrl(new URL(url)); +        } catch (MalformedURLException e) { +            e.printStackTrace(); +        }      } -    protected JSONObject getDefinition() { +    public boolean define(JSONObject providerJson) { +       /* +        * fix against "api_uri": "https://calyx.net.malicious.url.net:4430", +        * This method aims to prevent attacks where the provider.json file got manipulated by a third party. +        * The main url should not change. +        */ + +        try { +            String providerApiUrl = providerJson.getString(Provider.API_URL); +            String providerDomain = providerJson.getString(Provider.DOMAIN); +            if (getMainUrlString().contains(providerDomain) && providerApiUrl.contains(providerDomain  + ":")) { +                definition = providerJson; +                parseDefinition(definition); +                return true; +            } else { +                return false; +            } +        } catch (JSONException e) { +            e.printStackTrace(); +            return false; +        } +    } + +    public JSONObject getDefinition() {          return definition;      } @@ -141,10 +177,17 @@ public final class Provider implements Parcelable {          return apiUrl;      } -    protected String certificatePin() { return certificatePin; } +    protected String getApiUrlWithVersion() { +        return getApiUrlString() + "/" + getApiVersion(); +    } + + +    protected String getApiUrlString() { +        return getApiUrl().toString(); +    } -    protected boolean hasCertificatePin() { -        return certificatePin != null && !certificatePin.isEmpty(); +    public String getApiVersion() { +        return apiVersion;      }      boolean hasCaCert() { @@ -200,26 +243,8 @@ 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 +                && !getEipServiceJson().has(ERRORS);      }      public boolean allowsRegistration() { @@ -237,19 +262,31 @@ 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(getEipServiceJsonString()); +        parcel.writeString(getPrivateKey()); +        parcel.writeString(getVpnCertificate());      }      @Override      public boolean equals(Object o) {          if (o instanceof Provider) {              Provider p = (Provider) o; -            return p.getDomain().equals(getDomain()); +            return p.getDomain().equals(getDomain()) && +            definition.toString().equals(p.getDefinition().toString()) && +            eipServiceJson.toString().equals(p.getEipServiceJson().toString())&& +            mainUrl.equals(p.getMainUrl()) && +            apiUrl.equals(p.getApiUrl()) && +            certificatePin.equals(p.getCertificatePin()) && +            certificatePinEncoding.equals(p.getCertificatePinEncoding()) && +            caCert.equals(p.getCaCert()) && +            apiVersion.equals(p.getApiVersion()) && +            privateKey.equals(p.getPrivateKey()) && +            vpnCertificate.equals(p.getVpnCertificate()) && +            allowAnonymous == p.allowsAnonymous() && +            allowRegistered == p.allowsRegistered();          } else return false;      } @@ -280,14 +317,26 @@ public final class Provider implements Parcelable {      private Provider(Parcel in) {          try {              mainUrl.setUrl(new URL(in.readString())); -            String definitionString = in.readString(); -            if (!definitionString.isEmpty()) { -                definition = new JSONObject((definitionString)); +            String tmpString = in.readString(); +            if (!tmpString.isEmpty()) { +                definition = new JSONObject((tmpString));                  parseDefinition(definition);              } -            String caCert = in.readString(); -            if (!caCert.isEmpty()) { -                this.caCert = caCert; +            tmpString = in.readString(); +            if (!tmpString.isEmpty()) { +                this.caCert = tmpString; +            } +            tmpString = in.readString(); +            if (!tmpString.isEmpty()) { +                this.setEipServiceJson(new JSONObject(tmpString)); +            } +            tmpString = in.readString(); +            if (!tmpString.isEmpty()) { +                this.setPrivateKey(tmpString); +            } +            tmpString = in.readString(); +            if (!tmpString.isEmpty()) { +                this.setVpnCertificate(tmpString);              }          } catch (MalformedURLException | JSONException e) {              e.printStackTrace(); @@ -300,15 +349,41 @@ public final class Provider implements Parcelable {              this.certificatePin = pin.split(":")[1].trim();              this.certificatePinEncoding = pin.split(":")[0].trim();              this.apiUrl.setUrl(new URL(definition.getString(API_URL))); +            this.allowAnonymous = definition.getJSONObject(Provider.SERVICE).getBoolean(PROVIDER_ALLOW_ANONYMOUS); +            this.allowRegistered = definition.getJSONObject(Provider.SERVICE).getBoolean(PROVIDER_ALLOWED_REGISTERED); +            this.apiVersion = 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 boolean allowsAnonymous() { +        return allowAnonymous; +    } + +    public boolean allowsRegistered() { +        return allowRegistered; +    } + +    public boolean setEipServiceJson(JSONObject eipServiceJson) { +        if (eipServiceJson.has(ERRORS)) { +            return false; +        } +        this.eipServiceJson = eipServiceJson; +        return true; +    } + +    public JSONObject getEipServiceJson() { +        return eipServiceJson; +    } + +    public String getEipServiceJsonString() { +        return getEipServiceJson().toString(); +    }      public boolean isDefault() {          return getMainUrl().isDefault() &&                  getApiUrl().isDefault() && @@ -317,4 +392,53 @@ public final class Provider implements Parcelable {                  caCert.isEmpty();      } +    public String getPrivateKey() { +        return privateKey; +    } + +    public void setPrivateKey(String privateKey) { +        this.privateKey = privateKey; +    } + +    public String getVpnCertificate() { +        return vpnCertificate; +    } + +    public void setVpnCertificate(String vpnCertificate) { +        this.vpnCertificate = vpnCertificate; +    } + +    public boolean hasVpnCertificate() { +        return getVpnCertificate() != null && getVpnCertificate().length() >0 ; +    } + +    public String getCertificatePin() { +        return certificatePin; +    } + +    public String getCertificatePinEncoding() { +        return certificatePinEncoding; +    } + +    public String getCaCertFingerprint() { +        return getCertificatePinEncoding() + ":" + getCertificatePin(); +    } + +    /** +     * resets everything except the main url +     */ +    public void reset() { +        definition = new JSONObject(); +        eipServiceJson = new JSONObject(); +        apiUrl = new DefaultedURL(); +        certificatePin = ""; +        certificatePinEncoding = ""; +        caCert = ""; +        apiVersion = ""; +        privateKey = ""; +        vpnCertificate = ""; +        allowRegistered = false; +        allowAnonymous = false; +    } +  } diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java index ccc71a67..b3399416 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java @@ -20,6 +20,7 @@ import android.annotation.SuppressLint;  import android.app.IntentService;  import android.content.Intent;  import android.content.SharedPreferences; +import android.support.v4.content.LocalBroadcastManager;  import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; @@ -40,22 +41,17 @@ 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"; +            DOWNLOAD_EIP_SERVICE = "ProviderAPI.DOWNLOAD_EIP_SERVICE", +            PROVIDER_SET_UP = "ProviderAPI.PROVIDER_SET_UP";      final public static int              SUCCESSFUL_LOGIN = 3, @@ -80,21 +76,6 @@ public class ProviderAPI extends IntentService implements ProviderApiManagerBase      }      //TODO: refactor me, please! -    public static void stop() { -        ProviderApiManager.stop(); -    } - -    //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() { @@ -110,7 +91,7 @@ public class ProviderAPI extends IntentService implements ProviderApiManagerBase      @Override      public void broadcastEvent(Intent intent) { -        sendBroadcast(intent); +        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);      }      @Override diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderAPICommand.java b/app/src/main/java/se/leap/bitmaskclient/ProviderAPICommand.java index 0e4cfe8a..65d01b22 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderAPICommand.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderAPICommand.java @@ -6,40 +6,74 @@ import android.os.*;  import org.jetbrains.annotations.*;  public class ProviderAPICommand { -    private static Context context; +    private Context context; -    private static String action; -    private static Bundle parameters; -    private static ResultReceiver result_receiver; +    private String action; +    private Bundle parameters; +    private ResultReceiver resultReceiver; +    private Provider provider; -    public static void initialize(Context context) { -        ProviderAPICommand.context = context; +    private ProviderAPICommand(@NotNull Context context, @NotNull String action, @NotNull Provider provider, ResultReceiver resultReceiver) { +        this(context, action, Bundle.EMPTY, provider, resultReceiver); +    } +    private ProviderAPICommand(@NotNull Context context, @NotNull String action, @NotNull Provider provider) { +        this(context, action, Bundle.EMPTY, provider);      } -    private static boolean isInitialized() { -        return context != null; +    private ProviderAPICommand(@NotNull Context context, @NotNull String action, @NotNull Bundle parameters, @NotNull Provider provider) { +        this(context, action, parameters, provider, null);      } -    public static void execute(Bundle parameters, @NotNull String action, @NotNull ResultReceiver result_receiver) throws IllegalStateException { -        if(!isInitialized()) throw new IllegalStateException(); +    private ProviderAPICommand(@NotNull Context context, @NotNull String action, @NotNull Bundle parameters, @NotNull Provider provider, @Nullable ResultReceiver resultReceiver) { +        super(); +        this.context = context; +        this.action = action; +        this.parameters = parameters; +        this.resultReceiver = resultReceiver; +        this.provider = provider; +    } -        ProviderAPICommand.action = action; -        ProviderAPICommand.parameters = parameters; -        ProviderAPICommand.result_receiver = result_receiver; +    private boolean isInitialized() { +        return context != null; +    } -        Intent intent = setUpIntent(); -        context.startService(intent); +    private void execute() { +        if (isInitialized()) { +            Intent intent = setUpIntent(); +            context.startService(intent); +        }      } -    private static Intent setUpIntent() { +    private Intent setUpIntent() {          Intent command = new Intent(context, ProviderAPI.class);          command.setAction(action);          command.putExtra(ProviderAPI.PARAMETERS, parameters); -        command.putExtra(ProviderAPI.RECEIVER_KEY, result_receiver); +        if (resultReceiver != null) { +            command.putExtra(ProviderAPI.RECEIVER_KEY, resultReceiver); +        } +        command.putExtra(Constants.PROVIDER_KEY, provider);          return command;      } +    public static void execute(Context context, String action, Provider provider) { +        ProviderAPICommand command = new ProviderAPICommand(context, action, provider); +        command.execute(); +    } +    public static void execute(Context context, String action, Bundle parameters, Provider provider) { +        ProviderAPICommand command = new ProviderAPICommand(context, action, parameters, provider); +        command.execute(); +    } + +    public static void execute(Context context, String action, Bundle parameters, Provider provider, ResultReceiver resultReceiver) { +        ProviderAPICommand command = new ProviderAPICommand(context, action, parameters, provider, resultReceiver); +        command.execute(); +    } + +    public static void execute(Context context, String action, Provider provider, ResultReceiver resultReceiver) { +        ProviderAPICommand command = new ProviderAPICommand(context, action, provider, resultReceiver); +        command.execute(); +    }  } diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java index 8117fb99..505ee55b 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,22 +50,22 @@ 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;  import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_CERTIFICATE;  import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_EIP_SERVICE;  import static se.leap.bitmaskclient.ProviderAPI.ERRORID; @@ -73,23 +74,19 @@ 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;  import static se.leap.bitmaskclient.ProviderAPI.SUCCESSFUL_LOGOUT;  import static se.leap.bitmaskclient.ProviderAPI.SUCCESSFUL_SIGNUP; -import static se.leap.bitmaskclient.ProviderAPI.UPDATE_PROGRESSBAR;  import static se.leap.bitmaskclient.ProviderAPI.UPDATE_PROVIDER_DETAILS;  import static se.leap.bitmaskclient.R.string.certificate_error;  import static se.leap.bitmaskclient.R.string.error_io_exception_user_message; @@ -109,36 +106,19 @@ 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; +    protected SharedPreferences preferences;      protected Resources resources; -    protected OkHttpClientGenerator clientGenerator; - -    public static void stop() { -        go_ahead = false; -    } - -    public static String lastProviderMainUrl() { -        return lastProviderMainUrl; -    } +    OkHttpClientGenerator clientGenerator; -    public ProviderApiManagerBase(SharedPreferences preferences, Resources resources, OkHttpClientGenerator clientGenerator, ProviderApiServiceCallback callback) { +    ProviderApiManagerBase(SharedPreferences preferences, Resources resources, OkHttpClientGenerator clientGenerator, ProviderApiServiceCallback callback) {          this.preferences = preferences;          this.resources = resources;          this.serviceCallback = callback; @@ -150,96 +130,92 @@ 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(); -            Bundle task = new Bundle(); -            String mainUrl = parameters.getString(Provider.MAIN_URL); -            if (mainUrl == null) { -                mainUrl = lastProviderMainUrl; -            } -            task.putString(MAIN_URL, mainUrl); -            Bundle result = setUpProvider(task); -            if (result.getBoolean(RESULT_KEY)) { -                sendToReceiverOrBroadcast(receiver, PROVIDER_OK, result); -            } else { -                sendToReceiverOrBroadcast(receiver, PROVIDER_NOK, result); -            } -        } else if (action.equalsIgnoreCase(SET_UP_PROVIDER)) { -            Bundle result = setUpProvider(parameters); -            if (go_ahead) { -                if (result.getBoolean(RESULT_KEY)) { -                    sendToReceiverOrBroadcast(receiver, PROVIDER_OK, result); +        Bundle result = new Bundle(); +        switch (action) { +            case UPDATE_PROVIDER_DETAILS: +                resetProviderDetails(provider); +                Bundle task = new Bundle(); +                result = setUpProvider(provider, task); +                if (result.getBoolean(BROADCAST_RESULT_KEY)) { +                    sendToReceiverOrBroadcast(receiver, PROVIDER_OK, result, provider);                  } else { -                    sendToReceiverOrBroadcast(receiver, PROVIDER_NOK, result); +                    sendToReceiverOrBroadcast(receiver, PROVIDER_NOK, result, provider);                  } -            } -        } else if (action.equalsIgnoreCase(SIGN_UP)) { -            UserStatus.updateStatus(UserStatus.SessionStatus.SIGNING_UP, resources); -            Bundle result = tryToRegister(parameters); -            if (result.getBoolean(RESULT_KEY)) { -                sendToReceiverOrBroadcast(receiver, SUCCESSFUL_SIGNUP, result); -            } else { -                sendToReceiverOrBroadcast(receiver, FAILED_SIGNUP, result); -            } -        } 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); -            } else { -                sendToReceiverOrBroadcast(receiver, FAILED_LOGIN, result); -                UserStatus.updateStatus(UserStatus.SessionStatus.NOT_LOGGED_IN, resources); -            } -        } 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); -            } else { -                sendToReceiverOrBroadcast(receiver, LOGOUT_FAILED, Bundle.EMPTY); -                UserStatus.updateStatus(UserStatus.SessionStatus.DIDNT_LOG_OUT, resources); -            } -        } else if (action.equalsIgnoreCase(DOWNLOAD_CERTIFICATE)) { -            if (updateVpnCertificate()) { -                sendToReceiverOrBroadcast(receiver, CORRECTLY_DOWNLOADED_CERTIFICATE, Bundle.EMPTY); -            } else { -                sendToReceiverOrBroadcast(receiver, INCORRECTLY_DOWNLOADED_CERTIFICATE, Bundle.EMPTY); -            } -        } else if (action.equalsIgnoreCase(DOWNLOAD_EIP_SERVICE)) { -            Bundle result = getAndSetEipServiceJson(); -            if (result.getBoolean(RESULT_KEY)) { -                sendToReceiverOrBroadcast(receiver, CORRECTLY_DOWNLOADED_EIP_SERVICE, result); -            } else { -                sendToReceiverOrBroadcast(receiver, INCORRECTLY_DOWNLOADED_EIP_SERVICE, result); -            } -        } else if (action.equalsIgnoreCase(PROVIDER_SET_UP)) { -            if(EIP_SERVICE_JSON_DOWNLOADED && CA_CERT_DOWNLOADED && PROVIDER_JSON_DOWNLOADED ) { -                if(receiver!= null) { -                    sendToReceiverOrBroadcast(receiver, PROVIDER_OK, Bundle.EMPTY); +                break; +            case SET_UP_PROVIDER: +                result = setUpProvider(provider, parameters); +                if (result.getBoolean(BROADCAST_RESULT_KEY)) { +                    sendToReceiverOrBroadcast(receiver, PROVIDER_OK, result, provider); +                } else { +                    sendToReceiverOrBroadcast(receiver, PROVIDER_NOK, result, provider);                  } -            } +                break; +            case SIGN_UP: +                result = tryToRegister(parameters); +                if (result.getBoolean(BROADCAST_RESULT_KEY)) { +                    sendToReceiverOrBroadcast(receiver, SUCCESSFUL_SIGNUP, result, provider); +                } else { +                    sendToReceiverOrBroadcast(receiver, FAILED_SIGNUP, result, provider); +                } +                break; +            case LOG_IN: +                result = tryToAuthenticate(provider, parameters); +                if (result.getBoolean(BROADCAST_RESULT_KEY)) { +                    sendToReceiverOrBroadcast(receiver, SUCCESSFUL_LOGIN, result, provider); +                } else { +                    sendToReceiverOrBroadcast(receiver, FAILED_LOGIN, result, provider); +                } +                break; +            case LOG_OUT: +                if (logOut(provider)) { +                    sendToReceiverOrBroadcast(receiver, SUCCESSFUL_LOGOUT, Bundle.EMPTY, provider); +                } else { +                    sendToReceiverOrBroadcast(receiver, LOGOUT_FAILED, Bundle.EMPTY, provider); +                } +                break; +            case DOWNLOAD_CERTIFICATE: +                if (updateVpnCertificate(provider)) { +                    sendToReceiverOrBroadcast(receiver, CORRECTLY_DOWNLOADED_CERTIFICATE, Bundle.EMPTY, provider); +                } else { +                    sendToReceiverOrBroadcast(receiver, INCORRECTLY_DOWNLOADED_CERTIFICATE, Bundle.EMPTY, provider); +                } +                break; +            case DOWNLOAD_EIP_SERVICE: +                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, provider); +                } +                break; +            case PROVIDER_SET_UP: +                if(provider.hasEIP() && provider.hasCaCert() && provider.hasDefinition()) { +                    if(receiver!= null) { +                        result.putParcelable(PROVIDER_KEY, provider); +                        receiver.send(PROVIDER_OK, result); +                    } +                } +                break;          }      } -    protected void resetProviderDetails() { -        CA_CERT_DOWNLOADED = PROVIDER_JSON_DOWNLOADED = false; -        deleteProviderDetailsFromPreferences(providerDefinition); -        providerCaCert = ""; -        providerDefinition = new JSONObject(); +    void resetProviderDetails(Provider provider) { +        provider.reset(); +        ConfigHelper.deleteProviderDetailsFromPreferences(preferences, provider.getDomain());      } -    protected String formatErrorMessage(final int toastStringId) { +    String formatErrorMessage(final int toastStringId) {          return formatErrorMessage(resources.getString(toastStringId));      } @@ -256,7 +232,7 @@ public abstract class ProviderApiManagerBase {          }      } -    protected void addErrorMessageToJson(JSONObject jsonObject, String errorMessage) { +    private void addErrorMessageToJson(JSONObject jsonObject, String errorMessage) {          try {              jsonObject.put(ERRORS, errorMessage);          } catch (JSONException e) { @@ -264,7 +240,7 @@ public abstract class ProviderApiManagerBase {          }      } -    protected void addErrorMessageToJson(JSONObject jsonObject, String errorMessage, String errorId) { +    private void addErrorMessageToJson(JSONObject jsonObject, String errorMessage, String errorId) {          try {              jsonObject.put(ERRORS, errorMessage);              jsonObject.put(ERRORID, errorId); @@ -278,32 +254,37 @@ public abstract class ProviderApiManagerBase {      private Bundle tryToRegister(Bundle task) {          Bundle result = new Bundle(); -        int progress = 0; -        String username = User.userName(); -        String password = task.getString(SessionDialog.PASSWORD); +        String username = task.getString(CREDENTIALS_USERNAME); +        String password = task.getString(CREDENTIALS_PASSWORD); +        Provider provider = task.getParcelable(PROVIDER_KEY); + +        if(provider == null) { +            result.putBoolean(BROADCAST_RESULT_KEY, false); +            Log.e(TAG, "no provider when trying to register"); +            return result; +        }          if (validUserLoginData(username, password)) { -            result = register(username, password); -            broadcastProgress(progress++); +            result = register(provider, username, password);          } else {              if (!wellFormedPassword(password)) { -                result.putBoolean(RESULT_KEY, false); -                result.putString(SessionDialog.USERNAME, username); -                result.putBoolean(SessionDialog.ERRORS.PASSWORD_INVALID_LENGTH.toString(), true); +                result.putBoolean(BROADCAST_RESULT_KEY, false); +                result.putString(CREDENTIALS_USERNAME, username); +                result.putBoolean(CREDENTIAL_ERRORS.PASSWORD_INVALID_LENGTH.toString(), true);              }              if (!validUsername(username)) { -                result.putBoolean(RESULT_KEY, false); -                result.putBoolean(SessionDialog.ERRORS.USERNAME_MISSING.toString(), true); +                result.putBoolean(BROADCAST_RESULT_KEY, false); +                result.putBoolean(CREDENTIAL_ERRORS.USERNAME_MISSING.toString(), true);              }          }          return result;      } -    private Bundle register(String username, String password) { +    private Bundle register(Provider provider, String username, String password) {          JSONObject stepResult = null; -        OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(stepResult); +        OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), stepResult);          if (okHttpClient == null) {              return authFailedNotification(stepResult, username);          } @@ -313,15 +294,15 @@ public abstract class ProviderApiManagerBase {          BigInteger password_verifier = client.calculateV(username, password, salt); -        JSONObject api_result = sendNewUserDataToSRPServer(providerApiUrl, username, new BigInteger(1, salt).toString(16), password_verifier.toString(16), okHttpClient); +        JSONObject api_result = sendNewUserDataToSRPServer(provider.getApiUrlString(), username, new BigInteger(1, salt).toString(16), password_verifier.toString(16), okHttpClient);          Bundle result = new Bundle();          if (api_result.has(ERRORS))              result = authFailedNotification(api_result, username);          else { -            result.putString(SessionDialog.USERNAME, username); -            result.putString(SessionDialog.PASSWORD, password); -            result.putBoolean(RESULT_KEY, true); +            result.putString(CREDENTIALS_USERNAME, username); +            result.putString(CREDENTIALS_PASSWORD, password); +            result.putBoolean(BROADCAST_RESULT_KEY, true);          }          return result; @@ -330,37 +311,39 @@ public abstract class ProviderApiManagerBase {      /**       * Starts the authentication process using SRP protocol.       * -     * @param task containing: username, password and api url. -     * @return a bundle with a boolean value mapped to a key named RESULT_KEY, and which is true if authentication was successful. +     * @param task containing: username, password and provider +     * @return a bundle with a boolean value mapped to a key named BROADCAST_RESULT_KEY, and which is true if authentication was successful.       */ -    private Bundle tryToAuthenticate(Bundle task) { +    private Bundle tryToAuthenticate(Provider provider, Bundle task) {          Bundle result = new Bundle(); -        int progress = 0; -        String username = User.userName(); -        String password = task.getString(SessionDialog.PASSWORD); +        String username = task.getString(CREDENTIALS_USERNAME); +        String password = task.getString(CREDENTIALS_PASSWORD); +          if (validUserLoginData(username, password)) { -            result = authenticate(username, password); -            broadcastProgress(progress++); +            result = authenticate(provider, username, password);          } else {              if (!wellFormedPassword(password)) { -                result.putBoolean(RESULT_KEY, false); -                result.putString(SessionDialog.USERNAME, username); -                result.putBoolean(SessionDialog.ERRORS.PASSWORD_INVALID_LENGTH.toString(), true); +                result.putBoolean(BROADCAST_RESULT_KEY, false); +                result.putString(CREDENTIALS_USERNAME, username); +                result.putBoolean(CREDENTIAL_ERRORS.PASSWORD_INVALID_LENGTH.toString(), true);              }              if (!validUsername(username)) { -                result.putBoolean(RESULT_KEY, false); -                result.putBoolean(SessionDialog.ERRORS.USERNAME_MISSING.toString(), true); +                result.putBoolean(BROADCAST_RESULT_KEY, false); +                result.putBoolean(CREDENTIAL_ERRORS.USERNAME_MISSING.toString(), true);              }          }          return result;      } -    private Bundle authenticate(String username, String password) { +    private Bundle authenticate(Provider provider, String username, String password) {          Bundle result = new Bundle();          JSONObject stepResult = new JSONObject(); -        OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(stepResult); + +        String providerApiUrl = provider.getApiUrlWithVersion(); + +        OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), stepResult);          if (okHttpClient == null) {              return authFailedNotification(stepResult, username);          } @@ -378,13 +361,13 @@ public abstract class ProviderApiManagerBase {                  setTokenIfAvailable(step_result);                  byte[] M2 = new BigInteger(step_result.getString(LeapSRPSession.M2), 16).toByteArray();                  if (client.verify(M2)) { -                    result.putBoolean(RESULT_KEY, true); +                    result.putBoolean(BROADCAST_RESULT_KEY, true);                  } else {                      authFailedNotification(step_result, username);                  }              } else { -                result.putBoolean(RESULT_KEY, false); -                result.putString(SessionDialog.USERNAME, username); +                result.putBoolean(BROADCAST_RESULT_KEY, false); +                result.putString(CREDENTIALS_USERNAME, username);                  result.putString(resources.getString(R.string.user_message), resources.getString(R.string.error_srp_math_error_user_message));              }          } catch (JSONException e) { @@ -428,38 +411,29 @@ public abstract class ProviderApiManagerBase {          }          if (!username.isEmpty()) -            userNotificationBundle.putString(SessionDialog.USERNAME, username); -        userNotificationBundle.putBoolean(RESULT_KEY, false); +            userNotificationBundle.putString(CREDENTIALS_USERNAME, username); +        userNotificationBundle.putBoolean(BROADCAST_RESULT_KEY, false);          return userNotificationBundle;      } -    void sendToReceiverOrBroadcast(ResultReceiver receiver, int resultCode, Bundle resultData) { +    void sendToReceiverOrBroadcast(ResultReceiver receiver, int resultCode, Bundle resultData, Provider provider) { +        if (resultData == null || resultData == Bundle.EMPTY) { +            resultData = new Bundle(); +        } +        resultData.putParcelable(PROVIDER_KEY, provider);          if (receiver != null) {              receiver.send(resultCode, resultData);          } else { -            broadcastEvent(PROVIDER_API_EVENT, resultCode, resultData); +            broadcastEvent(resultCode, resultData);          }      } -    /** -     * Sets up an intent with the progress value passed as a parameter -     * and sends it as a broadcast. -     * -     * @param progress -     */ -    void broadcastProgress(int progress) { -        Intent intentUpdate = new Intent(UPDATE_PROGRESSBAR); +    private void broadcastEvent(int resultCode , Bundle resultData) { +        Intent intentUpdate = new Intent(BROADCAST_PROVIDER_API_EVENT);          intentUpdate.addCategory(Intent.CATEGORY_DEFAULT); -        intentUpdate.putExtra(CURRENT_PROGRESS, progress); -        serviceCallback.broadcastEvent(intentUpdate); -    } - -    void broadcastEvent(String action, int resultCode , Bundle resultData) { -        Intent intentUpdate = new Intent(action); -        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);      } @@ -595,7 +569,7 @@ public abstract class ProviderApiManagerBase {          JSONObject errorJson = new JSONObject();          String baseUrl = getApiUrl(providerDefinition); -        OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(errorJson, caCert); +        OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(caCert, errorJson);          if (okHttpClient == null) {              result.putString(ERRORS, errorJson.toString());              return false; @@ -627,28 +601,24 @@ public abstract class ProviderApiManagerBase {      /**       * Downloads a provider.json from a given URL, adding a new provider using the given name.       * -     * @param task containing a boolean meaning if the provider is custom or not, another boolean meaning if the user completely trusts this provider, the provider name and its provider.json url. -     * @return a bundle with a boolean value mapped to a key named RESULT_KEY, and which is true if the update was successful. +     * @param task containing a boolean meaning if the provider is custom or not, another boolean meaning if the user completely trusts this provider +     * @return a bundle with a boolean value mapped to a key named BROADCAST_RESULT_KEY, and which is true if the update was successful.       */ -    protected abstract Bundle setUpProvider(Bundle task); +    protected abstract Bundle setUpProvider(Provider provider, Bundle task);      /**       * Downloads the eip-service.json from a given URL, and saves eip service capabilities including the offered gateways -     * @return a bundle with a boolean value mapped to a key named RESULT_KEY, and which is true if the download was successful. +     * @return a bundle with a boolean value mapped to a key named BROADCAST_RESULT_KEY, and which is true if the download was successful.       */ -    protected abstract Bundle getAndSetEipServiceJson(); +    protected abstract Bundle getAndSetEipServiceJson(Provider provider);      /**       * Downloads a new OpenVPN certificate, attaching authenticated cookie for authenticated certificate.       *       * @return true if certificate was downloaded correctly, false if provider.json is not present in SharedPreferences, or if the certificate url could not be parsed as a URI, or if there was an SSL error.       */ -    protected abstract boolean updateVpnCertificate(); - +    protected abstract boolean updateVpnCertificate(Provider provider); -    protected static boolean caCertDownloaded() { -        return CA_CERT_DOWNLOADED; -    }      protected boolean isValidJson(String jsonString) {          try { @@ -662,19 +632,19 @@ public abstract class ProviderApiManagerBase {          }      } -    protected boolean validCertificate(String cert_string) { +    protected boolean validCertificate(Provider provider, String certString) {          boolean result = false; -        if (!ConfigHelper.checkErroneousDownload(cert_string)) { -            X509Certificate certificate = ConfigHelper.parseX509CertificateFromString(cert_string); +        if (!ConfigHelper.checkErroneousDownload(certString)) { +            X509Certificate certificate = ConfigHelper.parseX509CertificateFromString(certString);              try {                  if (certificate != null) { -                    JSONObject provider_json = new JSONObject(preferences.getString(Provider.KEY, "")); -                    String fingerprint = provider_json.getString(Provider.CA_CERT_FINGERPRINT); +                    JSONObject providerJson = provider.getDefinition(); +                    String fingerprint = providerJson.getString(Provider.CA_CERT_FINGERPRINT);                      String encoding = fingerprint.split(":")[0]; -                    String expected_fingerprint = fingerprint.split(":")[1]; -                    String real_fingerprint = getFingerprintFromCertificate(certificate, encoding); +                    String expectedFingerprint = fingerprint.split(":")[1]; +                    String realFingerprint = getFingerprintFromCertificate(certificate, encoding); -                    result = real_fingerprint.trim().equalsIgnoreCase(expected_fingerprint.trim()); +                    result = realFingerprint.trim().equalsIgnoreCase(expectedFingerprint.trim());                  } else                      result = false;              } catch (JSONException | NoSuchAlgorithmException | CertificateEncodingException e) { @@ -685,69 +655,54 @@ 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.setPrivateKey(getPersistedPrivateKey(providerDomain)); +            provider.setVpnCertificate(getPersistedVPNCertificate(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); -        if (ConfigHelper.checkErroneousDownload(cert_string)) { +        String caCert = provider.getCaCert(); + +        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());          }          try {              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())) { -                return setErrorResult(result, warning_corrupted_provider_cert, ERROR_CERTIFICATE_PINNING.toString()); -            } +            String encoding = provider.getCertificatePinEncoding(); +            String expectedFingerprint = provider.getCertificatePin(); - -            if (!hasApiUrlExpectedDomain(providerDefinition, mainUrl)){ -                return setErrorResult(result, warning_corrupted_provider_details, ERROR_CORRUPTED_PROVIDER_JSON.toString()); +            String realFingerprint = getFingerprintFromCertificate(certificate, encoding); +            if (!realFingerprint.trim().equalsIgnoreCase(expectedFingerprint.trim())) { +                return setErrorResult(result, warning_corrupted_provider_cert, ERROR_CERTIFICATE_PINNING.toString());              } -            if (!canConnect(cert_string, providerDefinition, result)) { +            if (!canConnect(caCert, provider.getDefinition(), result)) {                  return result;              }          } catch (NoSuchAlgorithmException e ) { @@ -758,11 +713,11 @@ public abstract class ProviderApiManagerBase {              return setErrorResult(result, warning_expired_provider_cert, ERROR_INVALID_CERTIFICATE.toString());          } -        result.putBoolean(RESULT_KEY, true); +        result.putBoolean(BROADCAST_RESULT_KEY, true);          return result;      } -    protected Bundle setErrorResult(Bundle result, int errorMessageId, String errorId) { +    Bundle setErrorResult(Bundle result, int errorMessageId, String errorId) {          JSONObject errorJson = new JSONObject();          if (errorId != null) {              addErrorMessageToJson(errorJson, resources.getString(errorMessageId), errorId); @@ -770,38 +725,10 @@ 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;      } -    /** -     * This method aims to prevent attacks where the provider.json file got manipulated by a third party. -     * The main url is visible to the provider when setting up a new provider. -     * The user is responsible to check that this is the provider main url he intends to connect to. -     * -     * @param providerDefinition -     * @param mainUrlString -     * @return -     */ -    private boolean hasApiUrlExpectedDomain(JSONObject providerDefinition, String mainUrlString) { -        //  fix against "api_uri": "https://calyx.net.malicious.url.net:4430", -        String apiUrlString = getApiUrl(providerDefinition); -        String providerDomain = getProviderDomain(providerDefinition); -        if (mainUrlString.contains(providerDomain) && apiUrlString.contains(providerDomain  + ":")) { -            return true; -        } -        return false; -    } - -    protected String getCaCertFingerprint(JSONObject providerDefinition) { -        try { -            return providerDefinition.getString(Provider.CA_CERT_FINGERPRINT); -        } catch (JSONException e) { -            e.printStackTrace(); -        } -        return ""; -    } -      protected String getApiUrl(JSONObject providerDefinition) {          try {              return providerDefinition.getString(Provider.API_URL); @@ -811,41 +738,17 @@ 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 String getPersistedPrivateKey(String providerDomain) { +        return ConfigHelper.getFromPersistedProvider(PROVIDER_PRIVATE_KEY, providerDomain, preferences);      } -    protected void deleteProviderDetailsFromPreferences(JSONObject providerDefinition) { -        String providerDomain = getProviderDomain(providerDefinition); - -        if (preferences.contains(Provider.KEY + "." + providerDomain)) { -            preferences.edit().remove(Provider.KEY + "." + providerDomain).apply(); -        } -        if (preferences.contains(Provider.CA_CERT + "." + providerDomain)) { -            preferences.edit().remove(Provider.CA_CERT + "." + providerDomain).apply(); -        } -        if (preferences.contains(Provider.CA_CERT_FINGERPRINT + "." + providerDomain)) { -            preferences.edit().remove(Provider.CA_CERT_FINGERPRINT + "." + providerDomain).apply(); -        } -    } - -    protected String getPersistedCaCertFingerprint(String providerDomain) { -        try { -            return getPersistedProviderDefinition(providerDomain).getString(Provider.CA_CERT_FINGERPRINT); -        } catch (JSONException e) { -            e.printStackTrace(); -        } -        return ""; +    protected String getPersistedVPNCertificate(String providerDomain) { +        return ConfigHelper.getFromPersistedProvider(PROVIDER_VPN_CERTIFICATE, providerDomain, preferences);      }      protected JSONObject getPersistedProviderDefinition(String providerDomain) {          try { -            return new JSONObject(preferences.getString(Provider.KEY + "." + providerDomain, "")); +            return new JSONObject(ConfigHelper.getFromPersistedProvider(Provider.KEY, providerDomain, preferences));          } catch (JSONException e) {              e.printStackTrace();              return new JSONObject(); @@ -856,16 +759,6 @@ public abstract class ProviderApiManagerBase {          return preferences.getString(Provider.CA_CERT + "." + providerDomain, "");      } -    protected String getProviderDomain(JSONObject providerDefinition) { -        try { -            return providerDefinition.getString(Provider.DOMAIN); -        } catch (JSONException e) { -            e.printStackTrace(); -        } - -        return ""; -    } -      protected boolean hasUpdatedProviderDetails(String domain) {          return preferences.contains(Provider.KEY + "." + domain) && preferences.contains(Provider.CA_CERT + "." + domain);      } @@ -879,22 +772,22 @@ public abstract class ProviderApiManagerBase {       * Interprets the error message as a JSON object and extract the "errors" keyword pair.       * If the error message is not a JSON object, then it is returned untouched.       * -     * @param string_json_error_message +     * @param stringJsonErrorMessage       * @return final error message       */ -    protected String pickErrorMessage(String string_json_error_message) { -        String error_message = ""; +    protected String pickErrorMessage(String stringJsonErrorMessage) { +        String errorMessage = "";          try { -            JSONObject json_error_message = new JSONObject(string_json_error_message); -            error_message = json_error_message.getString(ERRORS); +            JSONObject jsonErrorMessage = new JSONObject(stringJsonErrorMessage); +            errorMessage = jsonErrorMessage.getString(ERRORS);          } catch (JSONException e) {              // TODO Auto-generated catch block -            error_message = string_json_error_message; +            errorMessage = stringJsonErrorMessage;          } catch (NullPointerException e) {              //do nothing          } -        return error_message; +        return errorMessage;      }      @NonNull @@ -907,25 +800,22 @@ 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;          }          return false;      } -    //FIXME: don't save private keys in shared preferences! use the keystore -    protected boolean loadCertificate(String cert_string) { +    protected boolean loadCertificate(Provider provider, String cert_string) {          if (cert_string == null) {              return false;          } @@ -944,13 +834,13 @@ public abstract class ProviderApiManagerBase {              RSAPrivateKey key = ConfigHelper.parseRsaKeyFromString(keyString);              keyString = Base64.encodeToString(key.getEncoded(), Base64.DEFAULT); -            preferences.edit().putString(PROVIDER_PRIVATE_KEY, "-----BEGIN RSA PRIVATE KEY-----\n" + keyString + "-----END RSA PRIVATE KEY-----").commit(); +            provider.setPrivateKey( "-----BEGIN RSA PRIVATE KEY-----\n" + keyString + "-----END RSA PRIVATE KEY-----");              X509Certificate certificate = ConfigHelper.parseX509CertificateFromString(certificateString);              certificateString = Base64.encodeToString(certificate.getEncoded(), Base64.DEFAULT); -            preferences.edit().putString(PROVIDER_VPN_CERTIFICATE, "-----BEGIN CERTIFICATE-----\n" + certificateString + "-----END CERTIFICATE-----").commit(); +            provider.setVpnCertificate( "-----BEGIN CERTIFICATE-----\n" + certificateString + "-----END CERTIFICATE-----");              return true; -        } catch (CertificateException e) { +        } catch (CertificateException | NullPointerException e) {              // TODO Auto-generated catch block              e.printStackTrace();              return false; diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java index 172c52d3..7714e979 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java @@ -11,6 +11,7 @@ import android.support.annotation.Nullable;  import android.support.annotation.StringRes;  import android.support.design.widget.TextInputEditText;  import android.support.design.widget.TextInputLayout; +import android.support.v4.content.LocalBroadcastManager;  import android.support.v7.widget.AppCompatButton;  import android.support.v7.widget.AppCompatTextView;  import android.text.Editable; @@ -28,17 +29,21 @@ import org.json.JSONException;  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.Constants.PROVIDER_KEY; +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 @@ -92,9 +97,9 @@ 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); +        LocalBroadcastManager.getInstance(this).registerReceiver(providerAPIBroadcastReceiver, updateIntentFilter);          setUpListeners();          if(savedInstanceState != null) { @@ -152,7 +157,7 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc      protected void onDestroy() {          super.onDestroy();          if (providerAPIBroadcastReceiver != null) -            unregisterReceiver(providerAPIBroadcastReceiver); +            LocalBroadcastManager.getInstance(this).unregisterReceiver(providerAPIBroadcastReceiver);      }      @OnClick(R.id.button) @@ -186,34 +191,28 @@ 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); +    void downloadVpnCertificate(Provider handledProvider) { +        provider = handledProvider; +        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;      } @@ -324,17 +323,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))) { @@ -364,7 +363,8 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc          hideProgressBar();      } -    private void successfullyFinished() { +    private void successfullyFinished(Provider handledProvider) { +        provider = handledProvider;          Intent resultData = new Intent();          resultData.putExtra(Provider.KEY, provider);          setResult(RESULT_OK, resultData); @@ -377,23 +377,26 @@ 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); +            Bundle resultData = intent.getParcelableExtra(BROADCAST_RESULT_KEY); +            Provider handledProvider = resultData.getParcelable(PROVIDER_KEY); +              switch (resultCode) {                  case ProviderAPI.SUCCESSFUL_SIGNUP:                  case ProviderAPI.SUCCESSFUL_LOGIN: -                    downloadVpnCertificate(); +                    downloadVpnCertificate(handledProvider);                      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: -                    successfullyFinished(); +                    successfullyFinished(handledProvider);                      //activity.eip_fragment.updateEipService();                      break;                  case ProviderAPI.INCORRECTLY_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 fdf8df3c..41d2d849 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java @@ -23,8 +23,10 @@ import android.content.Intent;  import android.content.IntentFilter;  import android.os.Bundle;  import android.os.Handler; +import android.support.annotation.NonNull;  import android.support.v4.app.DialogFragment;  import android.support.v4.app.FragmentTransaction; +import android.support.v4.content.LocalBroadcastManager;  import android.util.Log;  import android.view.Menu;  import android.widget.ListView; @@ -32,6 +34,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; @@ -44,20 +47,20 @@ import butterknife.InjectView;  import butterknife.OnItemClick;  import se.leap.bitmaskclient.fragments.AboutFragment; -import static android.view.View.GONE;  import static se.leap.bitmaskclient.Constants.APP_ACTION_QUIT; -import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; +import static se.leap.bitmaskclient.Constants.BROADCAST_PROVIDER_API_EVENT; +import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; +import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY;  import static se.leap.bitmaskclient.Constants.PROVIDER_KEY;  import static se.leap.bitmaskclient.Constants.REQUEST_CODE_CONFIGURE_LEAP;  import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_CERTIFICATE;  import static se.leap.bitmaskclient.ProviderAPI.ERRORS;  import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE; -import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_API_EVENT;  import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_NOK;  import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_OK;  import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_SET_UP; -import static se.leap.bitmaskclient.ProviderAPI.RESULT_CODE; -import static se.leap.bitmaskclient.ProviderAPI.RESULT_KEY; +import static se.leap.bitmaskclient.ProviderAPI.UPDATE_PROVIDER_DETAILS;  /**   * abstract base Activity that builds and shows the list of known available providers. @@ -100,7 +103,7 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity      private boolean isActivityShowing;      private String reasonToFail; -    public abstract void retrySetUpProvider(); +    public abstract void retrySetUpProvider(@NonNull Provider provider);      protected abstract void onItemSelectedLogic(); @@ -116,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) { @@ -139,7 +142,6 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity          if (savedInstanceState != null)              restoreState(savedInstanceState); -        setUpProviderAPIResultReceiver();      }      private void restoreState(Bundle savedInstanceState) { @@ -163,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())) { @@ -185,13 +188,13 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity      protected void onPause() {          super.onPause();          isActivityShowing = false; +        if (providerAPIBroadcastReceiver != null) +            LocalBroadcastManager.getInstance(this).unregisterReceiver(providerAPIBroadcastReceiver);      }      @Override      protected void onDestroy() {          super.onDestroy(); -        if (providerAPIBroadcastReceiver != null) -            unregisterReceiver(providerAPIBroadcastReceiver);          providerAPIResultReceiver = null;      } @@ -208,25 +211,17 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity      private void setUpProviderAPIResultReceiver() {          providerAPIResultReceiver = new ProviderAPIResultReceiver(new Handler(), this);          providerAPIBroadcastReceiver = new ProviderAPIBroadcastReceiver(); -        IntentFilter updateIntentFilter = new IntentFilter(PROVIDER_API_EVENT); + +        IntentFilter updateIntentFilter = new IntentFilter(BROADCAST_PROVIDER_API_EVENT);          updateIntentFilter.addCategory(Intent.CATEGORY_DEFAULT); -        registerReceiver(providerAPIBroadcastReceiver, updateIntentFilter); +        LocalBroadcastManager.getInstance(this).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(); @@ -234,29 +229,25 @@ 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); -          reasonToFail = resultData.getString(ERRORS);          showDownloadFailedDialog();      } -    void handleCorrectlyDownloadedCertificate() { +    void handleCorrectlyDownloadedCertificate(Provider handledProvider) { +        this.provider = handledProvider;          showProviderDetails();      }      void handleIncorrectlyDownloadedCertificate() { -        mConfigState.setAction(PROVIDER_NOT_SET); -        hideProgressBar(); +        cancelSettingUpProvider();          setResult(RESULT_CANCELED, mConfigState);      }      @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. @@ -294,37 +285,22 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity      }      private void stopSettingUpProvider() { -        ProviderAPI.stop(); -        loadingScreen.setVisibility(GONE); -          cancelSettingUpProvider();      }      @Override      public void cancelSettingUpProvider() { -        hideProgressBar();          mConfigState.setAction(PROVIDER_NOT_SET); -        preferences.edit().remove(Provider.KEY).remove(PROVIDER_ALLOW_ANONYMOUS).remove(PROVIDER_KEY).apply(); +        hideProgressBar();      }      @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, provider);      }      public void checkProviderSetUp() { -        Intent providerAPICommand = new Intent(this, ProviderAPI.class); -        providerAPICommand.setAction(PROVIDER_SET_UP); -        providerAPICommand.putExtra(ProviderAPI.RECEIVER_KEY, providerAPIResultReceiver); -        startService(providerAPICommand); +        ProviderAPICommand.execute(this, PROVIDER_SET_UP, provider, providerAPIResultReceiver);      }      private void askDashboardToQuitApp() { @@ -337,29 +313,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);      } @@ -372,10 +349,10 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity              DialogFragment newFragment;              try {                  JSONObject errorJson = new JSONObject(reasonToFail); -                newFragment = DownloadFailedDialog.newInstance(errorJson); +                newFragment = DownloadFailedDialog.newInstance(provider, errorJson);              } catch (JSONException e) {                  e.printStackTrace(); -                newFragment = DownloadFailedDialog.newInstance(reasonToFail); +                newFragment = DownloadFailedDialog.newInstance(provider, reasonToFail);              }              newFragment.show(fragmentTransaction, DownloadFailedDialog.TAG);          } catch (IllegalStateException e) { @@ -422,51 +399,33 @@ public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity              Log.d(TAG, "received Broadcast");              String action = intent.getAction(); -            if (action == null || !action.equalsIgnoreCase(PROVIDER_API_EVENT)) { +            if (action == null || !action.equalsIgnoreCase(BROADCAST_PROVIDER_API_EVENT)) {                  return;              }              if (mConfigState.getAction() != null &&                      mConfigState.getAction().equalsIgnoreCase(SETTING_UP_PROVIDER)) { -                int resultCode = intent.getIntExtra(RESULT_CODE, -1); +                int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, -1);                  Log.d(TAG, "Broadcast resultCode: " + Integer.toString(resultCode)); -                Bundle resultData = intent.getParcelableExtra(RESULT_KEY); -                String handledProvider = resultData.getString(Provider.KEY); - -                String providerName = ConfigHelper.getProviderName(handledProvider); -                String providerDomain = ConfigHelper.getProviderDomain(handledProvider); - -                //FIXME: remove that lines as soon as Provider gets sent via broadcast -                // and make sure providers are the same - remove providersMatch -                if (resultCode == PROVIDER_OK && handledProvider == null) { -                    providerName = ConfigHelper.getProviderName(preferences); -                    providerDomain = ConfigHelper.getProviderDomain(preferences); -                } -                boolean providersMatch = true; -                if (providerDomain != null) { -                    providersMatch = providerDomain.equalsIgnoreCase(provider.getDomain()); -                } -                if (providerName != null && !providersMatch) { -                    providersMatch = providerName.equalsIgnoreCase(provider.getName()); -                } - +                Bundle resultData = intent.getParcelableExtra(BROADCAST_RESULT_KEY); +                Provider handledProvider = resultData.getParcelable(PROVIDER_KEY); -                switch (resultCode) { -                    case PROVIDER_OK: -                        if (providersMatch) -                            handleProviderSetUp(); -                        break; -                    case PROVIDER_NOK: -                        if(providersMatch) +                if (handledProvider != null && handledProvider.getDomain().equalsIgnoreCase(provider.getDomain())) { +                    switch (resultCode) { +                        case PROVIDER_OK: +                            handleProviderSetUp(handledProvider); +                            break; +                        case PROVIDER_NOK:                              handleProviderSetupFailed(resultData); -                        break; -                    case CORRECTLY_DOWNLOADED_CERTIFICATE: -                        handleCorrectlyDownloadedCertificate(); -                        break; -                    case INCORRECTLY_DOWNLOADED_CERTIFICATE: -                        handleIncorrectlyDownloadedCertificate(); -                        break; +                            break; +                        case CORRECTLY_DOWNLOADED_CERTIFICATE: +                            handleCorrectlyDownloadedCertificate(handledProvider); +                            break; +                        case INCORRECTLY_DOWNLOADED_CERTIFICATE: +                            handleIncorrectlyDownloadedCertificate(); +                            break; +                    }                  }              }          } diff --git a/app/src/main/java/se/leap/bitmaskclient/StartActivity.java b/app/src/main/java/se/leap/bitmaskclient/StartActivity.java index e4758ac9..ee1e2a69 100644 --- a/app/src/main/java/se/leap/bitmaskclient/StartActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/StartActivity.java @@ -14,12 +14,15 @@ import java.lang.annotation.RetentionPolicy;  import de.blinkt.openvpn.core.VpnStatus;  import se.leap.bitmaskclient.eip.EIP; +import se.leap.bitmaskclient.eip.EipCommand;  import se.leap.bitmaskclient.userstatus.User;  import static se.leap.bitmaskclient.Constants.APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE;  import static se.leap.bitmaskclient.Constants.EIP_ACTION_START;  import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT;  import static se.leap.bitmaskclient.Constants.PREFERENCES_APP_VERSION; +import static se.leap.bitmaskclient.Constants.PROVIDER_EIP_DEFINITION; +import static se.leap.bitmaskclient.Constants.PROVIDER_KEY;  import static se.leap.bitmaskclient.Constants.REQUEST_CODE_CONFIGURE_LEAP;  import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;  import static se.leap.bitmaskclient.MainActivity.ACTION_SHOW_VPN_FRAGMENT; @@ -73,7 +76,6 @@ public class StartActivity extends Activity {          }          // initialize app necessities -        ProviderAPICommand.initialize(getApplicationContext());          VpnStatus.initLogCache(getApplicationContext().getCacheDir());          User.init(getString(R.string.default_username)); @@ -128,6 +130,13 @@ public class StartActivity extends Activity {          if (hasNewFeature(FeatureVersionCode.MULTIPLE_PROFILES)) {              // TODO prepare usage of multiple profiles          } +        if (hasNewFeature(FeatureVersionCode.RENAMED_EIP_IN_PREFERENCES)) { +            String eipJson = preferences.getString(PROVIDER_KEY, null); +            if (eipJson != null) { +                preferences.edit().putString(PROVIDER_EIP_DEFINITION, eipJson). +                        remove(PROVIDER_KEY).apply(); +            } +        }          // ensure all upgrades have passed before storing new information          storeAppVersion(); @@ -155,7 +164,7 @@ public class StartActivity extends Activity {              } else {                  Log.d(TAG, "vpn provider is configured");                  if (getIntent() != null && getIntent().getBooleanExtra(EIP_RESTART_ON_BOOT, false)) { -                    eipCommand(EIP_ACTION_START); +                    EipCommand.startVPN(getApplicationContext(), true);                      finish();                      return;                  } @@ -183,7 +192,7 @@ public class StartActivity extends Activity {              if (resultCode == RESULT_OK && data.hasExtra(Provider.KEY)) {                  Provider provider = data.getParcelableExtra(Provider.KEY);                  ConfigHelper.storeProviderInPreferences(preferences, provider); -                eipCommand(EIP_ACTION_START); +                EipCommand.startVPN(this.getApplicationContext(), false);                  showMainActivity();              } else if (resultCode == RESULT_CANCELED) {                  finish(); @@ -199,16 +208,4 @@ public class StartActivity extends Activity {          finish();      } - -    /** -     * 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) { -        Intent vpn_intent = new Intent(this.getApplicationContext(), EIP.class); -        vpn_intent.setAction(action); -        this.startService(vpn_intent); -    }  } 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 73c68e4c..9d5d4341 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(),                  R.layout.single_list_item,                  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 894ad672..9c7f6d1a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -22,6 +22,7 @@ import android.content.Intent;  import android.content.SharedPreferences;  import android.os.Bundle;  import android.os.ResultReceiver; +import android.support.v4.content.LocalBroadcastManager;  import android.util.Log;  import org.json.JSONException; @@ -32,17 +33,20 @@ import java.lang.ref.WeakReference;  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;  import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_ALWAYS_ON_VPN;  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_EARLY_ROUTES;  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.EIP_TRIGGERED_FROM_UI; -import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; +import static se.leap.bitmaskclient.Constants.PROVIDER_EIP_DEFINITION;  import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE;  import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; @@ -94,7 +98,8 @@ public final class EIP extends IntentService {          switch (action) {              case EIP_ACTION_START: -                startEIP(!intent.hasExtra(EIP_TRIGGERED_FROM_UI)); +                boolean earlyRoutes = intent.getBooleanExtra(EIP_EARLY_ROUTES, true); +                startEIP(earlyRoutes);                  break;              case EIP_ACTION_START_ALWAYS_ON_VPN:                  startEIPAlwaysOnVpn(); @@ -132,9 +137,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);      }      /** @@ -182,7 +187,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);      }      /** @@ -195,7 +200,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);      }      /** @@ -206,13 +211,13 @@ 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() {          JSONObject result = new JSONObject();          try { -            String eipDefinitionString = preferences.getString(PROVIDER_KEY, ""); +            String eipDefinitionString = preferences.getString(PROVIDER_EIP_DEFINITION, "");              if (!eipDefinitionString.isEmpty()) {                  result = new JSONObject(eipDefinitionString);              } @@ -246,14 +251,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 (mReceiverRef.get() != null) {              mReceiverRef.get().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"); +        LocalBroadcastManager.getInstance(this).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..1c2ae5da --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java @@ -0,0 +1,82 @@ +package se.leap.bitmaskclient.eip; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.ResultReceiver; +import android.support.annotation.NonNull; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +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_UPDATE; +import static se.leap.bitmaskclient.Constants.EIP_EARLY_ROUTES; +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, 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, @Nullable Intent vpnIntent) { +        // TODO validate "action"...how do we get the list of intent-filters for a class via Android API? +        if (vpnIntent == null) { +            vpnIntent = new Intent(); +        } +        vpnIntent.setComponent(new ComponentName(context.getApplicationContext(), EIP.class)); +        vpnIntent.setAction(action); +        if (resultReceiver != null) +            vpnIntent.putExtra(EIP_RECEIVER, resultReceiver); +        context.startService(vpnIntent); +    } + +    public static void updateEipService(@NonNull Context context, ResultReceiver resultReceiver) { +        execute(context, EIP_ACTION_UPDATE, resultReceiver, null); +    } + +    public static void updateEipService(@NonNull Context context) { +        execute(context, EIP_ACTION_UPDATE, null, null); +    } + +    public static void startVPN(@NonNull Context context, boolean earlyRoutes) { +        Intent baseIntent = new Intent(); +        baseIntent.putExtra(EIP_EARLY_ROUTES, earlyRoutes); +        execute(context, EIP_ACTION_START, null, baseIntent); +    } + +    public static void startVPN(@NonNull Context context, ResultReceiver resultReceiver) { +        execute(context, EIP_ACTION_START, resultReceiver, null); +    } + +    public static void stopVPN(@NonNull Context context) { +        execute(context, EIP_ACTION_STOP); +    } + +    public static void stopVPN(@NonNull Context context, ResultReceiver resultReceiver) { +        execute(context, EIP_ACTION_STOP, resultReceiver, null); +    } + +    public static void checkVpnCertificate(@NonNull Context context) { +        execute(context, EIP_ACTION_CHECK_CERT_VALIDITY); +    } + +    public static void checkVpnCertificate(@NonNull Context context, ResultReceiver resultReceiver) { +        execute(context, EIP_ACTION_CHECK_CERT_VALIDITY, resultReceiver, null); +    } + +} diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java index 0da74872..df252500 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java @@ -24,6 +24,7 @@ import java.util.Observable;  import de.blinkt.openvpn.core.ConnectionStatus;  import de.blinkt.openvpn.core.LogItem; +import de.blinkt.openvpn.core.ProfileManager;  import de.blinkt.openvpn.core.VpnStatus;  /** @@ -92,7 +93,14 @@ public class EipStatus extends Observable implements VpnStatus.StateListener {                  currentEipLevel = EipLevel.CONNECTED;                  break;              case LEVEL_VPNPAUSED: -                throw new IllegalStateException("Ics-Openvpn's VPNPAUSED state is not supported by Bitmask"); +                if (ProfileManager.getLastConnectedVpn() != null && ProfileManager.getLastConnectedVpn().mPersistTun) { +                    //if persistTun is enabled, treat EipLevel as connecting as it *shouldn't* allow passing traffic in the clear... +                    currentEipLevel = EipLevel.CONNECTING; +                } else { +                    //... if persistTun is not enabled, background network traffic will pass in the clear +                    currentEipLevel = EipLevel.DISCONNECTED; +                } +                break;              case LEVEL_CONNECTING_SERVER_REPLIED:              case LEVEL_CONNECTING_NO_SERVER_REPLY_YET:              case LEVEL_WAITING_FOR_USER_INPUT: diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java index ff7d011e..6cccdcd2 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java @@ -16,14 +16,16 @@   */  package se.leap.bitmaskclient.eip; -import com.google.gson.*; +import com.google.gson.Gson; -import org.json.*; +import org.json.JSONException; +import org.json.JSONObject; -import java.io.*; +import java.io.IOException; +import java.io.StringReader; -import de.blinkt.openvpn.*; -import de.blinkt.openvpn.core.*; +import de.blinkt.openvpn.VpnProfile; +import de.blinkt.openvpn.core.ConfigParser;  /**   * Gateway provides objects defining gateways and their metadata. @@ -37,7 +39,7 @@ public class Gateway {      public final static String TAG = Gateway.class.getSimpleName(); -    private JSONObject general_configuration; +    private JSONObject generalConfiguration;      private JSONObject secrets;      private JSONObject gateway; @@ -54,7 +56,7 @@ public class Gateway {          this.gateway = gateway;          this.secrets = secrets; -        general_configuration = getGeneralConfiguration(eip_definition); +        generalConfiguration = getGeneralConfiguration(eip_definition);          timezone = getTimezone(eip_definition);          mName = locationAsName(eip_definition); @@ -80,9 +82,9 @@ public class Gateway {          return location.optString("name");      } -    private JSONObject getLocationInfo(JSONObject eip_definition) { +    private JSONObject getLocationInfo(JSONObject eipDefinition) {          try { -            JSONObject locations = eip_definition.getJSONObject("locations"); +            JSONObject locations = eipDefinition.getJSONObject("locations");              return locations.getJSONObject(gateway.getString("location"));          } catch (JSONException e) { @@ -97,8 +99,8 @@ public class Gateway {          try {              ConfigParser cp = new ConfigParser(); -            VpnConfigGenerator vpn_configuration_generator = new VpnConfigGenerator(general_configuration, secrets, gateway); -            String configuration = vpn_configuration_generator.generate(); +            VpnConfigGenerator vpnConfigurationGenerator = new VpnConfigGenerator(generalConfiguration, secrets, gateway); +            String configuration = vpnConfigurationGenerator.generate();              cp.parseConfig(new StringReader(configuration));              return cp.convertProfile(); diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java index 0b330ed9..1bdb53ab 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java @@ -94,14 +94,14 @@ public class GatewaysManager {          return new Gson().toJson(gateways, list_type);      } -    public void fromEipServiceJson(JSONObject eip_definition) { +    public void fromEipServiceJson(JSONObject eipDefinition) {          try { -            JSONArray gatewaysDefined = eip_definition.getJSONArray("gateways"); +            JSONArray gatewaysDefined = eipDefinition.getJSONArray("gateways");              for (int i = 0; i < gatewaysDefined.length(); i++) {                  JSONObject gw = gatewaysDefined.getJSONObject(i);                  if (isOpenVpnGateway(gw)) {                      JSONObject secrets = secretsConfiguration(); -                    Gateway aux = new Gateway(eip_definition, secrets, gw); +                    Gateway aux = new Gateway(eipDefinition, secrets, gw);                      if (!containsProfileWithSecrets(aux.getProfile())) {                          addGateway(aux);                      } 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 4b8ce55d..2d8b5c6f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatusFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatusFragment.java @@ -18,18 +18,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 final 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) @@ -145,33 +146,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..5317118b 100644 --- a/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java +++ b/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java @@ -33,15 +33,14 @@ 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.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_CORRUPTED_PROVIDER_JSON;  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; +import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_details;  /**   * Implements the logic of the provider api http requests. The methods of this class need to be called from @@ -66,76 +65,39 @@ 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) { -            //FIXME: this should be refactored in order to avoid static variables all over here -            lastProviderMainUrl = task.containsKey(Provider.MAIN_URL) ? -                    task.getString(Provider.MAIN_URL) : -                    ""; - -            if (isEmpty(lastProviderMainUrl)) { -                currentDownload.putBoolean(RESULT_KEY, false); -                setErrorResult(currentDownload, malformed_url, null); -                return currentDownload; -            } - -            //TODO: remove that -            providerCaCertFingerprint = task.containsKey(Provider.CA_CERT_FINGERPRINT) ? -                    task.getString(Provider.CA_CERT_FINGERPRINT) : -                    ""; -            providerCaCert = task.containsKey(Provider.CA_CERT) ? -                    task.getString(Provider.CA_CERT) : -                    ""; - -            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(); +        if (isEmpty(provider.getMainUrlString()) || provider.getMainUrl().isDefault()) { +            currentDownload.putBoolean(BROADCAST_RESULT_KEY, false); +            setErrorResult(currentDownload, malformed_url, null); +            return currentDownload; +        } -            //provider details invalid -            if (currentDownload.containsKey(ERRORS)) { -                return currentDownload; -            } +        getPersistedProviderUpdates(provider); +        currentDownload = validateProviderDetails(provider); -            //no provider certificate available -            if (currentDownload.containsKey(RESULT_KEY) && !currentDownload.getBoolean(RESULT_KEY)) { -                resetProviderDetails(); -            } +        //provider details invalid +        if (currentDownload.containsKey(ERRORS)) { +            return currentDownload; +        } -            EIP_SERVICE_JSON_DOWNLOADED = false; -            go_ahead = true; +        //no provider certificate available +        if (currentDownload.containsKey(BROADCAST_RESULT_KEY) && !currentDownload.getBoolean(BROADCAST_RESULT_KEY)) { +            resetProviderDetails(provider);          } -        if (!PROVIDER_JSON_DOWNLOADED) -            currentDownload = getAndSetProviderJson(lastProviderMainUrl, providerCaCert, providerDefinition); -        if (PROVIDER_JSON_DOWNLOADED || (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY))) { -            broadcastProgress(++progress); -            PROVIDER_JSON_DOWNLOADED = true; - -            if (!CA_CERT_DOWNLOADED) -                currentDownload = downloadCACert(); -            if (CA_CERT_DOWNLOADED || (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY))) { -                broadcastProgress(++progress); -                CA_CERT_DOWNLOADED = true; -                currentDownload = getAndSetEipServiceJson(); -                if (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY)) { -                    broadcastProgress(++progress); -                    EIP_SERVICE_JSON_DOWNLOADED = true; -                } +        if (!provider.hasDefinition()) { +            currentDownload = getAndSetProviderJson(provider); +        } +        if (provider.hasDefinition() || (currentDownload.containsKey(BROADCAST_RESULT_KEY) && currentDownload.getBoolean(BROADCAST_RESULT_KEY))) { +            if (!provider.hasCaCert()) +                currentDownload = downloadCACert(provider); +            if (provider.hasCaCert() || (currentDownload.containsKey(BROADCAST_RESULT_KEY) && currentDownload.getBoolean(BROADCAST_RESULT_KEY))) { +                currentDownload = getAndSetEipServiceJson(provider);              }          } @@ -143,67 +105,63 @@ public class ProviderApiManager extends ProviderApiManagerBase {      } -    private Bundle getAndSetProviderJson(String providerMainUrl, String caCert, JSONObject providerDefinition) { +    private Bundle getAndSetProviderJson(Provider provider) {          Bundle result = new Bundle(); -        if (go_ahead) { -            String providerDotJsonString; -            if(providerDefinition.length() == 0 || caCert.isEmpty()) -                providerDotJsonString = downloadWithCommercialCA(providerMainUrl + "/provider.json"); -            else { -                providerDotJsonString = downloadFromApiUrlWithProviderCA("/provider.json", caCert, providerDefinition); -            } +        String caCert = provider.getCaCert(); +        JSONObject providerDefinition = provider.getDefinition(); -            if (!isValidJson(providerDotJsonString)) { -                setErrorResult(result, malformed_url, null); -                return result; -            } +        String providerDotJsonString; +        if(providerDefinition.length() == 0 || caCert.isEmpty()) { +            String providerJsonUrl = provider.getMainUrlString() + "/provider.json"; +            providerDotJsonString = downloadWithCommercialCA(providerJsonUrl, provider); +        } else { +            providerDotJsonString = downloadFromApiUrlWithProviderCA("/provider.json", caCert, providerDefinition); +        } -            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); -            } catch (JSONException e) { -                String reason_to_fail = pickErrorMessage(providerDotJsonString); -                result.putString(ERRORS, reason_to_fail); -                result.putBoolean(RESULT_KEY, false); +        if (!isValidJson(providerDotJsonString)) { +            setErrorResult(result, malformed_url, null); +            return result; +        } + +        try { +            JSONObject providerJson = new JSONObject(providerDotJsonString); +            if (provider.define(providerJson)) { +                result.putBoolean(BROADCAST_RESULT_KEY, true); +            } else { +                return setErrorResult(result, warning_corrupted_provider_details, ERROR_CORRUPTED_PROVIDER_JSON.toString());              } + +        } catch (JSONException e) { +            String reason_to_fail = pickErrorMessage(providerDotJsonString); +            result.putString(ERRORS, reason_to_fail); +            result.putBoolean(BROADCAST_RESULT_KEY, false);          }          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 = ""; -        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); - -                preferences.edit().putString(PROVIDER_KEY, eip_service_json.toString()).commit(); - -                result.putBoolean(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 eipServiceJsonString = ""; +        try { +            JSONObject providerJson = provider.getDefinition(); +            String eipServiceUrl = providerJson.getString(Provider.API_URL) + "/" + providerJson.getString(Provider.API_VERSION) + "/" + EIP.SERVICE_API_PATH; +            eipServiceJsonString = downloadWithProviderCA(provider.getCaCert(), eipServiceUrl); +            JSONObject eipServiceJson = new JSONObject(eipServiceJsonString); +            eipServiceJson.getInt(Provider.API_RETURN_SERIAL); + +            provider.setEipServiceJson(eipServiceJson); + +            result.putBoolean(BROADCAST_RESULT_KEY, true); +        } catch (NullPointerException | JSONException e) { +            String reasonToFail = pickErrorMessage(eipServiceJsonString); +            result.putString(ERRORS, reasonToFail); +            result.putBoolean(BROADCAST_RESULT_KEY, false);          }          return result;      } @@ -214,19 +172,18 @@ 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 providerMainUrl = providerJson.getString(Provider.API_URL); +            URL newCertStringUrl = new URL(providerMainUrl + "/" + providerJson.getString(Provider.API_VERSION) + "/" + PROVIDER_VPN_CERTIFICATE); -            String cert_string = downloadWithProviderCA(new_cert_string_url.toString()); +            String certString = downloadWithProviderCA(provider.getCaCert(), newCertStringUrl.toString()); -            if (ConfigHelper.checkErroneousDownload(cert_string)) +            if (ConfigHelper.checkErroneousDownload(certString))                  return false;              else -                return loadCertificate(cert_string); +                return loadCertificate(provider, certString);          } catch (IOException e) {              // TODO Auto-generated catch block              e.printStackTrace(); @@ -238,23 +195,22 @@ public class ProviderApiManager extends ProviderApiManagerBase {          }      } -    private Bundle downloadCACert() { +    private Bundle downloadCACert(Provider provider) {          Bundle result = new Bundle();          try { -            JSONObject providerJson = new JSONObject(preferences.getString(Provider.KEY, "")); -            String caCertUrl = providerJson.getString(Provider.CA_CERT_URI); -            String providerDomain = getDomainFromMainURL(lastProviderMainUrl); -            String cert_string = downloadWithCommercialCA(caCertUrl); - -            if (validCertificate(cert_string) && go_ahead) { -                preferences.edit().putString(Provider.CA_CERT, cert_string).commit(); -                preferences.edit().putString(Provider.CA_CERT + "." + providerDomain, cert_string).commit(); -                result.putBoolean(RESULT_KEY, true); +            String caCertUrl = provider.getDefinition().getString(Provider.CA_CERT_URI); +            String providerDomain = getDomainFromMainURL(provider.getMainUrlString()); +            String certString = downloadWithCommercialCA(caCertUrl, provider); + +            if (validCertificate(provider, certString)) { +                provider.setCaCert(certString); +                preferences.edit().putString(Provider.CA_CERT + "." + providerDomain, certString).apply(); +                result.putBoolean(BROADCAST_RESULT_KEY, true);              } else {                  setErrorResult(result, warning_corrupted_provider_cert, ERROR_CERTIFICATE_PINNING.toString());              }          } catch (JSONException e) { -            setErrorResult(result, malformed_url, null); +            e.printStackTrace();          }          return result; @@ -263,10 +219,8 @@ public class ProviderApiManager extends ProviderApiManagerBase {      /**       * Tries to download the contents of the provided url using commercially validated CA certificate from chosen provider.       * -     * @param string_url -     * @return       */ -    private String downloadWithCommercialCA(String string_url) { +    private String downloadWithCommercialCA(String stringUrl, Provider provider) {          String responseString;          JSONObject errorJson = new JSONObject(); @@ -277,14 +231,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(); @@ -304,7 +258,7 @@ public class ProviderApiManager extends ProviderApiManagerBase {          String responseString;          JSONObject errorJson = new JSONObject();          String baseUrl = getApiUrl(providerDefinition); -        OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(errorJson, caCert); +        OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(caCert, errorJson);          if (okHttpClient == null) {              return errorJson.toString();          } @@ -324,11 +278,11 @@ public class ProviderApiManager extends ProviderApiManagerBase {       * @param urlString as a string       * @return an empty string if it fails, the url content if not.       */ -    private String downloadWithProviderCA(String urlString) { +    private String downloadWithProviderCA(String caCert, String urlString) {          JSONObject initError = new JSONObject();          String responseString; -        OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(initError); +        OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(caCert, initError);          if (okHttpClient == null) {              return initError.toString();          } diff --git a/app/src/production/java/se/leap/bitmaskclient/ProviderListActivity.java b/app/src/production/java/se/leap/bitmaskclient/ProviderListActivity.java index 8403b046..725ede3e 100644 --- a/app/src/production/java/se/leap/bitmaskclient/ProviderListActivity.java +++ b/app/src/production/java/se/leap/bitmaskclient/ProviderListActivity.java @@ -16,12 +16,13 @@   */  package se.leap.bitmaskclient; -import android.content.Intent; -import android.os.Bundle; +import android.support.annotation.NonNull;  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,42 +62,12 @@ 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() { -        cancelSettingUpProvider(); -        if (!ProviderAPI.caCertDownloaded()) { -            addAndSelectNewProvider(ProviderAPI.lastProviderMainUrl()); -        } else { -            showProgressBar(); - -            Intent providerApiCommand = new Intent(this, ProviderAPI.class); -            providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); -            providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, providerAPIResultReceiver); -            Bundle parameters = new Bundle(); -            parameters.putString(Provider.MAIN_URL, provider.getMainUrl().toString()); -            providerApiCommand.putExtra(ProviderAPI.PARAMETERS, parameters); - -            startService(providerApiCommand); -        } +    public void retrySetUpProvider(@NonNull Provider provider) { +        ProviderAPICommand.execute(this, SET_UP_PROVIDER, provider);      }  } diff --git a/app/src/sharedTest/java/se.leap.bitmaskclient/testutils/TestSetupHelper.java b/app/src/sharedTest/java/se.leap.bitmaskclient/testutils/TestSetupHelper.java index 725924e6..a9584238 100644 --- a/app/src/sharedTest/java/se.leap.bitmaskclient/testutils/TestSetupHelper.java +++ b/app/src/sharedTest/java/se.leap.bitmaskclient/testutils/TestSetupHelper.java @@ -17,10 +17,17 @@  package se.leap.bitmaskclient.testutils; +import junit.framework.Test; + +import org.json.JSONException; +  import java.io.BufferedReader;  import java.io.IOException;  import java.io.InputStream;  import java.io.InputStreamReader; +import java.net.URL; + +import se.leap.bitmaskclient.Provider;  /**   * Created by cyberta on 08.10.17. @@ -40,4 +47,30 @@ public class TestSetupHelper {          return sb.toString();      } + +    public static Provider getConfiguredProvider() throws IOException, JSONException { +        return getProvider(null, null, null); +    } + +    public static Provider getProvider(String domain, String caCertFile, String jsonFile) { +        if (domain == null) +            domain = "https://riseup.net"; +        if (caCertFile == null) +            caCertFile = "riseup.net.pem"; +        if (jsonFile == null) +            jsonFile = "riseup.net.json"; + +        try { +            return new Provider( +                    new URL(domain), +                    getInputAsString(TestSetupHelper.class.getClassLoader().getResourceAsStream(caCertFile)), +                    getInputAsString(TestSetupHelper.class.getClassLoader().getResourceAsStream(jsonFile)) + +            ); +        } catch (IOException e) { +            e.printStackTrace(); +        } +        return null; +    } +  } diff --git a/app/src/test/java/se/leap/bitmaskclient/DefaultedURLTest.java b/app/src/test/java/se/leap/bitmaskclient/DefaultedURLTest.java new file mode 100644 index 00000000..cbf47621 --- /dev/null +++ b/app/src/test/java/se/leap/bitmaskclient/DefaultedURLTest.java @@ -0,0 +1,31 @@ +package se.leap.bitmaskclient; + +import org.junit.Test; + +import java.net.MalformedURLException; +import java.net.URL; + +import static org.junit.Assert.*; + +/** + * Created by cyberta on 11.02.18. + */ +public class DefaultedURLTest { + +    @Test +    public void testEquals_false() throws MalformedURLException { +        DefaultedURL defaultedURL = new DefaultedURL(); +        DefaultedURL customURL = new DefaultedURL(); +        customURL.setUrl(new URL("https://customurl.com")); + +        assertFalse(defaultedURL.equals(customURL)); +    } + +    @Test +    public void testEquals_true() throws MalformedURLException { +        DefaultedURL defaultedURL = new DefaultedURL(); +        DefaultedURL customURL = new DefaultedURL(); +        assertTrue(defaultedURL.equals(customURL)); +    } + +} diff --git a/app/src/test/java/se/leap/bitmaskclient/ProviderTest.java b/app/src/test/java/se/leap/bitmaskclient/ProviderTest.java new file mode 100644 index 00000000..794c3087 --- /dev/null +++ b/app/src/test/java/se/leap/bitmaskclient/ProviderTest.java @@ -0,0 +1,21 @@ +package se.leap.bitmaskclient; + +import org.junit.Test; + +import se.leap.bitmaskclient.testutils.TestSetupHelper; + +import static org.junit.Assert.assertTrue; + +/** + * Created by cyberta on 12.02.18. + */ +public class ProviderTest { + +    @Test +    public void testEquals_sameFields_returnsTrue() throws Exception { +        Provider p1 = TestSetupHelper.getConfiguredProvider(); +        Provider p2 = TestSetupHelper.getConfiguredProvider(); +        assertTrue("Providers should be same:", p1.equals(p2)); +    } + +} diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/EipStatusTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/EipStatusTest.java index 15085b46..f332b094 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/EipStatusTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/EipStatusTest.java @@ -3,9 +3,12 @@ package se.leap.bitmaskclient.eip;  import org.junit.Before;  import org.junit.Test;  import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import de.blinkt.openvpn.VpnProfile;  import de.blinkt.openvpn.core.ConnectionStatus; +import de.blinkt.openvpn.core.ProfileManager;  import de.blinkt.openvpn.core.VpnStatus;  import se.leap.bitmaskclient.R; @@ -18,6 +21,8 @@ import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_VPNPAUSED;  import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT;  import static de.blinkt.openvpn.core.ConnectionStatus.UNKNOWN_LEVEL;  import static junit.framework.Assert.assertTrue; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.when;  import static se.leap.bitmaskclient.eip.EipStatus.EipLevel.CONNECTING;  import static se.leap.bitmaskclient.eip.EipStatus.EipLevel.DISCONNECTED;  import static se.leap.bitmaskclient.eip.EipStatus.EipLevel.UNKNOWN; @@ -26,7 +31,8 @@ import static se.leap.bitmaskclient.eip.EipStatus.EipLevel.UNKNOWN;   * Created by cyberta on 06.12.17.   * TODO: Mock AsyncTask   */ -@RunWith(MockitoJUnitRunner.class) +@RunWith(PowerMockRunner.class) +@PrepareForTest({ProfileManager.class})  public class EipStatusTest {      EipStatus eipStatus; @@ -46,9 +52,28 @@ public class EipStatusTest {          assertTrue("LEVEL_CONNECTED state", eipStatus.getState().equals("CONNECTED"));      } -    @Test(expected= IllegalStateException.class) -    public void testUpdateState_LEVEL_VPNPAUSED() throws Exception { -        VpnStatus.updateStateString("USERPAUSE", "", R.string.state_userpause, LEVEL_VPNPAUSED); +    @Test +    public void testUpdateState_LEVEL_VPNPAUSED_hasPersistentTun() throws Exception { + +        mockStatic(ProfileManager.class); +        VpnProfile mockVpnProfile = new VpnProfile("mockProfile"); +        mockVpnProfile.mPersistTun = true; +        when(ProfileManager.getLastConnectedVpn()).thenReturn(mockVpnProfile); +        VpnStatus.updateStateString("SCREENOFF", "", R.string.state_screenoff, LEVEL_VPNPAUSED); +        assertTrue("LEVEL_VPN_PAUSED eipLevel", eipStatus.getEipLevel() == CONNECTING); +        assertTrue("LEVEL_VPN_PAUSED level", eipStatus.getLevel() == LEVEL_VPNPAUSED); +    } + +    @Test +    public void testUpdateState_LEVEL_VPNPAUSED_hasNotPersistentTun() throws Exception { + +        mockStatic(ProfileManager.class); +        VpnProfile mockVpnProfile = new VpnProfile("mockProfile"); +        mockVpnProfile.mPersistTun = false; +        when(ProfileManager.getLastConnectedVpn()).thenReturn(mockVpnProfile); +        VpnStatus.updateStateString("SCREENOFF", "", R.string.state_screenoff, LEVEL_VPNPAUSED); +        assertTrue("LEVEL_VPN_PAUSED eipLevel", eipStatus.getEipLevel() == DISCONNECTED); +        assertTrue("LEVEL_VPN_PAUSED level", eipStatus.getLevel() == LEVEL_VPNPAUSED);      }      @Test 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..4842d170 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java @@ -24,6 +24,7 @@ import android.content.res.Resources;  import android.os.Bundle;  import android.text.TextUtils; +import org.json.JSONException;  import org.junit.Before;  import org.junit.Test;  import org.junit.runner.RunWith; @@ -45,21 +46,25 @@ import se.leap.bitmaskclient.ProviderApiManager;  import se.leap.bitmaskclient.ProviderApiManagerBase;  import se.leap.bitmaskclient.testutils.MockSharedPreferences; +import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; +import static se.leap.bitmaskclient.Constants.PROVIDER_KEY;  import static se.leap.bitmaskclient.ProviderAPI.ERRORS;  import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_NOK;  import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_OK; -import static se.leap.bitmaskclient.ProviderAPI.RESULT_KEY;  import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.ERROR_CASE_UPDATED_CERTIFICATE;  import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.NO_ERROR; -import static se.leap.bitmaskclient.testutils.TestSetupHelper.getInputAsString;  import static se.leap.bitmaskclient.testutils.MockHelper.mockBundle;  import static se.leap.bitmaskclient.testutils.MockHelper.mockClientGenerator; +import static se.leap.bitmaskclient.testutils.MockHelper.mockConfigHelper;  import static se.leap.bitmaskclient.testutils.MockHelper.mockFingerprintForCertificate;  import static se.leap.bitmaskclient.testutils.MockHelper.mockIntent;  import static se.leap.bitmaskclient.testutils.MockHelper.mockProviderApiConnector;  import static se.leap.bitmaskclient.testutils.MockHelper.mockResources;  import static se.leap.bitmaskclient.testutils.MockHelper.mockResultReceiver;  import static se.leap.bitmaskclient.testutils.MockHelper.mockTextUtils; +import static se.leap.bitmaskclient.testutils.TestSetupHelper.getConfiguredProvider; +import static se.leap.bitmaskclient.testutils.TestSetupHelper.getInputAsString; +import static se.leap.bitmaskclient.testutils.TestSetupHelper.getProvider;  /** @@ -103,235 +108,248 @@ public class ProviderApiManagerTest {          mockResources = mockResources(getClass().getClassLoader().getResourceAsStream("error_messages.json"));      } -      @Test -    public void test_handleIntentSetupProvider_noProviderMainURL() { +    public void test_handleIntentSetupProvider_noProviderMainURL() throws IOException, JSONException { +        Provider provider = new Provider(""); +          providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());          Bundle expectedResult = mockBundle(); -        expectedResult.putBoolean(RESULT_KEY, false); -        expectedResult.putString(ERRORS, "{\"errors\":\"It doesn't seem to be a Bitmask provider.\"}"); -        Intent provider_API_command = mockIntent(); -        Bundle parameters = mockBundle(); -        parameters.putString(Provider.MAIN_URL, ""); +        expectedResult.putBoolean(BROADCAST_RESULT_KEY, false); +        expectedResult.putString(ERRORS, "{\"errors\":\"It doesn't seem to be a Bitmask provider.\"}"); +        expectedResult.putParcelable(PROVIDER_KEY, provider); -        provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); -        provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); -        provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); +        Intent providerApiCommand = mockIntent(); +        providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); +        providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); +        providerApiCommand.putExtra(PROVIDER_KEY, provider); -        providerApiManager.handleIntent(provider_API_command); +        providerApiManager.handleIntent(providerApiCommand);      }      @Test -    public void test_handleIntentSetupProvider_happyPath_preseededProviderAndCA() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { +    public void test_handleIntentSetupProvider_happyPath_preseededProviderAndCA() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { +        Provider provider = getConfiguredProvider(); +          mockFingerprintForCertificate(" a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494");          mockProviderApiConnector(NO_ERROR);          providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());          Bundle expectedResult = mockBundle(); -        expectedResult.putBoolean(RESULT_KEY, true); -        Intent provider_API_command = mockIntent(); -        Bundle parameters = mockBundle(); -        parameters.putString(Provider.MAIN_URL, "https://riseup.net"); -        parameters.putString(Provider.CA_CERT, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"))); -        parameters.putString(Provider.KEY, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))); +        expectedResult.putBoolean(BROADCAST_RESULT_KEY, true); +        expectedResult.putParcelable(PROVIDER_KEY, provider); + +        Intent providerApiCommand = mockIntent(); -        provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); -        provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); -        provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_OK, expectedResult)); +        providerApiCommand.putExtra(PROVIDER_KEY, provider); +        providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); +        providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_OK, expectedResult)); -        providerApiManager.handleIntent(provider_API_command); +        providerApiManager.handleIntent(providerApiCommand);      }      @Test -    public void test_handleIntentSetupProvider_happyPath_no_preseededProviderAndCA() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { +    public void test_handleIntentSetupProvider_happyPath_no_preseededProviderAndCA() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { +        Provider provider = new Provider("https://riseup.net"); +          mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494");          mockProviderApiConnector(NO_ERROR);          providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());          Bundle expectedResult = mockBundle(); -        expectedResult.putBoolean(RESULT_KEY, true); -        Intent provider_API_command = mockIntent(); -        Bundle parameters = mockBundle(); -        parameters.putString(Provider.MAIN_URL, "https://riseup.net"); +        expectedResult.putBoolean(BROADCAST_RESULT_KEY, true); +        expectedResult.putParcelable(PROVIDER_KEY, provider); + +        Intent providerApiCommand = mockIntent(); -        provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); -        provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); -        provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_OK, expectedResult)); +        providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); +        providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_OK, expectedResult)); +        providerApiCommand.putExtra(PROVIDER_KEY, provider); -        providerApiManager.handleIntent(provider_API_command); +        providerApiManager.handleIntent(providerApiCommand);      }      @Test -    public void test_handleIntentSetupProvider_happyPath_storedProviderAndCAFromPreviousSetup() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { -        mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494"); +    public void test_handleIntentSetupProvider_happyPath_storedProviderAndCAFromPreviousSetup() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { +        Provider provider = new Provider("https://riseup.net"); +        mockConfigHelper("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494", getConfiguredProvider()); +          mockProviderApiConnector(NO_ERROR);          mockPreferences.edit().putString(Provider.KEY + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))).apply();          mockPreferences.edit().putString(Provider.CA_CERT + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"))).apply();          providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); +          Bundle expectedResult = mockBundle(); -        expectedResult.putBoolean(RESULT_KEY, true); +        expectedResult.putBoolean(BROADCAST_RESULT_KEY, true); +        expectedResult.putParcelable(PROVIDER_KEY, provider); -        Intent provider_API_command = mockIntent(); -        Bundle parameters = mockBundle(); -        parameters.putString(Provider.MAIN_URL, "https://riseup.net"); +        Intent providerApiCommand = mockIntent(); +        providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); +        providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_OK, expectedResult)); -        provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); -        provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); -        provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_OK, expectedResult)); +        providerApiCommand.putExtra(PROVIDER_KEY, provider); -        providerApiManager.handleIntent(provider_API_command); +        providerApiManager.handleIntent(providerApiCommand);      }      @Test -    public void test_handleIntentSetupProvider_preseededProviderAndCA_failedCAPinning() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { +    public void test_handleIntentSetupProvider_preseededProviderAndCA_failedCAPinning() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { +        Provider provider = getConfiguredProvider();          mockFingerprintForCertificate(" a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29495");          mockProviderApiConnector(NO_ERROR);          providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());          Bundle expectedResult = mockBundle(); -        expectedResult.putBoolean(RESULT_KEY, false); +        expectedResult.putBoolean(BROADCAST_RESULT_KEY, false);          expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_CERTIFICATE_PINNING\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}"); +        expectedResult.putParcelable(PROVIDER_KEY, provider); + +        Intent providerApiCommand = mockIntent(); -        Intent provider_API_command = mockIntent(); -        Bundle parameters = mockBundle(); -        parameters.putString(Provider.MAIN_URL, "https://riseup.net"); -        parameters.putString(Provider.CA_CERT, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"))); -        parameters.putString(Provider.KEY, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))); +        providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); +        providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); -        provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); -        provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); -        provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); +        providerApiCommand.putExtra(PROVIDER_KEY, provider); -        providerApiManager.handleIntent(provider_API_command); +        providerApiManager.handleIntent(providerApiCommand);      }      @Test -    public void test_handleIntentSetupProvider_no_preseededProviderAndCA_failedPinning() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { +    public void test_handleIntentSetupProvider_no_preseededProviderAndCA_failedPinning() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { +        Provider provider = new Provider("https://riseup.net");          mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29495");          mockProviderApiConnector(NO_ERROR);          providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); +          Bundle expectedResult = mockBundle(); -        expectedResult.putBoolean(RESULT_KEY, false); +        expectedResult.putBoolean(BROADCAST_RESULT_KEY, false);          expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_CERTIFICATE_PINNING\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}"); +        expectedResult.putParcelable(PROVIDER_KEY, provider); + +        Intent providerApiCommand = mockIntent(); -        Intent provider_API_command = mockIntent(); -        Bundle parameters = mockBundle(); -        parameters.putString(Provider.MAIN_URL, "https://riseup.net"); +        providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); +        providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); -        provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); -        provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); -        provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); +        providerApiCommand.putExtra(PROVIDER_KEY, provider); -        providerApiManager.handleIntent(provider_API_command); +        providerApiManager.handleIntent(providerApiCommand);      }      @Test -    public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_failedPinning() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { -        mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29495"); +    public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_failedPinning() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { +        Provider provider = new Provider("https://riseup.net"); +        mockConfigHelper("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29495", getConfiguredProvider()); +          mockProviderApiConnector(NO_ERROR);          mockPreferences.edit().putString(Provider.KEY + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))).apply();          mockPreferences.edit().putString(Provider.CA_CERT + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"))).apply();          providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); +          Bundle expectedResult = mockBundle(); -        expectedResult.putBoolean(RESULT_KEY, false); +        expectedResult.putBoolean(BROADCAST_RESULT_KEY, false);          expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_CERTIFICATE_PINNING\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}"); +        expectedResult.putParcelable(PROVIDER_KEY, provider); + +        Intent providerApiCommand = mockIntent(); -        Intent provider_API_command = mockIntent(); -        Bundle parameters = mockBundle(); -        parameters.putString(Provider.MAIN_URL, "https://riseup.net"); +        providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); +        providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); -        provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); -        provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); -        provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); +        providerApiCommand.putExtra(PROVIDER_KEY, provider); -        providerApiManager.handleIntent(provider_API_command); +        providerApiManager.handleIntent(providerApiCommand);      }      @Test -    public void test_handleIntentSetupProvider_preseededProviderAndCA_outdatedCertificate() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { +    public void test_handleIntentSetupProvider_preseededProviderAndCA_outdatedCertificate() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { +        Provider provider = getProvider(null ,"outdated_cert.pem", null);          mockProviderApiConnector(NO_ERROR);          providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); +          Bundle expectedResult = mockBundle(); -        expectedResult.putBoolean(RESULT_KEY, false); +        expectedResult.putBoolean(BROADCAST_RESULT_KEY, false);          expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_INVALID_CERTIFICATE\",\"errors\":\"Stored provider certificate is expired. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}"); +        expectedResult.putParcelable(PROVIDER_KEY, provider); + +        Intent providerApiCommand = mockIntent(); -        Intent provider_API_command = mockIntent(); -        Bundle parameters = mockBundle(); -        parameters.putString(Provider.MAIN_URL, "https://riseup.net"); -        parameters.putString(Provider.CA_CERT, getInputAsString(getClass().getClassLoader().getResourceAsStream("outdated_cert.pem"))); -        parameters.putString(Provider.KEY, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))); +        providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); +        providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); -        provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); -        provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); -        provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); +        providerApiCommand.putExtra(PROVIDER_KEY, provider); -        providerApiManager.handleIntent(provider_API_command); +        providerApiManager.handleIntent(providerApiCommand);      }      @Test -    public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_outdatedCertificate() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { +    public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_outdatedCertificate() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { +        Provider provider = new Provider("https://riseup.net");          mockProviderApiConnector(NO_ERROR);          mockPreferences.edit().putString(Provider.KEY + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))).apply();          mockPreferences.edit().putString(Provider.CA_CERT + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("outdated_cert.pem"))).apply();          providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); +          Bundle expectedResult = mockBundle(); -        expectedResult.putBoolean(RESULT_KEY, false); +        expectedResult.putBoolean(BROADCAST_RESULT_KEY, false);          expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_INVALID_CERTIFICATE\",\"errors\":\"Stored provider certificate is expired. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}"); +        expectedResult.putParcelable(PROVIDER_KEY, provider); + +        Intent providerApiCommand = mockIntent(); -        Intent provider_API_command = mockIntent(); -        Bundle parameters = mockBundle(); -        parameters.putString(Provider.MAIN_URL, "https://riseup.net"); +        providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); +        providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); -        provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); -        provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); -        provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); +        providerApiCommand.putExtra(PROVIDER_KEY, provider); -        providerApiManager.handleIntent(provider_API_command); +        providerApiManager.handleIntent(providerApiCommand);      }      @Test -    public void test_handleIntentSetupProvider_preseededProviderAndCA_ValidCertificateButUpdatedCertificateOnServerSide() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { -        mockFingerprintForCertificate(" a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494"); +    public void test_handleIntentSetupProvider_preseededProviderAndCA_ValidCertificateButUpdatedCertificateOnServerSide() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { +        Provider provider = getConfiguredProvider(); + +        mockConfigHelper("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494", getConfiguredProvider());          mockProviderApiConnector(ERROR_CASE_UPDATED_CERTIFICATE);          providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());          Bundle expectedResult = mockBundle(); -        expectedResult.putBoolean(RESULT_KEY, false); +        expectedResult.putBoolean(BROADCAST_RESULT_KEY, false);          expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_INVALID_CERTIFICATE\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}"); +        expectedResult.putParcelable(PROVIDER_KEY, provider); -        Intent provider_API_command = mockIntent(); -        Bundle parameters = mockBundle(); -        parameters.putString(Provider.MAIN_URL, "https://riseup.net"); -        parameters.putString(Provider.CA_CERT, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"))); -        parameters.putString(Provider.KEY, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))); +        Intent providerApiCommand = mockIntent(); -        provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); -        provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); -        provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); +        providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); +        providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); -        providerApiManager.handleIntent(provider_API_command); +        providerApiCommand.putExtra(PROVIDER_KEY, provider); + +        providerApiManager.handleIntent(providerApiCommand);      }      @Test -    public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_ValidCertificateButUpdatedCertificateOnServerSide() throws IOException, CertificateEncodingException, NoSuchAlgorithmException { -        mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494"); +    public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_ValidCertificateButUpdatedCertificateOnServerSide() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { +        Provider provider = new Provider("https://riseup.net"); + +        mockConfigHelper("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494", getConfiguredProvider());          mockProviderApiConnector(ERROR_CASE_UPDATED_CERTIFICATE);          mockPreferences.edit().putString(Provider.KEY + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))).apply();          mockPreferences.edit().putString(Provider.CA_CERT + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"))).apply();          providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); +          Bundle expectedResult = mockBundle(); -        expectedResult.putBoolean(RESULT_KEY, false); +        expectedResult.putBoolean(BROADCAST_RESULT_KEY, false);          expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_INVALID_CERTIFICATE\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}"); +        expectedResult.putParcelable(PROVIDER_KEY, provider); + +        Intent providerApiCommand = mockIntent(); -        Intent provider_API_command = mockIntent(); -        Bundle parameters = mockBundle(); -        parameters.putString(Provider.MAIN_URL, "https://riseup.net"); +        providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); +        providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); -        provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); -        provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); -        provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult)); +        providerApiCommand.putExtra(PROVIDER_KEY, provider); -        providerApiManager.handleIntent(provider_API_command); +        providerApiManager.handleIntent(providerApiCommand);      }  } diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java index 8372c9bc..c2362c7b 100644 --- a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java +++ b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java @@ -1,6 +1,7 @@  package se.leap.bitmaskclient.testutils;  import android.content.Intent; +import android.content.SharedPreferences;  import android.content.res.Resources;  import android.os.Bundle;  import android.os.Parcelable; @@ -28,7 +29,9 @@ import java.util.Set;  import okhttp3.OkHttpClient;  import se.leap.bitmaskclient.ConfigHelper; +import se.leap.bitmaskclient.Constants;  import se.leap.bitmaskclient.OkHttpClientGenerator; +import se.leap.bitmaskclient.Provider;  import se.leap.bitmaskclient.R;  import se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider;  import se.leap.bitmaskclient.testutils.matchers.BundleMatcher; @@ -44,6 +47,8 @@ import static org.mockito.Mockito.doAnswer;  import static org.mockito.Mockito.mock;  import static org.mockito.Mockito.when;  import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY; +import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE;  /**   * Created by cyberta on 29.01.18. @@ -338,6 +343,30 @@ public class MockHelper {          return resultReceiver;      } +    public static void mockConfigHelper(String mockedFingerprint, final Provider providerFromPrefs) throws CertificateEncodingException, NoSuchAlgorithmException { +        // FIXME use MockSharedPreferences instead of provider +        mockStatic(ConfigHelper.class); +        when(ConfigHelper.getFromPersistedProvider(anyString(), anyString(), any(SharedPreferences.class))).thenAnswer(new Answer<String>() { +            @Override +            public String answer(InvocationOnMock invocation) throws Throwable { +                String key = (String) invocation.getArguments()[0]; +                switch (key) { +                    case PROVIDER_PRIVATE_KEY: +                        return providerFromPrefs.getPrivateKey(); +                    case PROVIDER_VPN_CERTIFICATE: +                        return providerFromPrefs.getVpnCertificate(); +                    case Provider.KEY: +                        return providerFromPrefs.getDefinition().toString(); +                    case Provider.CA_CERT_FINGERPRINT: +                        return providerFromPrefs.getCaCertFingerprint(); +                } +                return null; +            } +        }); +        when(ConfigHelper.getFingerprintFromCertificate(any(X509Certificate.class), anyString())).thenReturn(mockedFingerprint); +        when(ConfigHelper.checkErroneousDownload(anyString())).thenCallRealMethod(); +        when(ConfigHelper.parseX509CertificateFromString(anyString())).thenCallRealMethod(); +    }      public static void mockFingerprintForCertificate(String mockedFingerprint) throws CertificateEncodingException, NoSuchAlgorithmException {          mockStatic(ConfigHelper.class);          when(ConfigHelper.getFingerprintFromCertificate(any(X509Certificate.class), anyString())).thenReturn(mockedFingerprint); @@ -353,8 +382,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(any(JSONObject.class), anyString())).thenReturn(mockedOkHttpClient); +        when(mockClientGenerator.initSelfSignedCAHttpClient(anyString(), any(JSONObject.class))).thenReturn(mockedOkHttpClient);          return mockClientGenerator;      }  | 
