summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcyBerta <cyberta@riseup.net>2018-01-10 17:14:45 +0100
committercyBerta <cyberta@riseup.net>2018-01-10 17:14:45 +0100
commitae8341cf1f563fbcf2bdd6a4eff9525f42e9e995 (patch)
tree43f7da97aa2d8dd1313fe90a69df93f7b96749f0
parent0b647ea5e7ff67747080b2ffcebc948da0fbecb5 (diff)
8773 more test cases and clean-up
-rw-r--r--app/build.gradle6
-rw-r--r--app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java11
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/BaseConfigurationWizard.java3
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java25
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderApiConnector.java18
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java159
-rw-r--r--app/src/main/res/values/strings.xml2
-rw-r--r--app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java12
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java96
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BackendMockProvider.java (renamed from app/src/test/java/se/leap/bitmaskclient/testutils/answers/BackendAnswerFabric.java)22
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BaseBackendResponse.java75
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/NoErrorBackendResponse.java89
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/UpdatedCertificateBackendResponse.java97
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/TestSetupHelper.java15
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/answers/NoErrorAnswer.java58
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/matchers/BundleMatcher.java16
-rw-r--r--app/src/test/resources/error_messages.json2
-rw-r--r--app/src/test/resources/outdated_cert.pem33
-rw-r--r--app/src/test/resources/updated_cert.pem33
19 files changed, 538 insertions, 234 deletions
diff --git a/app/build.gradle b/app/build.gradle
index 8b956944..b894af38 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -70,9 +70,9 @@ dependencies {
testCompile 'org.powermock:powermock-module-junit4:1.7.3'
testCompile 'org.powermock:powermock-core:1.7.3'
testCompile 'org.powermock:powermock-module-junit4-rule:1.7.3'
- testCompile 'com.madgag.spongycastle:bctls-jdk15on:1.58.0.0'
- testCompile 'com.madgag.spongycastle:bcpkix-jdk15on:1.58.0.0'
- testCompile 'com.madgag.spongycastle:bcpg-jdk15on:1.58.0.0'
+ //testCompile 'com.madgag.spongycastle:bctls-jdk15on:1.58.0.0'
+ //testCompile 'com.madgag.spongycastle:bcpkix-jdk15on:1.58.0.0'
+ //testCompile 'com.madgag.spongycastle:bcpg-jdk15on:1.58.0.0'
androidTestCompile 'com.jayway.android.robotium:robotium-solo:5.6.3'
testCompile 'junit:junit:4.12'
diff --git a/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java b/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java
index 9c27fd1f..6105c4b7 100644
--- a/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java
+++ b/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java
@@ -49,11 +49,11 @@ import se.leap.bitmaskclient.eip.EIP;
import static android.text.TextUtils.isEmpty;
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;
/**
* Created by cyberta on 04.01.18.
@@ -89,8 +89,7 @@ public class ProviderApiManager extends ProviderApiManagerBase {
"";
if (isEmpty(lastProviderMainUrl)) {
- currentDownload.putBoolean(RESULT_KEY, false);
- setErrorResult(currentDownload, resources.getString(R.string.malformed_url), null);
+ setErrorResult(currentDownload, malformed_url, null);
return currentDownload;
}
@@ -260,12 +259,10 @@ public class ProviderApiManager extends ProviderApiManagerBase {
preferences.edit().putString(Provider.CA_CERT + "." + providerDomain, certString).commit();
result.putBoolean(RESULT_KEY, true);
} else {
- setErrorResult(result, resources.getString(R.string.warning_corrupted_provider_cert), ERROR_CERTIFICATE_PINNING.toString());
- result.putBoolean(RESULT_KEY, false);
+ setErrorResult(result, warning_corrupted_provider_cert, ERROR_CERTIFICATE_PINNING.toString());
}
} catch (JSONException e) {
- setErrorResult(result, resources.getString(malformed_url), null);
- result.putBoolean(RESULT_KEY, false);
+ setErrorResult(result, malformed_url, null);
}
return result;
diff --git a/app/src/main/java/se/leap/bitmaskclient/BaseConfigurationWizard.java b/app/src/main/java/se/leap/bitmaskclient/BaseConfigurationWizard.java
index c26184bb..63453ac3 100644
--- a/app/src/main/java/se/leap/bitmaskclient/BaseConfigurationWizard.java
+++ b/app/src/main/java/se/leap/bitmaskclient/BaseConfigurationWizard.java
@@ -409,8 +409,6 @@ public abstract class BaseConfigurationWizard extends Activity
}
-
-
/**
* Once selected a provider, this fragment offers the user to log in,
* use it anonymously (if possible)
@@ -430,7 +428,6 @@ public abstract class BaseConfigurationWizard extends Activity
}
}
-
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.configuration_wizard_activity, menu);
diff --git a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java
index 54bcc1f4..0e861059 100644
--- a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java
@@ -47,7 +47,7 @@ import java.security.spec.PKCS8EncodedKeySpec;
import static android.R.attr.name;
/**
- * Stores constants, and implements auxiliary methods used across all LEAP Android classes.
+ * Stores constants, and implements auxiliary methods used across all Bitmask Android classes.
*
* @author parmegv
* @author MeanderingCode
@@ -172,25 +172,30 @@ public class ConfigHelper {
return key;
}
- public static String base64toHex(String base64_input) {
- byte[] byteArray = Base64.decode(base64_input);
- int readBytes = byteArray.length;
+ private static String byteArrayToHex(byte[] input) {
+ int readBytes = input.length;
StringBuffer hexData = new StringBuffer();
int onebyte;
for (int i = 0; i < readBytes; i++) {
- onebyte = ((0x000000ff & byteArray[i]) | 0xffffff00);
+ onebyte = ((0x000000ff & input[i]) | 0xffffff00);
hexData.append(Integer.toHexString(onebyte).substring(6));
}
return hexData.toString();
}
+ /**
+ * Calculates the hexadecimal representation of a sha256/sha1 fingerprint of a certificate
+ *
+ * @param certificate
+ * @param encoding
+ * @return
+ * @throws NoSuchAlgorithmException
+ * @throws CertificateEncodingException
+ */
@NonNull
public static String getFingerprintFromCertificate(X509Certificate certificate, String encoding) throws NoSuchAlgorithmException, CertificateEncodingException /*, UnsupportedEncodingException*/ {
- return base64toHex(
- //new String(Base64.encode(MessageDigest.getInstance(encoding).digest(certificate.getEncoded())), "US-ASCII"));
- android.util.Base64.encodeToString(
- MessageDigest.getInstance(encoding).digest(certificate.getEncoded()),
- android.util.Base64.DEFAULT));
+ byte[] byteArray = MessageDigest.getInstance(encoding).digest(certificate.getEncoded());
+ return byteArrayToHex(byteArray);
}
/**
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderApiConnector.java b/app/src/main/java/se/leap/bitmaskclient/ProviderApiConnector.java
index 9aad14d5..439fb5e2 100644
--- a/app/src/main/java/se/leap/bitmaskclient/ProviderApiConnector.java
+++ b/app/src/main/java/se/leap/bitmaskclient/ProviderApiConnector.java
@@ -60,18 +60,14 @@ public class ProviderApiConnector {
return false;
}
- public static boolean canConnect(@NonNull OkHttpClient okHttpClient, String url) {
- try {
- Request.Builder requestBuilder = new Request.Builder()
- .url(url)
- .method("GET", null);
- Request request = requestBuilder.build();
+ public static boolean canConnect(@NonNull OkHttpClient okHttpClient, String url) throws RuntimeException, IOException {
+ Request.Builder requestBuilder = new Request.Builder()
+ .url(url)
+ .method("GET", null);
+ Request request = requestBuilder.build();
- Response response = okHttpClient.newCall(request).execute();
- return response.isSuccessful();
- } catch (RuntimeException | IOException e) {
- return false;
- }
+ Response response = okHttpClient.newCall(request).execute();
+ return response.isSuccessful();
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java
index 396d642b..cc005fcd 100644
--- a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java
+++ b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java
@@ -30,7 +30,6 @@ import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
-import java.io.InputStream;
import java.math.BigInteger;
import java.net.ConnectException;
import java.net.MalformedURLException;
@@ -46,16 +45,10 @@ import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.util.ArrayList;
import java.util.List;
-import java.util.Locale;
-import java.util.Scanner;
import javax.net.ssl.SSLHandshakeException;
-import okhttp3.MediaType;
import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.RequestBody;
-import okhttp3.Response;
import se.leap.bitmaskclient.userstatus.SessionDialog;
import se.leap.bitmaskclient.userstatus.User;
import se.leap.bitmaskclient.userstatus.UserStatus;
@@ -96,9 +89,11 @@ import static se.leap.bitmaskclient.R.string.error_io_exception_user_message;
import static se.leap.bitmaskclient.R.string.error_json_exception_user_message;
import static se.leap.bitmaskclient.R.string.error_no_such_algorithm_exception_user_message;
import static se.leap.bitmaskclient.R.string.malformed_url;
-import static se.leap.bitmaskclient.R.string.retry;
import static se.leap.bitmaskclient.R.string.server_unreachable_message;
import static se.leap.bitmaskclient.R.string.service_is_down_error;
+import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_cert;
+import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_details;
+import static se.leap.bitmaskclient.R.string.warning_expired_provider_cert;
/**
* Implements the logic of the http api calls. The methods of this class needs to be called from
@@ -136,10 +131,6 @@ public abstract class ProviderApiManagerBase {
return lastProviderMainUrl;
}
-
- private final MediaType JSON
- = MediaType.parse("application/json; charset=utf-8");
-
public ProviderApiManagerBase(SharedPreferences preferences, Resources resources, OkHttpClientGenerator clientGenerator, ProviderApiServiceCallback callback) {
this.preferences = preferences;
this.resources = resources;
@@ -390,7 +381,7 @@ public abstract class ProviderApiManagerBase {
private boolean setTokenIfAvailable(JSONObject authentication_step_result) {
try {
LeapSRPSession.setToken(authentication_step_result.getString(LeapSRPSession.TOKEN));
- } catch (JSONException e) { //
+ } catch (JSONException e) {
return false;
}
return true;
@@ -540,33 +531,12 @@ public abstract class ProviderApiManagerBase {
}
private String requestStringFromServer(String url, String request_method, String jsonString, List<Pair<String, String>> headerArgs, @NonNull OkHttpClient okHttpClient) {
- //Response response;
String plainResponseBody = null;
- /*RequestBody jsonBody = jsonString != null ? RequestBody.create(JSON, jsonString) : null;
- Request.Builder requestBuilder = new Request.Builder()
- .url(url)
- .method(request_method, jsonBody);
- if (headerArgs != null) {
- for (Pair<String, String> keyValPair : headerArgs) {
- requestBuilder.addHeader(keyValPair.first, keyValPair.second);
- }
- }
- //TODO: move to getHeaderArgs()?
- String locale = Locale.getDefault().getLanguage() + Locale.getDefault().getCountry();
- requestBuilder.addHeader("Accept-Language", locale);
- Request request = requestBuilder.build();
-*/
try {
- //response = okHttpClient.newCall(request).execute();
- //response = ProviderApiConnector.requestStringFromServer(url, request_method, jsonString, headerArgs, okHttpClient);
- //InputStream inputStream = response.body().byteStream();
- //Scanner scanner = new Scanner(inputStream).useDelimiter("\\A");
- //if (scanner.hasNext()) {
- // plainResponseBody = scanner.next();
- //}
plainResponseBody = ProviderApiConnector.requestStringFromServer(url, request_method, jsonString, headerArgs, okHttpClient);
+
} catch (NullPointerException npe) {
plainResponseBody = formatErrorMessage(error_json_exception_user_message);
} catch (UnknownHostException | SocketTimeoutException e) {
@@ -589,6 +559,39 @@ public abstract class ProviderApiManagerBase {
return plainResponseBody;
}
+ private boolean canConnect(String caCert, JSONObject providerDefinition, Bundle result) {
+ JSONObject errorJson = new JSONObject();
+ String baseUrl = getApiUrl(providerDefinition);
+
+ OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(errorJson, caCert);
+ if (okHttpClient == null) {
+ result.putString(ERRORS, errorJson.toString());
+ return false;
+ }
+
+ try {
+
+ return ProviderApiConnector.canConnect(okHttpClient, baseUrl);
+
+ } catch (UnknownHostException | SocketTimeoutException e) {
+ setErrorResult(result, server_unreachable_message, null);
+ } catch (MalformedURLException e) {
+ setErrorResult(result, malformed_url, null);
+ } catch (SSLHandshakeException e) {
+ setErrorResult(result, warning_corrupted_provider_cert, ERROR_INVALID_CERTIFICATE.toString());
+ } catch (ConnectException e) {
+ setErrorResult(result, service_is_down_error, null);
+ } catch (IllegalArgumentException e) {
+ setErrorResult(result, error_no_such_algorithm_exception_user_message, null);
+ } catch (UnknownServiceException e) {
+ //unable to find acceptable protocols - tlsv1.2 not enabled?
+ setErrorResult(result, error_no_such_algorithm_exception_user_message, null);
+ } catch (IOException e) {
+ setErrorResult(result, error_io_exception_user_message, null);
+ }
+ return false;
+ }
+
/**
* Downloads a provider.json from a given URL, adding a new provider using the given name.
*
@@ -642,7 +645,7 @@ public abstract class ProviderApiManagerBase {
result = real_fingerprint.trim().equalsIgnoreCase(expected_fingerprint.trim());
} else
result = false;
- } catch (JSONException | NoSuchAlgorithmException | CertificateEncodingException /*| UnsupportedEncodingException*/ e) {
+ } catch (JSONException | NoSuchAlgorithmException | CertificateEncodingException e) {
result = false;
}
}
@@ -650,10 +653,7 @@ public abstract class ProviderApiManagerBase {
return result;
}
-
-
protected void checkPersistedProviderUpdates() {
- //String providerDomain = getProviderDomain(providerDefinition);
String providerDomain = getDomainFromMainURL(lastProviderMainUrl);
if (hasUpdatedProviderDetails(providerDomain)) {
providerCaCert = getPersistedProviderCA(providerDomain);
@@ -682,8 +682,7 @@ public abstract class ProviderApiManagerBase {
result.putBoolean(RESULT_KEY, true);
} catch (JSONException e) {
e.printStackTrace();
- result.putBoolean(RESULT_KEY, false);
- result = setErrorResult(result, resources.getString(R.string.warning_corrupted_provider_details), ERROR_CORRUPTED_PROVIDER_JSON.toString());
+ setErrorResult(result, warning_corrupted_provider_details, ERROR_CORRUPTED_PROVIDER_JSON.toString());
}
return result;
@@ -699,7 +698,7 @@ public abstract class ProviderApiManagerBase {
X509Certificate certificate = ConfigHelper.parseX509CertificateFromString(cert_string);
if (certificate == null) {
- return setErrorResult(result, resources.getString(R.string.warning_corrupted_provider_cert), ERROR_INVALID_CERTIFICATE.toString());
+ return setErrorResult(result, warning_corrupted_provider_cert, ERROR_INVALID_CERTIFICATE.toString());
}
try {
certificate.checkValidity();
@@ -708,39 +707,38 @@ public abstract class ProviderApiManagerBase {
String expected_fingerprint = fingerprint.split(":")[1];
String real_fingerprint = getFingerprintFromCertificate(certificate, encoding);
if (!real_fingerprint.trim().equalsIgnoreCase(expected_fingerprint.trim())) {
- return setErrorResult(result, resources.getString(R.string.warning_corrupted_provider_cert), ERROR_CERTIFICATE_PINNING.toString());
+ return setErrorResult(result, warning_corrupted_provider_cert, ERROR_CERTIFICATE_PINNING.toString());
}
if (!hasApiUrlExpectedDomain(providerDefinition, mainUrl)){
- return setErrorResult(result, resources.getString(R.string.warning_corrupted_provider_details), ERROR_CORRUPTED_PROVIDER_JSON.toString());
+ return setErrorResult(result, warning_corrupted_provider_details, ERROR_CORRUPTED_PROVIDER_JSON.toString());
}
if (!canConnect(cert_string, providerDefinition, result)) {
return result;
}
} catch (NoSuchAlgorithmException e ) {
- return setErrorResult(result, resources.getString(error_no_such_algorithm_exception_user_message), null);
+ return setErrorResult(result, error_no_such_algorithm_exception_user_message, null);
} catch (ArrayIndexOutOfBoundsException e) {
- return setErrorResult(result, resources.getString(R.string.warning_corrupted_provider_details), ERROR_CORRUPTED_PROVIDER_JSON.toString());
+ return setErrorResult(result, warning_corrupted_provider_details, ERROR_CORRUPTED_PROVIDER_JSON.toString());
} catch (CertificateEncodingException | CertificateNotYetValidException | CertificateExpiredException e) {
- return setErrorResult(result, resources.getString(R.string.warning_expired_provider_cert), ERROR_INVALID_CERTIFICATE.toString());
- } /*catch (UnsupportedEncodingException e) {
- return setErrorResult(result, resources.getString(R.string.warning_corrupted_provider_cert), ERROR_CERTIFICATE_PINNING.toString());
- }*/
+ return setErrorResult(result, warning_expired_provider_cert, ERROR_INVALID_CERTIFICATE.toString());
+ }
result.putBoolean(RESULT_KEY, true);
return result;
}
- protected Bundle setErrorResult(Bundle result, String errorMessage, String errorId) {
+ protected Bundle setErrorResult(Bundle result, int errorMessageId, String errorId) {
JSONObject errorJson = new JSONObject();
if (errorId != null) {
- addErrorMessageToJson(errorJson, errorMessage, errorId);
+ addErrorMessageToJson(errorJson, resources.getString(errorMessageId), errorId);
} else {
- addErrorMessageToJson(errorJson, errorMessage);
+ addErrorMessageToJson(errorJson, resources.getString(errorMessageId));
}
result.putString(ERRORS, errorJson.toString());
+ result.putBoolean(RESULT_KEY, false);
return result;
}
@@ -763,41 +761,6 @@ public abstract class ProviderApiManagerBase {
return false;
}
- private boolean canConnect(String caCert, JSONObject providerDefinition, Bundle result) {
- JSONObject errorJson = new JSONObject();
- String baseUrl = getApiUrl(providerDefinition);
-
- OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(errorJson, caCert);
- if (okHttpClient == null) {
- result.putString(ERRORS, errorJson.toString());
- return false;
- }
-
- //try {
-
- return ProviderApiConnector.canConnect(okHttpClient, baseUrl);
- /*} catch (RuntimeException | IOException e) {
- e.printStackTrace();
- }*/
-
- //return false;
-
-
- /*List<Pair<String, String>> headerArgs = getAuthorizationHeader();
- String plain_response = requestStringFromServer(baseUrl, "GET", null, headerArgs, okHttpClient);
-
- try {
- if (new JSONObject(plain_response).has(ERRORS)) {
- result.putString(ERRORS, plain_response);
- return false;
- }
- } catch (JSONException e) {
- //eat me
- }
-
- return true;*/
- }
-
protected String getCaCertFingerprint(JSONObject providerDefinition) {
try {
return providerDefinition.getString(Provider.CA_CERT_FINGERPRINT);
@@ -921,26 +884,6 @@ public abstract class ProviderApiManagerBase {
String deleteUrl = providerApiUrl + "/logout";
int progress = 0;
- /* Request.Builder requestBuilder = new Request.Builder()
- .url(deleteUrl)
- .delete();
- Request request = requestBuilder.build();*/
-
- //try {
-
- //Response response = okHttpClient.newCall(request).execute();
-// Response response = ProviderApiConnector.delete(okHttpClient, deleteUrl);
-// // v---- was already not authorized
-// if (response.isSuccessful() || response.code() == 401) {
-// broadcastProgress(progress++);
-// LeapSRPSession.setToken("");
-// }
-//
-// } catch (IOException | RuntimeException e) {
-// return false;
-// }
-// return true;
-
if (ProviderApiConnector.delete(okHttpClient, deleteUrl)) {
broadcastProgress(progress++);
LeapSRPSession.setToken("");
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 680f92e1..f59acd3d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -85,6 +85,6 @@
<string name="update_provider_details">Update provider details</string>
<string name="update_certificate">Update certificate</string>
<string name="warning_corrupted_provider_details">Stored provider details are corrupted. You can either update Bitmask (recommended) or update the provider details using a commercial CA certificate.</string>
- <string name="warning_corrupted_provider_cert">Stored provider certificate is corrupted. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.</string>
+ <string name="warning_corrupted_provider_cert">Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.</string>
<string name="warning_expired_provider_cert">Stored provider certificate is expired. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.</string>
</resources>
diff --git a/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java b/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java
index 31c63d01..b20a7759 100644
--- a/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java
+++ b/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java
@@ -37,6 +37,7 @@ import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_C
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;
/**
* Implements the logic of the provider api http requests. The methods of this class need to be called from
@@ -76,7 +77,7 @@ public class ProviderApiManager extends ProviderApiManagerBase {
if (isEmpty(lastProviderMainUrl)) {
currentDownload.putBoolean(RESULT_KEY, false);
- setErrorResult(currentDownload, resources.getString(R.string.malformed_url), null);
+ setErrorResult(currentDownload, malformed_url, null);
return currentDownload;
}
@@ -150,8 +151,7 @@ public class ProviderApiManager extends ProviderApiManagerBase {
}
if (!isValidJson(providerDotJsonString)) {
- result.putString(ERRORS, resources.getString(malformed_url));
- result.putBoolean(RESULT_KEY, false);
+ setErrorResult(result, malformed_url, null);
return result;
}
@@ -247,12 +247,10 @@ public class ProviderApiManager extends ProviderApiManagerBase {
preferences.edit().putString(Provider.CA_CERT + "." + providerDomain, cert_string).commit();
result.putBoolean(RESULT_KEY, true);
} else {
- setErrorResult(result, resources.getString(R.string.warning_corrupted_provider_cert), ERROR_CERTIFICATE_PINNING.toString());
- result.putBoolean(RESULT_KEY, false);
+ setErrorResult(result, warning_corrupted_provider_cert, ERROR_CERTIFICATE_PINNING.toString());
}
} catch (JSONException e) {
- setErrorResult(result, resources.getString(malformed_url), null);
- result.putBoolean(RESULT_KEY, false);
+ setErrorResult(result, malformed_url, null);
}
return result;
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 c39681c4..9ca90b17 100644
--- a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java
+++ b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java
@@ -49,6 +49,8 @@ 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.TestSetupHelper.mockBundle;
import static se.leap.bitmaskclient.testutils.TestSetupHelper.mockClientGenerator;
@@ -58,7 +60,7 @@ import static se.leap.bitmaskclient.testutils.TestSetupHelper.mockProviderApiCon
import static se.leap.bitmaskclient.testutils.TestSetupHelper.mockResources;
import static se.leap.bitmaskclient.testutils.TestSetupHelper.mockResultReceiver;
import static se.leap.bitmaskclient.testutils.TestSetupHelper.mockTextUtils;
-import static se.leap.bitmaskclient.testutils.answers.BackendAnswerFabric.TestBackendErrorCase.NO_ERROR;
+
/**
* Created by cyberta on 04.01.18.
@@ -188,7 +190,7 @@ public class ProviderApiManagerTest {
providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());
Bundle expectedResult = mockBundle();
expectedResult.putBoolean(RESULT_KEY, false);
- expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_CERTIFICATE_PINNING\",\"errors\":\"Stored provider certificate is corrupted. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}");
+ expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_CERTIFICATE_PINNING\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}");
Intent provider_API_command = mockIntent();
Bundle parameters = mockBundle();
@@ -210,7 +212,7 @@ public class ProviderApiManagerTest {
providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());
Bundle expectedResult = mockBundle();
expectedResult.putBoolean(RESULT_KEY, false);
- expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_CERTIFICATE_PINNING\",\"errors\":\"Stored provider certificate is corrupted. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}");
+ expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_CERTIFICATE_PINNING\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}");
Intent provider_API_command = mockIntent();
Bundle parameters = mockBundle();
@@ -232,11 +234,33 @@ public class ProviderApiManagerTest {
providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());
Bundle expectedResult = mockBundle();
expectedResult.putBoolean(RESULT_KEY, false);
- expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_CERTIFICATE_PINNING\",\"errors\":\"Stored provider certificate is corrupted. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}");
+ expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_CERTIFICATE_PINNING\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}");
+
+ Intent provider_API_command = mockIntent();
+ Bundle parameters = mockBundle();
+ parameters.putString(Provider.MAIN_URL, "https://riseup.net");
+
+ 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));
+
+ providerApiManager.handleIntent(provider_API_command);
+ }
+
+
+ @Test
+ public void test_handleIntentSetupProvider_preseededProviderAndCA_outdatedCertificate() throws IOException, CertificateEncodingException, NoSuchAlgorithmException {
+ mockProviderApiConnector(NO_ERROR);
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());
+ Bundle expectedResult = mockBundle();
+ expectedResult.putBoolean(RESULT_KEY, false);
+ expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_INVALID_CERTIFICATE\",\"errors\":\"Stored provider certificate is expired. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}");
Intent provider_API_command = mockIntent();
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")));
provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
@@ -245,5 +269,69 @@ public class ProviderApiManagerTest {
providerApiManager.handleIntent(provider_API_command);
}
+ @Test
+ public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_outdatedCertificate() throws IOException, CertificateEncodingException, NoSuchAlgorithmException {
+ 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.putString(ERRORS, "{\"errorId\":\"ERROR_INVALID_CERTIFICATE\",\"errors\":\"Stored provider certificate is expired. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}");
+ Intent provider_API_command = mockIntent();
+ Bundle parameters = mockBundle();
+ parameters.putString(Provider.MAIN_URL, "https://riseup.net");
+
+ 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));
+
+ providerApiManager.handleIntent(provider_API_command);
+ }
+
+ @Test
+ public void test_handleIntentSetupProvider_preseededProviderAndCA_ValidCertificateButUpdatedCertificateOnServerSide() throws IOException, CertificateEncodingException, NoSuchAlgorithmException {
+ mockFingerprintForCertificate(" a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494");
+ mockProviderApiConnector(ERROR_CASE_UPDATED_CERTIFICATE);
+
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());
+ Bundle expectedResult = mockBundle();
+ expectedResult.putBoolean(RESULT_KEY, false);
+ expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_INVALID_CERTIFICATE\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}");
+
+ Intent provider_API_command = mockIntent();
+ 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")));
+
+ 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));
+
+ providerApiManager.handleIntent(provider_API_command);
+ }
+
+ @Test
+ public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_ValidCertificateButUpdatedCertificateOnServerSide() throws IOException, CertificateEncodingException, NoSuchAlgorithmException {
+ mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494");
+ mockProviderApiConnector(ERROR_CASE_UPDATED_CERTIFICATE);
+ mockPreferences.edit().putString(Provider.KEY + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))).apply();
+ mockPreferences.edit().putString(Provider.CA_CERT + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"))).apply();
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());
+ Bundle expectedResult = mockBundle();
+ expectedResult.putBoolean(RESULT_KEY, false);
+ expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_INVALID_CERTIFICATE\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}");
+
+ Intent provider_API_command = mockIntent();
+ Bundle parameters = mockBundle();
+ parameters.putString(Provider.MAIN_URL, "https://riseup.net");
+
+ 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));
+
+ providerApiManager.handleIntent(provider_API_command);
+ }
}
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/answers/BackendAnswerFabric.java b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BackendMockProvider.java
index 00e276f4..9069661f 100644
--- a/app/src/test/java/se/leap/bitmaskclient/testutils/answers/BackendAnswerFabric.java
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BackendMockProvider.java
@@ -14,22 +14,22 @@
* 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.testutils.BackendMockResponses;
-package se.leap.bitmaskclient.testutils.answers;
-
-import org.mockito.stubbing.Answer;
+import java.io.IOException;
/**
- * Created by cyberta on 09.01.18.
+ * Created by cyberta on 10.01.18.
*/
-public class BackendAnswerFabric {
+public class BackendMockProvider {
/**
* This enum can be useful to provide different responses from a mocked ProviderApiConnector
* in order to test different error scenarios
*/
public enum TestBackendErrorCase {
NO_ERROR,
+ ERROR_CASE_UPDATED_CERTIFICATE,
ERROR_NO_RESPONSE_BODY, // => NullPointerException
ERROR_DNS_RESOLUTION_ERROR, // => UnkownHostException
ERROR_SOCKET_TIMEOUT, // => SocketTimeoutException
@@ -45,10 +45,16 @@ public class BackendAnswerFabric {
ERROR_WRONG_SRP_CREDENTIALS
}
- public static Answer<String> getAnswerForErrorcase(TestBackendErrorCase errorCase) {
+
+ public static void provideBackendResponsesFor(TestBackendErrorCase errorCase) throws IOException {
switch (errorCase) {
+
case NO_ERROR:
- return new NoErrorAnswer();
+ new NoErrorBackendResponse();
+ break;
+ case ERROR_CASE_UPDATED_CERTIFICATE:
+ new UpdatedCertificateBackendResponse();
+ break;
case ERROR_NO_RESPONSE_BODY:
break;
case ERROR_DNS_RESOLUTION_ERROR:
@@ -76,7 +82,5 @@ public class BackendAnswerFabric {
case ERROR_WRONG_SRP_CREDENTIALS:
break;
}
- return null;
}
-
}
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BaseBackendResponse.java b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BaseBackendResponse.java
new file mode 100644
index 00000000..98224019
--- /dev/null
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BaseBackendResponse.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2018 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.testutils.BackendMockResponses;
+
+import android.util.Pair;
+
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mockito;
+import org.mockito.stubbing.Answer;
+
+import java.io.IOException;
+
+import okhttp3.OkHttpClient;
+import se.leap.bitmaskclient.ProviderApiConnector;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.powermock.api.mockito.PowerMockito.mockStatic;
+
+/**
+ * Created by cyberta on 10.01.18.
+ */
+
+public abstract class BaseBackendResponse {
+
+ private Answer<String> answerRequestStringFromServer;
+ private Answer<Boolean> answerCanConnect;
+ private Answer<Boolean> answerDelete;
+
+ public BaseBackendResponse() throws IOException {
+ mockStatic(ProviderApiConnector.class);
+ this.answerRequestStringFromServer = getAnswerForRequestStringFromServer();
+ this.answerCanConnect = getAnswerForCanConnect();
+ this.answerDelete = getAnswerForDelete();
+
+ responseOnRequestStringFromServer();
+ responseOnCanConnect();
+ responseOnDelete();
+
+ }
+
+ public abstract Answer<String> getAnswerForRequestStringFromServer();
+ public abstract Answer<Boolean> getAnswerForCanConnect();
+ public abstract Answer<Boolean> getAnswerForDelete();
+
+
+ public void responseOnRequestStringFromServer() throws IOException, RuntimeException {
+ Mockito.when(ProviderApiConnector.requestStringFromServer(anyString(), anyString(), nullable(String.class), ArgumentMatchers.<Pair<String,String>>anyList(), any(OkHttpClient.class))).
+ thenAnswer(answerRequestStringFromServer);
+ }
+
+ public void responseOnCanConnect() throws IOException, RuntimeException {
+ Mockito.when(ProviderApiConnector.canConnect(any(OkHttpClient.class), anyString())).thenAnswer(answerCanConnect);
+ }
+
+ public void responseOnDelete() throws IOException, RuntimeException {
+ Mockito.when(ProviderApiConnector.delete(any(OkHttpClient.class), anyString())).thenAnswer(answerDelete);
+ }
+
+}
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/NoErrorBackendResponse.java b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/NoErrorBackendResponse.java
new file mode 100644
index 00000000..fa318e42
--- /dev/null
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/NoErrorBackendResponse.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2018 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.testutils.BackendMockResponses;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.IOException;
+
+import static se.leap.bitmaskclient.testutils.TestSetupHelper.getInputAsString;
+
+/**
+ * Created by cyberta on 10.01.18.
+ */
+
+public class NoErrorBackendResponse extends BaseBackendResponse {
+ public NoErrorBackendResponse() throws IOException {
+ super();
+ }
+
+ @Override
+ public Answer<String> getAnswerForRequestStringFromServer() {
+ return new Answer<String>() {
+ @Override
+ public String answer(InvocationOnMock invocation) throws Throwable {
+ String url = (String) invocation.getArguments()[0];
+ String requestMethod = (String) invocation.getArguments()[1];
+ String jsonPayload = (String) invocation.getArguments()[2];
+
+ if (url.contains("/provider.json")) {
+ //download provider json
+ return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"));
+ } else if (url.contains("/ca.crt")) {
+ //download provider ca cert
+ return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"));
+ } else if (url.contains("config/eip-service.json")) {
+ // download provider service json containing gateways, locations and openvpn settings
+ return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.service.json"));
+ } else if (url.contains("/users.json")) {
+ //create new user
+ //TODO: implement me
+ } else if (url.contains("/sessions.json")) {
+ //srp auth: sendAToSRPServer
+ //TODO: implement me
+ } else if (url.contains("/sessions/parmegvtest10.json")){
+ //srp auth: sendM1ToSRPServer
+ //TODO: implement me
+ }
+
+ return null;
+ }
+ };
+ }
+
+ @Override
+ public Answer<Boolean> getAnswerForCanConnect() {
+ return new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ return true;
+ }
+ };
+ }
+
+ @Override
+ public Answer<Boolean> getAnswerForDelete() {
+ return new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ return true;
+ }
+ };
+ }
+
+}
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/UpdatedCertificateBackendResponse.java b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/UpdatedCertificateBackendResponse.java
new file mode 100644
index 00000000..232649a1
--- /dev/null
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/UpdatedCertificateBackendResponse.java
@@ -0,0 +1,97 @@
+/**
+ * Copyright (c) 2018 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.testutils.BackendMockResponses;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.IOException;
+
+import javax.net.ssl.SSLHandshakeException;
+
+import static se.leap.bitmaskclient.testutils.TestSetupHelper.getInputAsString;
+
+/**
+ * Created by cyberta on 10.01.18.
+ */
+
+public class UpdatedCertificateBackendResponse extends BaseBackendResponse {
+ static volatile boolean wasCACertCalled = false;
+
+
+ public UpdatedCertificateBackendResponse() throws IOException {
+ super();
+ }
+
+ @Override
+ public Answer<String> getAnswerForRequestStringFromServer() {
+ return new Answer<String>() {
+
+ @Override
+ public String answer(InvocationOnMock invocation) throws Throwable {
+ String url = (String) invocation.getArguments()[0];
+
+ if (url.contains("/provider.json")) {
+ if (!wasCACertCalled) {
+ throw new SSLHandshakeException("Updated certificate on server side");
+ }
+ //download provider json
+ return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"));
+ } else if (url.contains("/ca.crt")) {
+ //download provider ca cert
+ wasCACertCalled = true;
+ return getInputAsString(getClass().getClassLoader().getResourceAsStream("updated_cert.pem"));
+ } else if (url.contains("config/eip-service.json")) {
+ // download provider service json containing gateways, locations and openvpn settings
+ if (!wasCACertCalled) {
+ throw new SSLHandshakeException("Updated certificate on server side");
+ }
+ return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.service.json"));
+ }
+
+ return null;
+ }
+ };
+ }
+
+ @Override
+ public Answer<Boolean> getAnswerForCanConnect() {
+ return new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ if (!wasCACertCalled) {
+ throw new SSLHandshakeException("Updated certificate on server side");
+ }
+ return true;
+ }
+ };
+ }
+
+ @Override
+ public Answer<Boolean> getAnswerForDelete() {
+ return new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ if (!wasCACertCalled) {
+ throw new SSLHandshakeException("Updated certificate on server side");
+ }
+ return true;
+ }
+ };
+ }
+
+}
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/TestSetupHelper.java b/app/src/test/java/se/leap/bitmaskclient/testutils/TestSetupHelper.java
index 19d7e13f..f8f70eaf 100644
--- a/app/src/test/java/se/leap/bitmaskclient/testutils/TestSetupHelper.java
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/TestSetupHelper.java
@@ -24,11 +24,9 @@ import android.os.Parcelable;
import android.os.ResultReceiver;
import android.support.annotation.NonNull;
import android.text.TextUtils;
-import android.util.Pair;
import org.json.JSONException;
import org.json.JSONObject;
-import org.mockito.ArgumentMatchers;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@@ -50,9 +48,8 @@ import java.util.Set;
import okhttp3.OkHttpClient;
import se.leap.bitmaskclient.ConfigHelper;
import se.leap.bitmaskclient.OkHttpClientGenerator;
-import se.leap.bitmaskclient.ProviderApiConnector;
import se.leap.bitmaskclient.R;
-import se.leap.bitmaskclient.testutils.answers.BackendAnswerFabric;
+import se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider;
import se.leap.bitmaskclient.testutils.matchers.BundleMatcher;
import static org.junit.Assert.assertEquals;
@@ -61,14 +58,11 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
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.testutils.answers.BackendAnswerFabric.TestBackendErrorCase.ERROR_NO_CONNECTION;
-import static se.leap.bitmaskclient.testutils.answers.BackendAnswerFabric.getAnswerForErrorcase;
/**
* Created by cyberta on 08.10.17.
@@ -383,14 +377,11 @@ public class TestSetupHelper {
mockStatic(ConfigHelper.class);
when(ConfigHelper.getFingerprintFromCertificate(any(X509Certificate.class), anyString())).thenReturn(mockedFingerprint);
when(ConfigHelper.checkErroneousDownload(anyString())).thenCallRealMethod();
- when(ConfigHelper.base64toHex(anyString())).thenCallRealMethod();
when(ConfigHelper.parseX509CertificateFromString(anyString())).thenCallRealMethod();
}
- public static void mockProviderApiConnector(final BackendAnswerFabric.TestBackendErrorCase errorCase) throws IOException {
- mockStatic(ProviderApiConnector.class);
- when(ProviderApiConnector.canConnect(any(OkHttpClient.class), anyString())).thenReturn(errorCase != ERROR_NO_CONNECTION);
- when(ProviderApiConnector.requestStringFromServer(anyString(), anyString(), nullable(String.class), ArgumentMatchers.<Pair<String,String>>anyList(), any(OkHttpClient.class))).thenAnswer(getAnswerForErrorcase(errorCase));
+ public static void mockProviderApiConnector(final BackendMockProvider.TestBackendErrorCase errorCase) throws IOException {
+ BackendMockProvider.provideBackendResponsesFor(errorCase);
}
public static OkHttpClientGenerator mockClientGenerator() {
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/answers/NoErrorAnswer.java b/app/src/test/java/se/leap/bitmaskclient/testutils/answers/NoErrorAnswer.java
deleted file mode 100644
index cbf9f6b8..00000000
--- a/app/src/test/java/se/leap/bitmaskclient/testutils/answers/NoErrorAnswer.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * Copyright (c) 2018 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.testutils.answers;
-
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import static se.leap.bitmaskclient.testutils.TestSetupHelper.getInputAsString;
-
-/**
- * Created by cyberta on 09.01.18.
- */
-
-public class NoErrorAnswer implements Answer<String> {
- @Override
- public String answer(InvocationOnMock invocation) throws Throwable {
- String url = (String) invocation.getArguments()[0];
- String requestMethod = (String) invocation.getArguments()[1];
- String jsonPayload = (String) invocation.getArguments()[2];
-
- if (url.contains("/provider.json")) {
- //download provider json
- return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"));
- } else if (url.contains("/ca.crt")) {
- //download provider ca cert
- return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"));
- } else if (url.contains("config/eip-service.json")) {
- // download provider service json containing gateways, locations and openvpn settings
- return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.service.json"));
- } else if (url.contains("/users.json")) {
- //create new user
- //TODO: implement me
- } else if (url.contains("/sessions.json")) {
- //srp auth: sendAToSRPServer
- //TODO: implement me
- } else if (url.contains("/sessions/parmegvtest10.json")){
- //srp auth: sendM1ToSRPServer
- //TODO: implement me
- }
-
- return null;
- }
-}
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/matchers/BundleMatcher.java b/app/src/test/java/se/leap/bitmaskclient/testutils/matchers/BundleMatcher.java
index a7867e08..d2d2a102 100644
--- a/app/src/test/java/se/leap/bitmaskclient/testutils/matchers/BundleMatcher.java
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/matchers/BundleMatcher.java
@@ -1,3 +1,19 @@
+/**
+ * Copyright (c) 2018 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.testutils.matchers;
import android.os.Bundle;
diff --git a/app/src/test/resources/error_messages.json b/app/src/test/resources/error_messages.json
index 486a3dab..4d72b074 100644
--- a/app/src/test/resources/error_messages.json
+++ b/app/src/test/resources/error_messages.json
@@ -11,6 +11,6 @@
"error_json_exception_user_message": "Try again: Bad response from the server",
"error_no_such_algorithm_exception_user_message": "Encryption algorithm not found. Please update your OS!",
"warning_corrupted_provider_details": "Stored provider details are corrupted. You can either update Bitmask (recommended) or update the provider details using a commercial CA certificate.",
-"warning_corrupted_provider_cert": "Stored provider certificate is corrupted. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.",
+"warning_corrupted_provider_cert": "Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.",
"warning_expired_provider_cert": "Stored provider certificate is expired. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate."
} \ No newline at end of file
diff --git a/app/src/test/resources/outdated_cert.pem b/app/src/test/resources/outdated_cert.pem
new file mode 100644
index 00000000..269efe5f
--- /dev/null
+++ b/app/src/test/resources/outdated_cert.pem
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+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
+-----END CERTIFICATE-----
diff --git a/app/src/test/resources/updated_cert.pem b/app/src/test/resources/updated_cert.pem
new file mode 100644
index 00000000..21f9a693
--- /dev/null
+++ b/app/src/test/resources/updated_cert.pem
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFyTCCA7GgAwIBAgIJALD2RMhYzVdWMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJOWTEWMBQGA1UEBwwNTmV3IFlvcmsgQ2l0eTENMAsG
+A1UECgwETEVBUDEVMBMGA1UECwwMVGVzdGluZyBEZXAuMSEwHwYJKoZIhvcNAQkB
+FhJkb25vdHJlcGx5QGxlYXAuc2UwHhcNMTgwMTA5MjMyMTQzWhcNMzMwMTA1MjMy
+MTQzWjB7MQswCQYDVQQGEwJVUzELMAkGA1UECAwCTlkxFjAUBgNVBAcMDU5ldyBZ
+b3JrIENpdHkxDTALBgNVBAoMBExFQVAxFTATBgNVBAsMDFRlc3RpbmcgRGVwLjEh
+MB8GCSqGSIb3DQEJARYSZG9ub3RyZXBseUBsZWFwLnNlMIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAvJqdroZQdvKOE9o/aga9GRfaY05Bs7EKdRp3pabu
+O5uEyx0tRpbif+rcb3DRDWfIH+wxrNcSp5rRbQollttnH5Sdd0aG7taZLPCMHVlk
+j92RRCggQQd3jrDzzKKNo3080B2BerWRK+Rzr0wVF7iD37Lcz5F74FoRhuXoYYSd
+euMPqtp29S6U0bEXtTVdSEYzGKi/EaF5eNcRXUJcPl4aTCH7HTGmhsoCeQuKriU8
+JluGtPxJVji+z3JuIjXFmoxBY2KKb9mEosvXsNNDKvhrrVApSQf/SvDL3FDx9D81
+MAHjQknn1INvSk3M7IWV/dGL0tnDQMiCPUM6XT+RQuM03aCMfu/IvWuDDUNumWBU
+5xtBX0Iu2OybRUzL9kyWsXFSQx617v7tuXuPWg158Dg3RyCIC2VdwouzGGsJXslc
+W4T74w6HsxDpzxqrzwnwVHahn5/qntptFBlB37waGKRdvrTqlWq2jwHSp0F+y/x6
+h9BobBAJ7E6Ix5tprQSkPI6X62yXBfz2GDfZRYL2ltdD72mN5zzoA/SaT+HTtjOK
+wS2GPxb5IZkB7u0yZQOkQ3hDtI4Ve8rj8Z0mK1S0HETU9kvA6/+3KmA1xeMjvlzL
+HNmyIfXtfd5NIIrrRuWwGR2Y9KrezAdW+UuxHDE2SLDNCzJKjyQhzi8110Vtlm4A
+45UCAwEAAaNQME4wHQYDVR0OBBYEFNm1kQeEsxrCNeJA7q/67Pz43/mVMB8GA1Ud
+IwQYMBaAFNm1kQeEsxrCNeJA7q/67Pz43/mVMAwGA1UdEwQFMAMBAf8wDQYJKoZI
+hvcNAQELBQADggIBAJpQL0Wh6eP3XpzS+PyiSFAjtKLsbtkDId0NTSrUiARFSj7I
+S9YH1b0iG9pEd3XaNRffC72/R7UnYz0tbRC8tXYOKRB+gOkOAQXAleq01Qs93mA5
+6Titg8k9qXw6Vv+QK08CJUUFva6vMNOIbWkKN7GfdQZOig2EqLZ+jBdIai/NZ+uD
+US2vk87Lyd+8VQZHsazxLLS/hMyBvJdiFhQmjBpak9J1EisqGWjmRVvo1Vq1aVy8
+IrqW6GweWs0toFflbd57xFeV7+VJ1WDg6HU+JY5hWnTyH8HDQJFTY0GBoPA4gnxx
+c8P6VCuOO67bJPRxlI89+o7lRqfWXc+C2qQqdYTQda9850EwrTRl6dcHQZvZUYak
+DV+vxN+zHZ+lN3RmQMl+BY/sIuDqu3MunPt8c8KXeL0zGw8A3v6UBzMJAN4FbFX6
+Rc9jdCvqi+DqtMq7nGJ+V9swlfPSeg5kot/Z9xlmjqT0256PW6yaxRsXhhNTGM/f
+qCUmxf0aP3W8V6tCorSg6aiJ/xi0tLrfFDttgEH4CJHJh1n6w/D9aM9Q+ZRHTAlN
+JXUZyhyv7OoyA/gsHoL6g28AzuCYAmPLVNS4Ym1L9r4R2Ltq1b4em7aUfIMomgqQ
+xm+bOUujZtIJhDA8ckKKyb7xP0fJ13sTQyUt0/6CxJq6rj0OfVlQ3shGOBTU
+-----END CERTIFICATE-----