From 242ef2603a3cdd91097db9b3824498c544865a10 Mon Sep 17 00:00:00 2001 From: Ahmad Vazirna Date: Tue, 11 Jun 2024 11:59:10 +0200 Subject: [PATCH 1/7] Lint --- app/src/org/commcare/network/HttpCalloutTask.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/org/commcare/network/HttpCalloutTask.java b/app/src/org/commcare/network/HttpCalloutTask.java index 4592aaeddb..cdcadec58f 100755 --- a/app/src/org/commcare/network/HttpCalloutTask.java +++ b/app/src/org/commcare/network/HttpCalloutTask.java @@ -2,6 +2,8 @@ import android.content.Context; +import okhttp3.ResponseBody; + import org.commcare.core.network.AuthenticationInterceptor; import org.commcare.core.network.CaptivePortalRedirectException; import org.commcare.core.network.ModernHttpRequester; @@ -18,6 +20,8 @@ import org.javarosa.xml.util.UnfullfilledRequirementsException; import org.xmlpull.v1.XmlPullParserException; +import retrofit2.Response; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -25,9 +29,6 @@ import javax.net.ssl.SSLException; -import okhttp3.ResponseBody; -import retrofit2.Response; - /** * @author ctsims */ From c4780b960c247c5c2f5e6365d748027205ae2ec7 Mon Sep 17 00:00:00 2001 From: Ahmad Vazirna Date: Tue, 11 Jun 2024 12:42:15 +0200 Subject: [PATCH 2/7] Add enum for response body error messages --- .../org/commcare/network/HttpCalloutTask.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/app/src/org/commcare/network/HttpCalloutTask.java b/app/src/org/commcare/network/HttpCalloutTask.java index cdcadec58f..9c8f7d45b0 100755 --- a/app/src/org/commcare/network/HttpCalloutTask.java +++ b/app/src/org/commcare/network/HttpCalloutTask.java @@ -47,6 +47,25 @@ public enum HttpCalloutOutcomes { CaptivePortal } + // Stores HTTP response body error messages associated with 401s + public enum ResponseBodyErrorMessages { + Max_Login_Attempts_Exceeded("maximum password attempts exceeded"), + No_Error_Message(""); + private final String errorMessage; + + ResponseBodyErrorMessages(String errorMessage) { + this.errorMessage = errorMessage; + } + public static ResponseBodyErrorMessages retrieveErrorMessageValue(String errorMessage){ + for (ResponseBodyErrorMessages msgs : ResponseBodyErrorMessages.values()){ + if (msgs.errorMessage.equals(errorMessage)){ + return msgs; + } + } + throw new IllegalArgumentException("HTTP Response body error message not recognized: "+ errorMessage); + } + } + private final Context c; public HttpCalloutTask(Context c) { From ef5dba34316f549fa127ad026b6250ff0bdc40be Mon Sep 17 00:00:00 2001 From: Ahmad Vazirna Date: Tue, 11 Jun 2024 12:48:27 +0200 Subject: [PATCH 3/7] Refactor --- app/src/org/commcare/network/HttpUtils.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/org/commcare/network/HttpUtils.java b/app/src/org/commcare/network/HttpUtils.java index 450d715deb..f8492ea7e9 100644 --- a/app/src/org/commcare/network/HttpUtils.java +++ b/app/src/org/commcare/network/HttpUtils.java @@ -26,6 +26,10 @@ */ public class HttpUtils { + + private static final String ERROR_BODY_DEFAULT_RESPONSE_KEY = "default_response"; + private static final String ERROR_BODY_ERROR_MESSAGE_KEY = "error"; + public static String getCredential(AuthInfo authInfo) { if (authInfo instanceof AuthInfo.ProvidedAuth) { return getCredential(buildDomainUser(authInfo.username), authInfo.password); @@ -80,8 +84,8 @@ public static String parseUserVisibleError(Response response) { responseStr = response.errorBody().string(); JSONObject errorKeyAndDefault = new JSONObject(responseStr); message = Localization.getWithDefault( - errorKeyAndDefault.getString("error"), - errorKeyAndDefault.getString("default_response")); + errorKeyAndDefault.getString(ERROR_BODY_ERROR_MESSAGE_KEY), + errorKeyAndDefault.getString(ERROR_BODY_DEFAULT_RESPONSE_KEY)); } catch (JSONException | IOException e) { message = responseStr != null ? responseStr : "Unknown issue"; } From 81bbae11f25435fbe734c997dd7d445419c8c202 Mon Sep 17 00:00:00 2001 From: Ahmad Vazirna Date: Tue, 11 Jun 2024 13:30:14 +0200 Subject: [PATCH 4/7] Parse non-localized error messages --- .../engine/references/JavaHttpReference.java | 2 +- app/src/org/commcare/network/HttpUtils.java | 12 ++++++++---- app/src/org/commcare/tasks/DataPullTask.java | 2 +- app/src/org/commcare/utils/FormUploadUtil.java | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/src/org/commcare/engine/references/JavaHttpReference.java b/app/src/org/commcare/engine/references/JavaHttpReference.java index a0db1d44b5..a963d35bfa 100755 --- a/app/src/org/commcare/engine/references/JavaHttpReference.java +++ b/app/src/org/commcare/engine/references/JavaHttpReference.java @@ -78,7 +78,7 @@ public InputStream getStream(Map params) throws IOException { return response.body().byteStream(); } else { if (response.code() == 406) { - throw new IOException(HttpUtils.parseUserVisibleError(response)); + throw new IOException(HttpUtils.parseUserVisibleError(response, true)); } else if (response.code() == 503) { throw new RateLimitedException(); } diff --git a/app/src/org/commcare/network/HttpUtils.java b/app/src/org/commcare/network/HttpUtils.java index f8492ea7e9..104814caa3 100644 --- a/app/src/org/commcare/network/HttpUtils.java +++ b/app/src/org/commcare/network/HttpUtils.java @@ -77,15 +77,19 @@ private static String getCredential(String username, String password) { } } - public static String parseUserVisibleError(Response response) { + public static String parseUserVisibleError(Response response, boolean isMessageLocalized) { String message; String responseStr = null; try { responseStr = response.errorBody().string(); JSONObject errorKeyAndDefault = new JSONObject(responseStr); - message = Localization.getWithDefault( - errorKeyAndDefault.getString(ERROR_BODY_ERROR_MESSAGE_KEY), - errorKeyAndDefault.getString(ERROR_BODY_DEFAULT_RESPONSE_KEY)); + if (isMessageLocalized) { + message = Localization.getWithDefault( + errorKeyAndDefault.getString(ERROR_BODY_ERROR_MESSAGE_KEY), + errorKeyAndDefault.getString(ERROR_BODY_DEFAULT_RESPONSE_KEY)); + } else { + message = errorKeyAndDefault.getString(ERROR_BODY_ERROR_MESSAGE_KEY); + } } catch (JSONException | IOException e) { message = responseStr != null ? responseStr : "Unknown issue"; } diff --git a/app/src/org/commcare/tasks/DataPullTask.java b/app/src/org/commcare/tasks/DataPullTask.java index 7f5242b644..68a58164d5 100644 --- a/app/src/org/commcare/tasks/DataPullTask.java +++ b/app/src/org/commcare/tasks/DataPullTask.java @@ -337,7 +337,7 @@ private ResultAndError makeRequestAndHandleResponse(AndroidTrans } private ResultAndError processErrorResponseWithMessage(RemoteDataPullResponse pullResponse) { - return new ResultAndError<>(PullTaskResult.ACTIONABLE_FAILURE, HttpUtils.parseUserVisibleError(pullResponse.getResponse())); + return new ResultAndError<>(PullTaskResult.ACTIONABLE_FAILURE, HttpUtils.parseUserVisibleError(pullResponse.getResponse(), true)); } private ResultAndError handleAuthFailed() { diff --git a/app/src/org/commcare/utils/FormUploadUtil.java b/app/src/org/commcare/utils/FormUploadUtil.java index e358b81b94..30882461df 100644 --- a/app/src/org/commcare/utils/FormUploadUtil.java +++ b/app/src/org/commcare/utils/FormUploadUtil.java @@ -226,7 +226,7 @@ private static FormUploadResult submitEntity(List parts, Str } private static FormUploadResult processActionableFaiure(Response response) { - String message = parseUserVisibleError(response); + String message = parseUserVisibleError(response, true); FormUploadResult result = FormUploadResult.ACTIONABLE_FAILURE; result.setErrorMessage(message); return result; From 9b6e78e6a01111b1e3b74e6c5a0d855c15ea2e98 Mon Sep 17 00:00:00 2001 From: Ahmad Vazirna Date: Tue, 11 Jun 2024 13:37:02 +0200 Subject: [PATCH 5/7] Add LockedOutUser as HTTP callout outcome --- app/src/org/commcare/network/HttpCalloutTask.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/org/commcare/network/HttpCalloutTask.java b/app/src/org/commcare/network/HttpCalloutTask.java index 9c8f7d45b0..96a8712ca0 100755 --- a/app/src/org/commcare/network/HttpCalloutTask.java +++ b/app/src/org/commcare/network/HttpCalloutTask.java @@ -44,6 +44,7 @@ public enum HttpCalloutOutcomes { NetworkFailureBadPassword, IncorrectPin, AuthOverHttp, + LockedOutUser, CaptivePortal } From dac71ead9123461f5ffa4f7aa45628c42eeb9e7f Mon Sep 17 00:00:00 2001 From: Ahmad Vazirna Date: Tue, 11 Jun 2024 13:54:24 +0200 Subject: [PATCH 6/7] Parse http response error message for failed auth --- app/src/org/commcare/network/HttpCalloutTask.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/org/commcare/network/HttpCalloutTask.java b/app/src/org/commcare/network/HttpCalloutTask.java index 96a8712ca0..c44eb04f9a 100755 --- a/app/src/org/commcare/network/HttpCalloutTask.java +++ b/app/src/org/commcare/network/HttpCalloutTask.java @@ -1,5 +1,8 @@ package org.commcare.network; +import static org.commcare.network.HttpCalloutTask.ResponseBodyErrorMessages.retrieveErrorMessageValue; +import static org.commcare.network.HttpUtils.parseUserVisibleError; + import android.content.Context; import okhttp3.ResponseBody; @@ -200,7 +203,13 @@ protected void beginResponseHandling(Response response) { } protected HttpCalloutOutcomes doResponseAuthFailed(Response response) { - return HttpCalloutOutcomes.AuthFailed; + switch (retrieveErrorMessageValue(parseUserVisibleError(response, false))){ + case Max_Login_Attempts_Exceeded: + return HttpCalloutOutcomes.LockedOutUser; + default: + return HttpCalloutOutcomes.AuthFailed; + } + } protected abstract HttpCalloutOutcomes doResponseOther(Response response); From 6e7cba86eed4297c156651df1839f3227e320a1c Mon Sep 17 00:00:00 2001 From: Ahmad Vazirna Date: Tue, 11 Jun 2024 13:58:20 +0200 Subject: [PATCH 7/7] Show message about user being locked out --- app/assets/locales/android_translatable_strings.txt | 3 +++ app/src/org/commcare/tasks/ManageKeyRecordTask.java | 4 ++++ .../views/notifications/NotificationMessageFactory.java | 7 ++++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/assets/locales/android_translatable_strings.txt b/app/assets/locales/android_translatable_strings.txt index 78452e8a60..c0419f872d 100644 --- a/app/assets/locales/android_translatable_strings.txt +++ b/app/assets/locales/android_translatable_strings.txt @@ -511,6 +511,9 @@ refresh.build.settings.error=No refresh occurred. In order to use this utility, login.attempt.fail.auth.title=Invalid Username or Password login.attempt.fail.auth.detail=Your username or password was not recognized, please type them again and retry. +login.attempts.exceeded.auth.title=Maximum password attempts exceeded +login.attempts.exceeded.auth.detail=Your user account has been locked due to too many attempts. Please contact your supervisor or CommCare Support. + login.attempt.fail.empty.url.title=Url not set login.attempt.fail.empty.url.detail=Sync url is empty. Please contact CommCare Support. diff --git a/app/src/org/commcare/tasks/ManageKeyRecordTask.java b/app/src/org/commcare/tasks/ManageKeyRecordTask.java index bd3933d7aa..6ca76959f2 100644 --- a/app/src/org/commcare/tasks/ManageKeyRecordTask.java +++ b/app/src/org/commcare/tasks/ManageKeyRecordTask.java @@ -207,6 +207,10 @@ protected void keysDoneOther(R receiver, HttpCalloutOutcomes outcome) { Logger.log(LogTypes.TYPE_USER, "ManageKeyRecordTask error|captive portal detected"); receiver.raiseLoginMessage(StockMessages.Sync_CaptivePortal, true); break; + case LockedOutUser: + Logger.log(LogTypes.TYPE_USER, "ManageKeyRecordTask error|maximum login attempts exceeded"); + receiver.raiseLoginMessage(StockMessages.Auth_UserLockedOut, false); + break; default: break; } diff --git a/app/src/org/commcare/views/notifications/NotificationMessageFactory.java b/app/src/org/commcare/views/notifications/NotificationMessageFactory.java index 1f15efceb3..eb15ab55c1 100755 --- a/app/src/org/commcare/views/notifications/NotificationMessageFactory.java +++ b/app/src/org/commcare/views/notifications/NotificationMessageFactory.java @@ -162,7 +162,12 @@ public enum StockMessages implements MessageTag { /** * Bad SSL Certificate * */ - BadSslCertificate("notification.bad.certificate"); + BadSslCertificate("notification.bad.certificate"), + + /** + * Exceeded the maximum number of login attempts * + */ + Auth_UserLockedOut("login.attempts.exceeded.auth"); StockMessages(String root) { this.root = root;