From 246694544c105d0388afb9894203a90e6faeb6d2 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Tue, 5 May 2020 20:19:19 +0300
Subject: [PATCH 01/70] Clean up unused file
---
.../org/smartregister/util/DownloadForm.java | 102 ------------------
1 file changed, 102 deletions(-)
delete mode 100644 opensrp-app/src/main/java/org/smartregister/util/DownloadForm.java
diff --git a/opensrp-app/src/main/java/org/smartregister/util/DownloadForm.java b/opensrp-app/src/main/java/org/smartregister/util/DownloadForm.java
deleted file mode 100644
index c19ad4432..000000000
--- a/opensrp-app/src/main/java/org/smartregister/util/DownloadForm.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package org.smartregister.util;
-
-import android.util.Log;
-
-import org.apache.http.util.ByteArrayBuffer;
-import org.smartregister.domain.DownloadStatus;
-import org.smartregister.domain.Response;
-import org.smartregister.domain.ResponseStatus;
-import org.smartregister.service.FormPathService;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-
-/**
- * Created by Dimas Ciputra on 3/21/15.
- */
-public class DownloadForm {
-
- /**
- * @author Rodgers Andati
- * @since 2019-04-25
- * This method downloads images given url. Migration from the old method that used httpclient
- * @param downloadURL This is the url of the image
- * @param fileName This is how the image should be name after it has been downloaded.
- * @param username This is the username used to authenticate when accessing the url endpoint.
- * @param password This is the password used to authenticate when accessing the url endpoint.
- * @return Response This returns whether the download succeeded or failed.
- */
- public static Response downloadFromURL(String downloadURL, String fileName, String username, String password) {
- HttpURLConnection httpUrlConnection;
- try {
- File dir = new File(FormPathService.sdcardPathDownload);
-
- if (!dir.exists()) {
- dir.mkdirs();
- }
-
- File file = new File(dir, fileName);
-
- long startTime = System.currentTimeMillis();
- Log.d("DownloadFormService", "download begin");
- Log.d("DownloadFormService", "download url: " + downloadURL);
- Log.d("DownloadFormService", "download file name: " + fileName);
-
- /* Open connection to URL */
- URL url = new URL(downloadURL);
- httpUrlConnection = (HttpURLConnection) url.openConnection();
-
- httpUrlConnection.setRequestProperty("username", username);
- httpUrlConnection.setRequestProperty("password", password);
-
- httpUrlConnection.connect();
-
- int status = httpUrlConnection.getResponseCode();
- if (status == HttpURLConnection.HTTP_OK) {
-
- InputStream inputStream = httpUrlConnection.getInputStream();
- BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
-
- long fileLength = bufferedInputStream.available();
- if (fileLength == 0) {
- return new Response(ResponseStatus.success,
- DownloadStatus.nothingDownloaded);
- }
- Log.d("DownloadFormService", "file length : " + fileLength);
-
- ByteArrayBuffer baf = new ByteArrayBuffer(9999);
- int current = 0;
- while ((current = bufferedInputStream.read()) != -1) {
- baf.append((byte) current);
- }
-
- /* Convert the bytes to String */
- FileOutputStream fos = new FileOutputStream(file);
- fos.write(baf.toByteArray());
- fos.flush();
- fos.close();
-
- Log.d("DownloadFormService",
- "download finished in " + ((System.currentTimeMillis() - startTime) / 1000)
- + " sec");
- httpUrlConnection.disconnect();
-
- } else {
- Log.d("RESPONSE", "Server returned non-OK status: " + status);
- return new Response(ResponseStatus.failure, DownloadStatus.failedDownloaded);
- }
-
- } catch (IOException e) {
- Log.d("DownloadFormService", "download error : " + e);
- return new Response(ResponseStatus.success, DownloadStatus.failedDownloaded);
- }
-
- return new Response(ResponseStatus.success, DownloadStatus.downloaded);
- }
-
-}
\ No newline at end of file
From abd54bd799f0f0876f03a251c07bd84ff808ed0d Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Tue, 5 May 2020 20:21:19 +0300
Subject: [PATCH 02/70] Implement account authenticator
---
.../account/AbstractAccountAuthenticator.java | 14 ++
.../account/AccountAuthenticator.java | 122 ++++++++++++++++++
.../account/AccountAuthenticatorXml.java | 34 +++++
3 files changed, 170 insertions(+)
create mode 100644 opensrp-app/src/main/java/org/smartregister/account/AbstractAccountAuthenticator.java
create mode 100644 opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
create mode 100644 opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticatorXml.java
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AbstractAccountAuthenticator.java b/opensrp-app/src/main/java/org/smartregister/account/AbstractAccountAuthenticator.java
new file mode 100644
index 000000000..5497ef7ba
--- /dev/null
+++ b/opensrp-app/src/main/java/org/smartregister/account/AbstractAccountAuthenticator.java
@@ -0,0 +1,14 @@
+package org.smartregister.account;
+
+import android.content.Context;
+
+/**
+ * Created by ndegwamartin on 11/05/2020.
+ */
+public abstract class AbstractAccountAuthenticator extends android.accounts.AbstractAccountAuthenticator {
+ public AbstractAccountAuthenticator(Context context) {
+ super(context);
+ }
+
+ public abstract String getRefreshToken();
+}
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java b/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
new file mode 100644
index 000000000..48d8158ac
--- /dev/null
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
@@ -0,0 +1,122 @@
+package org.smartregister.account;
+
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.accounts.NetworkErrorException;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import org.smartregister.CoreLibrary;
+import org.smartregister.view.activity.LoginActivity;
+
+import timber.log.Timber;
+
+/**
+ * Created by ndegwamartin on 2020-04-27.
+ */
+public class AccountAuthenticator extends AbstractAccountAuthenticator {
+
+ private String TAG = AccountAuthenticator.class.getCanonicalName();
+ private final Context mContext;
+
+ public AccountAuthenticator(Context context) {
+ super(context);
+
+ this.mContext = context;
+ }
+
+ @Override
+ public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
+
+ final Intent intent = new Intent(mContext, LoginActivity.class);
+ intent.putExtra(AccountHelper.INTENT_KEY.ACCOUNT_TYPE, accountType);
+ intent.putExtra(AccountHelper.INTENT_KEY.AUTH_TYPE, authTokenType);
+ intent.putExtra(AccountHelper.INTENT_KEY.IS_NEW_ACCOUNT, true);
+ intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
+
+ final Bundle bundle = new Bundle();
+ bundle.putParcelable(AccountManager.KEY_INTENT, intent);
+ return bundle;
+ }
+
+ @Override
+ public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
+
+ Timber.d("getAuthToken");
+
+ AccountManager accountManager = CoreLibrary.getInstance().getAccountManager();
+
+ String authToken = accountManager.peekAuthToken(account, authTokenType);
+ String refreshToken = "";
+ Timber.d("peekAuthToken " + authToken);
+
+ if (TextUtils.isEmpty(authToken)) {
+ final String password = accountManager.getPassword(account);
+ if (password != null) {
+ try {
+
+ Timber.d("Authenticate with saved credentials");
+
+ AccountResponse accountResponse = CoreLibrary.getInstance().context().getHttpAgent().oauth2authenticate(account.name, password, AccountHelper.OAUTH.GRANT_TYPE.PASSWORD);
+ authToken = accountResponse.getAccessToken();
+ refreshToken = accountResponse.getRefreshToken();
+
+ } catch (Exception e) {
+ Timber.e(e);
+ }
+ }
+ }
+
+ if (!TextUtils.isEmpty(authToken)) {
+ final Bundle result = new Bundle();
+ result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
+ result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
+ result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
+ result.putString(AccountHelper.KEY_REFRESH_TOKEN, refreshToken);
+ return result;
+ }
+
+ final Intent intent = new Intent(mContext, LoginActivity.class);
+ intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
+ intent.putExtra(AccountHelper.INTENT_KEY.ACCOUNT_TYPE, account.type);
+ intent.putExtra(AccountHelper.INTENT_KEY.AUTH_TYPE, authTokenType);
+ final Bundle bundle = new Bundle();
+ bundle.putParcelable(AccountManager.KEY_INTENT, intent);
+ return bundle;
+ }
+
+ @Override
+ public String getAuthTokenLabel(String authTokenType) {
+ return authTokenType.toUpperCase();
+ }
+
+ @Override
+ public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
+ final Bundle result = new Bundle();
+ result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
+ return result;
+ }
+
+ @Override
+ public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
+ return null;
+ }
+
+ @Override
+ public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
+ return null;
+ }
+
+ @Override
+ public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
+ return null;
+ }
+
+ @Override
+ public String getRefreshToken() {
+ return AccountHelper.getAccountManagerValue(AccountHelper.KEY_REFRESH_TOKEN,CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType());
+ }
+}
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticatorXml.java b/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticatorXml.java
new file mode 100644
index 000000000..f6a2956ed
--- /dev/null
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticatorXml.java
@@ -0,0 +1,34 @@
+package org.smartregister.account;
+
+/**
+ * Created by ndegwamartin on 08/05/2020.
+ */
+public class AccountAuthenticatorXml {
+ private String accountType;
+ private String accountName;
+ private int icon;
+
+ public String getAccountType() {
+ return accountType;
+ }
+
+ public String getAccountName() {
+ return accountName;
+ }
+
+ public int getIcon() {
+ return icon;
+ }
+
+ public void setAccountType(String accountType) {
+ this.accountType = accountType;
+ }
+
+ public void setAccountName(String accountName) {
+ this.accountName = accountName;
+ }
+
+ public void setIcon(int icon) {
+ this.icon = icon;
+ }
+}
From 75e54a7981fafd8adc12268dd452d9fef40b2740 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Wed, 6 May 2020 16:21:00 +0300
Subject: [PATCH 03/70] Implement account service and helpers
---
.../smartregister/account/AccountError.java | 18 +++++
.../smartregister/account/AccountHelper.java | 74 +++++++++++++++++++
.../account/AccountResponse.java | 62 ++++++++++++++++
.../smartregister/account/AccountService.java | 19 +++++
4 files changed, 173 insertions(+)
create mode 100644 opensrp-app/src/main/java/org/smartregister/account/AccountError.java
create mode 100644 opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
create mode 100644 opensrp-app/src/main/java/org/smartregister/account/AccountResponse.java
create mode 100644 opensrp-app/src/main/java/org/smartregister/account/AccountService.java
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountError.java b/opensrp-app/src/main/java/org/smartregister/account/AccountError.java
new file mode 100644
index 000000000..87cefcd8a
--- /dev/null
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountError.java
@@ -0,0 +1,18 @@
+package org.smartregister.account;
+
+import java.io.Serializable;
+
+/**
+ * Created by ndegwamartin on 2020-04-27.
+ */
+public class AccountError implements Serializable {
+
+ private int statusCode;
+ private String error;
+
+ public AccountError(int statusCode, String error) {
+ this.statusCode = statusCode;
+ this.error = error;
+
+ }
+}
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
new file mode 100644
index 000000000..df0dda4bb
--- /dev/null
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
@@ -0,0 +1,74 @@
+package org.smartregister.account;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+
+import org.smartregister.CoreLibrary;
+
+/**
+ * Created by ndegwamartin on 2020-04-27.
+ */
+public class AccountHelper {
+
+ private static AccountManager accountManager = CoreLibrary.getInstance().getAccountManager();
+
+ public final static String KEY_REFRESH_TOKEN = "KEY_REFRESH_TOKEN";
+ public final static int MAX_AUTH_RETRIES = 1;
+
+ public static final class OAUTH {
+ public final static String TOKEN_ENDPOINT = "/oauth/token";
+
+ public static final class GRANT_TYPE {
+ public final static String PASSWORD = "password";
+ public final static String REFRESH_TOKEN = "refresh_token";
+
+ }
+ }
+
+ public static final class INTENT_KEY {
+
+ public final static String ACCOUNT_TYPE = "ACCOUNT_TYPE";
+ public final static String AUTH_TYPE = "AUTH_TYPE";
+ public final static String ACCOUNT_NAME = "ACCOUNT_NAME";
+ public static final String ERROR_MESSAGE = "ERROR_MESSAGE";
+ public final static String ACCOUNT_PASSWORD = "ACCOUNT_PASSWORD";
+ public final static String IS_NEW_ACCOUNT = "IS_NEW_ACCOUNT";
+ public final static String ACCOUNT_GROUP_ID = "ACCOUNT_GROUP_ID";
+ }
+
+ public static final class TOKEN_TYPE {
+ public final static String PROVIDER = "provider";
+ public final static String ADMIN = "admin";
+ }
+
+
+ public static Account getOauthAccountByType(String accountType) {
+ Account[] accounts = accountManager.getAccountsByType(accountType);
+ if (accounts.length > 0) {
+ Account account = accounts[0];
+ return account;
+ }
+ return null;
+ }
+
+ public static String getAccountManagerValue(String key, String accountType) {
+ Account account = AccountHelper.getOauthAccountByType(accountType);
+ if (account != null) {
+ return CoreLibrary.getInstance().getAccountManager().getUserData(account, key);
+ }
+ return null;
+ }
+
+ /**
+ * @param accountType unique name to identify our account type in the Account Manager
+ * @param authTokenType type of token requested from server e.g. PROVIDER, ADMIN
+ * @return
+ */
+ public static String getOAuthToken(String accountType, String authTokenType) {
+ Account account = getOauthAccountByType(accountType);
+
+ return accountManager.peekAuthToken(account, authTokenType);
+ }
+
+
+}
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountResponse.java b/opensrp-app/src/main/java/org/smartregister/account/AccountResponse.java
new file mode 100644
index 000000000..0ece15cfc
--- /dev/null
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountResponse.java
@@ -0,0 +1,62 @@
+package org.smartregister.account;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Created by ndegwamartin on 06/05/2020.
+ */
+public class AccountResponse {
+
+ @SerializedName("access_token")
+ private String accessToken;
+
+ @SerializedName("token_type")
+ private String tokenType;
+
+ @SerializedName("refresh_token")
+ private String refreshToken;
+
+
+ @SerializedName("expires_in")
+ private Integer ExpiresIn;
+
+ @SerializedName("scope")
+ private String Scope;
+
+ private int status;
+
+ private AccountError accountError;
+
+ public AccountResponse(int status, AccountError accountError) {
+ this.status = status;
+ this.accountError = accountError;
+ }
+
+ public int getStatus() {
+ return status;
+ }
+
+ public AccountError getAccountError() {
+ return accountError;
+ }
+
+ public String getAccessToken() {
+ return accessToken;
+ }
+
+ public String getTokenType() {
+ return tokenType;
+ }
+
+ public String getRefreshToken() {
+ return refreshToken;
+ }
+
+ public Integer getExpiresIn() {
+ return ExpiresIn;
+ }
+
+ public String getScope() {
+ return Scope;
+ }
+}
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountService.java b/opensrp-app/src/main/java/org/smartregister/account/AccountService.java
new file mode 100644
index 000000000..2056fc9c8
--- /dev/null
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountService.java
@@ -0,0 +1,19 @@
+package org.smartregister.account;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * Created by ndegwamartin on 2020-04-27.
+ */
+public class AccountService extends Service {
+
+ @Override
+ public IBinder onBind(Intent intent) {
+
+ AccountAuthenticator authenticator = new AccountAuthenticator(this);
+ return authenticator.getIBinder();
+
+ }
+}
From dd3d2da96e01cd97760f3a441e415e7ce6c7175c Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Thu, 7 May 2020 19:10:30 +0300
Subject: [PATCH 04/70] Migrate login Migrate login to oauth2 Implement
configuration helpers Update Manifest and properties configs
---
gradle.properties | 3 +-
opensrp-app/AndroidManifest.xml | 68 +++++++++++-------
opensrp-app/res/xml/authenticator.xml | 7 ++
.../java/org/smartregister/CoreLibrary.java | 22 ++++++
.../org/smartregister/SyncConfiguration.java | 18 ++++-
.../login/interactor/BaseLoginInteractor.java | 13 ++--
.../login/task/RemoteLoginTask.java | 71 ++++++++++++++-----
.../view/activity/BaseLoginActivity.java | 18 ++++-
.../view/activity/LoginActivity.java | 4 +-
.../view/contract/BaseLoginContract.java | 4 ++
10 files changed, 174 insertions(+), 54 deletions(-)
create mode 100644 opensrp-app/res/xml/authenticator.xml
diff --git a/gradle.properties b/gradle.properties
index ae4fbfebe..ac928efda 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,4 +1,4 @@
-VERSION_NAME=1.15.4-SNAPSHOT
+VERSION_NAME=2.0.4-SNAPSHOT
VERSION_CODE=1
GROUP=org.smartregister
POM_SETTING_DESCRIPTION=OpenSRP Client Core Application
@@ -12,5 +12,4 @@ POM_SETTING_LICENCE_DIST=repo
POM_SETTING_DEVELOPER_ID=opensrp
POM_SETTING_DEVELOPER_NAME=OpenSRP Onadev
-
org.gradle.jvmargs=-Xmx2048m
diff --git a/opensrp-app/AndroidManifest.xml b/opensrp-app/AndroidManifest.xml
index 7431d16d6..4428271db 100644
--- a/opensrp-app/AndroidManifest.xml
+++ b/opensrp-app/AndroidManifest.xml
@@ -1,68 +1,82 @@
+ android:versionName="3.0.1">
-
+
+
+
+
+ android:name=".service.ImageUploadSyncService"
+ android:enabled="true" />
+
+
+
+
+
+
+
+ android:screenOrientation="landscape"
+ android:theme="@style/AppThemeNoTitle" />
+ android:screenOrientation="landscape"
+ android:theme="@style/AppThemeNoTitle" />
+ android:screenOrientation="landscape"
+ android:theme="@style/AppThemeNoTitle" />
+ android:screenOrientation="landscape"
+ android:theme="@style/AppThemeNoTitle" />
+ android:screenOrientation="landscape"
+ android:theme="@style/AppThemeNoTitle" />
+ android:screenOrientation="landscape"
+ android:theme="@style/AppThemeNoTitle" />
+ android:screenOrientation="landscape"
+ android:theme="@android:style/Theme.Holo.NoActionBar" />
+ android:screenOrientation="portrait"
+ android:theme="@style/AppThemeNoTitle" />
+ android:screenOrientation="landscape"
+ android:theme="@android:style/Theme.Holo.Dialog.NoActionBar" />
-
+
-
+
diff --git a/opensrp-app/res/xml/authenticator.xml b/opensrp-app/res/xml/authenticator.xml
new file mode 100644
index 000000000..1df47b99b
--- /dev/null
+++ b/opensrp-app/res/xml/authenticator.xml
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/opensrp-app/src/main/java/org/smartregister/CoreLibrary.java b/opensrp-app/src/main/java/org/smartregister/CoreLibrary.java
index 7bdfc3af5..fdc53fcc9 100644
--- a/opensrp-app/src/main/java/org/smartregister/CoreLibrary.java
+++ b/opensrp-app/src/main/java/org/smartregister/CoreLibrary.java
@@ -1,11 +1,13 @@
package org.smartregister;
+import android.accounts.AccountManager;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
+import org.smartregister.account.AccountAuthenticatorXml;
import org.smartregister.authorizer.P2PSyncAuthorizationService;
import org.smartregister.p2p.P2PLibrary;
import org.smartregister.pathevaluator.PathEvaluatorLibrary;
@@ -13,6 +15,7 @@
import org.smartregister.repository.P2PReceiverTransferDao;
import org.smartregister.repository.P2PSenderTransferDao;
import org.smartregister.sync.P2PSyncFinishCallback;
+import org.smartregister.util.Utils;
import static android.preference.PreferenceManager.getDefaultSharedPreferences;
@@ -34,6 +37,10 @@ public class CoreLibrary {
private P2POptions p2POptions;
+ private AccountManager accountManager;
+
+ private AccountAuthenticatorXml authenticatorXml;
+
public static void init(Context context) {
init(context, null);
}
@@ -151,6 +158,20 @@ public SyncConfiguration getSyncConfiguration() {
return syncConfiguration;
}
+ public AccountManager getAccountManager() {
+ if (accountManager == null)
+ accountManager = AccountManager.get(context.applicationContext());
+
+ return accountManager;
+ }
+
+ public AccountAuthenticatorXml getAccountAuthenticatorXml() {
+ if (authenticatorXml == null)
+ authenticatorXml = Utils.parseAuthenticatorXMLConfigData(context.applicationContext());
+
+ return authenticatorXml;
+ }
+
public static long getBuildTimeStamp() {
return buildTimeStamp;
}
@@ -186,4 +207,5 @@ private void sendPeerToPeerProcessingStatus(boolean status) {
LocalBroadcastManager.getInstance(context.applicationContext())
.sendBroadcast(intent);
}
+
}
diff --git a/opensrp-app/src/main/java/org/smartregister/SyncConfiguration.java b/opensrp-app/src/main/java/org/smartregister/SyncConfiguration.java
index 9385ff80c..290404ad9 100644
--- a/opensrp-app/src/main/java/org/smartregister/SyncConfiguration.java
+++ b/opensrp-app/src/main/java/org/smartregister/SyncConfiguration.java
@@ -1,5 +1,7 @@
package org.smartregister;
+import org.smartregister.account.AccountHelper;
+
import java.util.ArrayList;
import java.util.List;
@@ -29,7 +31,9 @@ public boolean disableActionService() {
return false;
}
- // determines whether to sync settings from server side. return false if not
+ /**
+ * Determines whether to sync settings from server side. return false if not
+ */
public boolean isSyncSettings() {
return false;
}
@@ -165,4 +169,16 @@ public boolean clearDataOnNewTeamLogin() {
public boolean runPlanEvaluationOnClientProcessing() {
return false;
}
+
+ public abstract String getOauthClientId();
+
+ public abstract String getOauthClientSecret();
+
+ /**
+ * Returns number of times to retry if 401 is received on a request before forcing user to enter credentials
+ * Default is once, can be overriden
+ */
+ public int getMaxAuthenticationRetries() {
+ return AccountHelper.MAX_AUTH_RETRIES;
+ }
}
diff --git a/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java b/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java
index 0a512ff23..e59d97d3e 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java
@@ -12,6 +12,7 @@
import org.smartregister.CoreLibrary;
import org.smartregister.P2POptions;
import org.smartregister.R;
+import org.smartregister.account.AccountAuthenticatorXml;
import org.smartregister.domain.LoginResponse;
import org.smartregister.domain.TimeStatus;
import org.smartregister.event.Listener;
@@ -72,7 +73,7 @@ public void loginWithLocalFlag(WeakReference view, boole
if (localLogin) {
localLogin(view, userName, password);
} else {
- remoteLogin(userName, password);
+ remoteLogin(userName, password, CoreLibrary.getInstance().getAccountAuthenticatorXml());
}
Timber.i("Login result finished " + DateTime.now().toString());
@@ -115,19 +116,19 @@ public void run() {
}).start();
}
- private void remoteLogin(final String userName, final String password) {
+ private void remoteLogin(final String userName, final String password, final AccountAuthenticatorXml accountAuthenticatorXml) {
try {
if (getSharedPreferences().fetchBaseURL("").isEmpty() && StringUtils.isNotBlank(this.getApplicationContext().getString(R.string.opensrp_url))) {
getSharedPreferences().savePreference("DRISHTI_BASE_URL", getApplicationContext().getString(R.string.opensrp_url));
}
if (!getSharedPreferences().fetchBaseURL("").isEmpty()) {
- tryRemoteLogin(userName, password, new Listener() {
+ tryRemoteLogin(userName, password, accountAuthenticatorXml, new Listener() {
public void onEvent(LoginResponse loginResponse) {
getLoginView().enableLoginButton(true);
if (loginResponse == LoginResponse.SUCCESS) {
- String username=loginResponse.payload()!=null && loginResponse.payload().user != null && StringUtils.isNotBlank(loginResponse.payload().user.getUsername())
+ String username = loginResponse.payload() != null && loginResponse.payload().user != null && StringUtils.isNotBlank(loginResponse.payload().user.getUsername())
? loginResponse.payload().user.getUsername() : userName;
if (getUserService().isUserInPioneerGroup(username)) {
TimeStatus timeStatus = getUserService().validateDeviceTime(
@@ -198,11 +199,11 @@ public void onComplete() {
}
}
- private void tryRemoteLogin(final String userName, final String password, final Listener afterLogincheck) {
+ private void tryRemoteLogin(final String userName, final String password, final AccountAuthenticatorXml accountAuthenticatorXml, final Listener afterLogincheck) {
if (remoteLoginTask != null && !remoteLoginTask.isCancelled()) {
remoteLoginTask.cancel(true);
}
- remoteLoginTask = new RemoteLoginTask(getLoginView(), userName, password, afterLogincheck);
+ remoteLoginTask = new RemoteLoginTask(getLoginView(), userName, password, accountAuthenticatorXml, afterLogincheck);
remoteLoginTask.execute();
}
diff --git a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
index fbcfc5967..4dc1580ea 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
@@ -1,6 +1,9 @@
package org.smartregister.login.task;
+import android.accounts.Account;
+import android.accounts.AccountManager;
import android.os.AsyncTask;
+import android.os.Bundle;
import org.json.JSONArray;
import org.json.JSONException;
@@ -9,6 +12,9 @@
import org.smartregister.Context;
import org.smartregister.CoreLibrary;
import org.smartregister.R;
+import org.smartregister.account.AccountAuthenticatorXml;
+import org.smartregister.account.AccountHelper;
+import org.smartregister.account.AccountResponse;
import org.smartregister.domain.LoginResponse;
import org.smartregister.event.Listener;
import org.smartregister.sync.helper.SyncSettingsServiceHelper;
@@ -17,6 +23,8 @@
import timber.log.Timber;
+import static org.smartregister.domain.LoginResponse.CUSTOM_SERVER_RESPONSE;
+
/**
* Created by ndegwamartin on 22/06/2018.
*/
@@ -25,13 +33,15 @@ public class RemoteLoginTask extends AsyncTask {
private BaseLoginContract.View mLoginView;
private final String mUsername;
private final String mPassword;
+ private final AccountAuthenticatorXml mAccountAuthenticatorXml;
private final Listener afterLoginCheck;
- public RemoteLoginTask(BaseLoginContract.View loginView, String username, String password, Listener afterLoginCheck) {
+ public RemoteLoginTask(BaseLoginContract.View loginView, String username, String password, AccountAuthenticatorXml accountAuthenticatorXml, Listener afterLoginCheck) {
mLoginView = loginView;
mUsername = username;
mPassword = password;
+ mAccountAuthenticatorXml = accountAuthenticatorXml;
this.afterLoginCheck = afterLoginCheck;
}
@@ -43,23 +53,52 @@ protected void onPreExecute() {
@Override
protected LoginResponse doInBackground(Void... params) {
- LoginResponse loginResponse = getOpenSRPContext().userService().isValidRemoteLogin(mUsername, mPassword);
- if (loginResponse != null && loginResponse.equals(LoginResponse.SUCCESS) && getOpenSRPContext().userService().getGroupId(mUsername) != null && CoreLibrary.getInstance().getSyncConfiguration().isSyncSettings()) {
- publishProgress(R.string.loading_client_settings);
-
- SyncSettingsServiceHelper syncSettingsServiceHelper = new SyncSettingsServiceHelper(getOpenSRPContext().configuration().dristhiBaseURL(), getOpenSRPContext().getHttpAgent());
- syncSettingsServiceHelper.setUsername(mUsername);
- syncSettingsServiceHelper.setPassword(mPassword);
-
- try {
- JSONArray settings = pullSetting(syncSettingsServiceHelper, loginResponse);
- JSONObject data = new JSONObject();
- data.put(AllConstants.PREF_KEY.SETTINGS, settings);
- loginResponse.setRawData(data);
- } catch (JSONException e) {
- Timber.e(e);
+
+ LoginResponse loginResponse;
+ Bundle userData = null;
+ try {
+
+ AccountResponse response = CoreLibrary.getInstance().context().getHttpAgent().oauth2authenticate(mUsername, mPassword, AccountHelper.OAUTH.GRANT_TYPE.PASSWORD);
+
+ AccountManager mAccountManager = CoreLibrary.getInstance().getAccountManager();
+
+ final Account account = new Account(mUsername, mAccountAuthenticatorXml.getAccountType());
+
+ loginResponse = getOpenSRPContext().userService().fetchUserDetails(response.getAccessToken());
+
+ if (loginResponse != null && loginResponse.equals(LoginResponse.SUCCESS)) {
+
+ userData = getOpenSRPContext().userService().saveUserGroup(mUsername, mPassword, loginResponse.payload());
+
+ if (getOpenSRPContext().userService().getGroupId(mUsername) != null && CoreLibrary.getInstance().getSyncConfiguration().isSyncSettings()) {
+
+
+ publishProgress(R.string.loading_client_settings);
+
+
+ SyncSettingsServiceHelper syncSettingsServiceHelper = new SyncSettingsServiceHelper(getOpenSRPContext().configuration().dristhiBaseURL(), getOpenSRPContext().getHttpAgent());
+ try {
+ JSONArray settings = pullSetting(syncSettingsServiceHelper, loginResponse);
+ JSONObject data = new JSONObject();
+ data.put(AllConstants.PREF_KEY.SETTINGS, settings);
+ loginResponse.setRawData(data);
+ } catch (JSONException e) {
+ Timber.e(e);
+ }
+
+ }
}
+
+ mAccountManager.addAccountExplicitly(account, response.getAccessToken(), userData);
+ mAccountManager.setAuthToken(account, mLoginView.getAuthTokenType(), response.getAccessToken());
+ mAccountManager.setUserData(account, AccountHelper.KEY_REFRESH_TOKEN, response.getRefreshToken());
+
+
+ } catch (Exception e) {
+
+ loginResponse = CUSTOM_SERVER_RESPONSE.withMessage(e.getMessage());
}
+
return loginResponse;
}
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java b/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java
index e04d13d67..a2754f541 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java
@@ -27,6 +27,7 @@
import org.joda.time.DateTime;
import org.smartregister.R;
+import org.smartregister.account.AccountHelper;
import org.smartregister.util.SyncUtils;
import org.smartregister.util.Utils;
import org.smartregister.view.contract.BaseLoginContract;
@@ -49,6 +50,7 @@ public abstract class BaseLoginActivity extends MultiLanguageActivity implements
private Button loginButton;
private Boolean showPasswordChecked = false;
private SyncUtils syncUtils;
+ private String authTokenType;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -272,7 +274,7 @@ public boolean isAppVersionAllowed() {
} catch (PackageManager.NameNotFoundException e) {
Timber.e(e);
}
- return isAppVersionAllowed;
+ return isAppVersionAllowed;
}
@Override
@@ -287,4 +289,18 @@ public void showClearDataDialog(@NonNull DialogInterface.OnClickListener onClick
.setCancelable(false)
.show();
}
+
+ public String getAuthTokenType() {
+
+ authTokenType = getIntent().getStringExtra(AccountHelper.INTENT_KEY.AUTH_TYPE);
+
+ if (authTokenType == null)
+ authTokenType = AccountHelper.TOKEN_TYPE.PROVIDER;
+
+ return authTokenType;
+ }
+
+ public boolean isNewAccount(){
+ return getIntent().getBooleanExtra(AccountHelper.INTENT_KEY.IS_NEW_ACCOUNT, false);
+ }
}
\ No newline at end of file
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java b/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java
index a38c38d78..a676faae7 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java
@@ -1,5 +1,6 @@
package org.smartregister.view.activity;
+import android.accounts.AccountAuthenticatorActivity;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
@@ -171,7 +172,6 @@ private void localLogin(View view, String userName, String password) {
}
-
private void remoteLogin(final View view, final String userName, final String password) {
try {
@@ -267,7 +267,7 @@ private void remoteLoginWith(String userName, String password, LoginResponseData
DrishtiSyncScheduler.startOnlyIfConnectedToNetwork(getApplicationContext());
}
- private void goToHome() {
+ protected void goToHome() {
startActivity(new Intent(this, NativeHomeActivity.class));
finish();
}
diff --git a/opensrp-app/src/main/java/org/smartregister/view/contract/BaseLoginContract.java b/opensrp-app/src/main/java/org/smartregister/view/contract/BaseLoginContract.java
index e52fa9096..a020d8e52 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/contract/BaseLoginContract.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/contract/BaseLoginContract.java
@@ -59,6 +59,10 @@ interface View {
boolean isAppVersionAllowed();
void showClearDataDialog(@NonNull DialogInterface.OnClickListener onClickListener);
+
+ String getAuthTokenType();
+
+ boolean isNewAccount();
}
interface Interactor {
From c0b858934a0bc165fec2575bf7d67c4755a11104 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Fri, 8 May 2020 18:50:23 +0300
Subject: [PATCH 05/70] Migrate user service Clean up update utils
---
.../smartregister/service/UserService.java | 50 ++++++++++++++++---
.../helper/SyncSettingsServiceHelper.java | 5 ++
.../org/smartregister/util/SyncUtils.java | 40 ++-------------
.../java/org/smartregister/util/Utils.java | 49 +++++++++++++++++-
4 files changed, 99 insertions(+), 45 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/service/UserService.java b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
index d14b9d5ac..6ec940e33 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/UserService.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
@@ -2,6 +2,7 @@
import android.annotation.TargetApi;
import android.os.Build;
+import android.os.Bundle;
import android.security.KeyPairGeneratorSpec;
import android.support.annotation.VisibleForTesting;
import android.util.Base64;
@@ -11,6 +12,7 @@
import org.smartregister.DristhiConfiguration;
import org.smartregister.SyncConfiguration;
import org.smartregister.SyncFilter;
+import org.smartregister.account.AccountHelper;
import org.smartregister.domain.LoginResponse;
import org.smartregister.domain.Response;
import org.smartregister.domain.TimeStatus;
@@ -291,6 +293,31 @@ public boolean isUserInPioneerGroup(String userName) {
return false;
}
+ public LoginResponse oauth2Authenticate(String userName, String password) {
+ String requestURL;
+
+ requestURL = configuration.dristhiBaseURL() + OPENSRP_AUTH_USER_URL_PATH;
+
+ LoginResponse loginResponse = httpAgent
+ .urlCanBeAccessWithGivenCredentials(requestURL, userName, password);
+
+ if (loginResponse != null && loginResponse.equals(LoginResponse.SUCCESS)) {
+ saveUserGroup(userName, password, loginResponse.payload());
+ }
+
+ return loginResponse;
+ }
+
+ public LoginResponse fetchUserDetails(String accessToken) {
+ String requestURL;
+
+ requestURL = configuration.dristhiBaseURL() + OPENSRP_AUTH_USER_URL_PATH;
+
+ LoginResponse loginResponse = httpAgent.fetchUserDetails(requestURL, accessToken);
+
+ return loginResponse;
+ }
+
public LoginResponse isValidRemoteLogin(String userName, String password) {
String requestURL;
@@ -337,7 +364,7 @@ private boolean loginWith(String userName, String password) {
username = allSharedPreferences.fetchRegisteredANM();
allSharedPreferences.updateANMUserName(username);
DrishtiApplication.getInstance().getRepository().getReadableDatabase();
- allSettings.registerANM(username, password);
+ allSettings.registerANM(username);
return loginSuccessful;
}
@@ -545,15 +572,19 @@ public void saveUserInfo(User user) {
* @param userInfo The user's info from the
* endpoint (should contain the {team}.{team}.{uuid} key)
*/
- public void saveUserGroup(String userName, String password, LoginResponseData userInfo) {
- String username = userInfo.user != null && StringUtils.isNotBlank(userInfo.user.getUsername())
- ? userInfo.user.getUsername() : userName;
+ public Bundle saveUserGroup(String userName, String password, LoginResponseData userInfo) {
+ Bundle bundle = new Bundle();
+
+ String username = userInfo.user != null && StringUtils.isNotBlank(userInfo.user.getUsername()) ? userInfo.user.getUsername() : userName;
+ bundle.putString(AccountHelper.INTENT_KEY.ACCOUNT_NAME, username);
+
+
if (keyStore != null && username != null) {
try {
KeyStore.PrivateKeyEntry privateKeyEntry = createUserKeyPair(username);
if (password == null) {
- return;
+ return null;
}
@@ -572,7 +603,10 @@ public void saveUserGroup(String userName, String password, LoginResponseData us
}
if (StringUtils.isBlank(groupId)) {
- return;
+ return null;
+ } else {
+
+ bundle.putString(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, groupId);
}
if (privateKeyEntry != null) {
@@ -593,6 +627,8 @@ public void saveUserGroup(String userName, String password, LoginResponseData us
Timber.e(e);
}
}
+
+ return bundle;
}
public boolean hasARegisteredUser() {
@@ -601,7 +637,7 @@ public boolean hasARegisteredUser() {
public void logout() {
logoutSession();
- allSettings.registerANM("", "");
+ allSettings.registerANM("");
allSettings.savePreviousFetchIndex("0");
DrishtiApplication.getInstance().getRepository().deleteRepository();
}
diff --git a/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java b/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java
index 4c8b49057..293a7cc93 100644
--- a/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java
@@ -103,8 +103,12 @@ private void getGlobalSettings(JSONArray settings) throws JSONException {
globalSettings = pullGlobalSettingsFromServer();
}
+<<<<<<< HEAD
aggregateSettings(settings, globalSettings);
}
+=======
+ Response resp = httpAgent.fetchWithCredentials(url);
+>>>>>>> Migrate user service
private void aggregateSettings(JSONArray settings, JSONArray globalSettings) throws JSONException {
if (!JsonFormUtils.isBlankJsonArray(globalSettings)) {
@@ -229,5 +233,6 @@ public String getPassword() {
public void setPassword(String password) {
this.password = password;
}
+
}
diff --git a/opensrp-app/src/main/java/org/smartregister/util/SyncUtils.java b/opensrp-app/src/main/java/org/smartregister/util/SyncUtils.java
index d0807c2fc..5a31f61c4 100644
--- a/opensrp-app/src/main/java/org/smartregister/util/SyncUtils.java
+++ b/opensrp-app/src/main/java/org/smartregister/util/SyncUtils.java
@@ -4,10 +4,8 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.util.Base64;
import org.apache.commons.lang3.StringUtils;
-import org.apache.http.HttpStatus;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -17,9 +15,6 @@
import org.smartregister.repository.AllSettings;
import org.smartregister.repository.BaseRepository;
-import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.net.URL;
import java.util.List;
import timber.log.Timber;
@@ -37,9 +32,6 @@
*/
public class SyncUtils {
-
- private static final String DETAILS_URL = "/user-details?anm-id=";
-
private org.smartregister.Context opensrpContent = CoreLibrary.getInstance().context();
private Context context;
@@ -49,33 +41,7 @@ public SyncUtils(Context context) {
}
public boolean verifyAuthorization() {
- String baseUrl = opensrpContent.configuration().dristhiBaseURL();
- if (baseUrl.endsWith("/")) {
- baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
- }
- final String username = opensrpContent.allSharedPreferences().fetchRegisteredANM();
- final String password = opensrpContent.allSettings().fetchANMPassword();
- baseUrl = baseUrl + DETAILS_URL + username;
- try {
- URL url = new URL(baseUrl);
- HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
- final String basicAuth = "Basic " + Base64.encodeToString((username + ":" + password).getBytes(), Base64.NO_WRAP);
- urlConnection.setRequestProperty("Authorization", basicAuth);
- int statusCode = urlConnection.getResponseCode();
- urlConnection.disconnect();
- if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
- Timber.i("User not authorized. User access was revoked, will log off user");
- return false;
- } else if (statusCode != HttpStatus.SC_OK) {
- Timber.w("Error occurred verifying authorization, User will not be logged off");
- } else {
- Timber.i("User is Authorized");
- }
-
- } catch (IOException e) {
- Timber.e(e);
- }
- return true;
+ return CoreLibrary.getInstance().context().getHttpAgent().verifyAuthorization();
}
public void logoutUser() {
@@ -152,7 +118,9 @@ private boolean isNewerSetting(Setting setting1, Setting setting2) {
}
private synchronized String getIncrementedServerVersion(Setting setting) {
- if (setting == null || StringUtils.isBlank(setting.getVersion())) { return null; }
+ if (setting == null || StringUtils.isBlank(setting.getVersion())) {
+ return null;
+ }
return String.valueOf(Long.valueOf(setting.getVersion()) + 1);
}
diff --git a/opensrp-app/src/main/java/org/smartregister/util/Utils.java b/opensrp-app/src/main/java/org/smartregister/util/Utils.java
index c354efe63..21dfce6a6 100644
--- a/opensrp-app/src/main/java/org/smartregister/util/Utils.java
+++ b/opensrp-app/src/main/java/org/smartregister/util/Utils.java
@@ -24,6 +24,7 @@
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
import android.graphics.Color;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
@@ -60,7 +61,9 @@
import org.joda.time.Years;
import org.smartregister.AllConstants;
import org.smartregister.CoreLibrary;
+import org.smartregister.R;
import org.smartregister.SyncFilter;
+import org.smartregister.account.AccountAuthenticatorXml;
import org.smartregister.commonregistry.CommonPersonObject;
import org.smartregister.commonregistry.CommonPersonObjectClient;
import org.smartregister.domain.FetchStatus;
@@ -867,13 +870,13 @@ protected static boolean deleteDbJournal(File databases, @NonNull String databas
public static String getAppId(@NonNull Context context) {
PackageInfo packageInfo = getPackageInfo(context);
- return packageInfo != null? packageInfo.packageName : null;
+ return packageInfo != null ? packageInfo.packageName : null;
}
@Nullable
public static String getAppVersion(@NonNull Context context) {
PackageInfo packageInfo = getPackageInfo(context);
- return packageInfo != null? packageInfo.versionName : null;
+ return packageInfo != null ? packageInfo.versionName : null;
}
@Nullable
@@ -906,4 +909,46 @@ public static int calculatePercentage(long totalCount, long partialCount){
}
}
+ protected static String safeArrayToString(char[] array) {
+
+ StringBuilder stringBuilder = new StringBuilder();
+ for (char c : array) {
+ stringBuilder.append(c);
+ }
+ return stringBuilder.toString();
+ }
+
+ public static AccountAuthenticatorXml parseAuthenticatorXMLConfigData(Context context) {
+ try {
+ int eventType = -1;
+ String namespace = "http://schemas.android.com/apk/res/android";
+ AccountAuthenticatorXml authenticatorXml = new AccountAuthenticatorXml();
+ XmlResourceParser parser = context.getResources().getXml(R.xml.authenticator);
+
+ while (eventType != XmlResourceParser.END_DOCUMENT) {
+ if (eventType == XmlResourceParser.START_TAG) {
+ String element = parser.getName();
+
+ if ("account-authenticator".equals(element)) {
+ //Account type id
+ String accountType = parser.getAttributeValue(namespace, "accountType");
+ authenticatorXml.setAccountType(accountType);
+
+ //Account Name
+ int labelId = parser.getAttributeResourceValue(namespace, "label", 0);
+ authenticatorXml.setAccountName(context.getResources().getString(labelId));
+
+ //Icon
+ int iconImageResourceId = parser.getAttributeResourceValue(namespace, "icon", 0);
+ authenticatorXml.setIcon(iconImageResourceId);
+ }
+ }
+ eventType = parser.next();
+ }
+ return authenticatorXml;
+ } catch (Exception e) {
+ Timber.e(e);
+ return null;
+ }
+ }
}
\ No newline at end of file
From 3d9c7755afe16bdbc90523489e990ea30d275276 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Sat, 9 May 2020 14:56:16 +0300
Subject: [PATCH 06/70] Implement login demo via sample app for oauth
---
sample/build.gradle | 8 ++
sample/src/main/AndroidManifest.xml | 12 ++-
.../sample/SampleLoginActivity.java | 45 +++++++++++
.../sample/application/SampleApplication.java | 11 +--
.../application/SampleSyncConfiguration.java | 75 +++++++++++++++++++
.../sample/interactor/LoginInteractor.java | 19 +++++
.../sample/presenter/LoginPresenter.java | 29 +++++++
sample/src/main/res/xml/authenticator.xml | 7 ++
sample/src/main/res/xml/preferences.xml | 9 +++
9 files changed, 205 insertions(+), 10 deletions(-)
create mode 100644 sample/src/main/java/org/smartregister/sample/SampleLoginActivity.java
create mode 100644 sample/src/main/java/org/smartregister/sample/application/SampleSyncConfiguration.java
create mode 100644 sample/src/main/java/org/smartregister/sample/interactor/LoginInteractor.java
create mode 100644 sample/src/main/java/org/smartregister/sample/presenter/LoginPresenter.java
create mode 100644 sample/src/main/res/xml/authenticator.xml
create mode 100644 sample/src/main/res/xml/preferences.xml
diff --git a/sample/build.gradle b/sample/build.gradle
index 55cf13478..b97f23124 100644
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -52,6 +52,14 @@ android {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
+
+ debug {
+
+ minifyEnabled false
+
+ buildConfigField "String", "OAUTH_CLIENT_ID", '"opensrp-trusted-client"'
+ buildConfigField "String", "OAUTH_CLIENT_SECRET", '"O@aTHS#cr3t"'
+ }
}
packagingOptions {
diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml
index 3100850d6..cdf071f18 100644
--- a/sample/src/main/AndroidManifest.xml
+++ b/sample/src/main/AndroidManifest.xml
@@ -16,15 +16,21 @@
android:theme="@style/AppTheme"
tools:replace="android:theme">
+ android:name=".SampleLoginActivity"
+ android:screenOrientation="fullSensor"
+ android:theme="@style/AppThemeNoTitle">
+
+
+
diff --git a/sample/src/main/java/org/smartregister/sample/SampleLoginActivity.java b/sample/src/main/java/org/smartregister/sample/SampleLoginActivity.java
new file mode 100644
index 000000000..89f4de02c
--- /dev/null
+++ b/sample/src/main/java/org/smartregister/sample/SampleLoginActivity.java
@@ -0,0 +1,45 @@
+package org.smartregister.sample;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.TextView;
+
+import org.smartregister.sample.presenter.LoginPresenter;
+import org.smartregister.view.activity.BaseLoginActivity;
+import org.smartregister.view.activity.NativeECSmartRegisterActivity;
+import org.smartregister.view.contract.BaseLoginContract;
+
+/**
+ * Created by ndegwamartin on 03/05/2020.
+ */
+public class SampleLoginActivity extends BaseLoginActivity implements BaseLoginContract.View {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ }
+
+ @Override
+ protected int getContentView() {
+ return R.layout.activity_login;
+ }
+
+ @Override
+ protected void initializePresenter() {
+ mLoginPresenter = new LoginPresenter(this);
+ }
+
+ @Override
+ public void goToHome(boolean isRemote) {
+ Intent navigateToRegister = new Intent(this, NativeECSmartRegisterActivity.class);
+ startActivity(navigateToRegister);
+ }
+
+}
diff --git a/sample/src/main/java/org/smartregister/sample/application/SampleApplication.java b/sample/src/main/java/org/smartregister/sample/application/SampleApplication.java
index a9e969b0c..8e9ba9e6a 100644
--- a/sample/src/main/java/org/smartregister/sample/application/SampleApplication.java
+++ b/sample/src/main/java/org/smartregister/sample/application/SampleApplication.java
@@ -1,5 +1,7 @@
package org.smartregister.sample.application;
+import org.smartregister.AllConstants;
+import org.smartregister.BuildConfig;
import org.smartregister.Context;
import org.smartregister.CoreLibrary;
import org.smartregister.view.activity.DrishtiApplication;
@@ -23,9 +25,9 @@ public void onCreate() {
context.updateApplicationContext(getApplicationContext());
//Initialize Modules
- CoreLibrary.init(context, null);
+ CoreLibrary.init(context, new SampleSyncConfiguration(), BuildConfig.BUILD_TIMESTAMP);
- getRepository().getReadableDatabase();
+ context.allSharedPreferences().savePreference(AllConstants.DRISHTI_BASE_URL, CoreLibrary.getInstance().context().getAppProperties().getProperty(AllConstants.DRISHTI_BASE_URL));
}
public static synchronized SampleApplication getInstance() {
@@ -38,9 +40,4 @@ public void logoutCurrentUser() {
}
- @Override
- public String getPassword() {
- return "sample-password";
- }
-
}
diff --git a/sample/src/main/java/org/smartregister/sample/application/SampleSyncConfiguration.java b/sample/src/main/java/org/smartregister/sample/application/SampleSyncConfiguration.java
new file mode 100644
index 000000000..205ab4b80
--- /dev/null
+++ b/sample/src/main/java/org/smartregister/sample/application/SampleSyncConfiguration.java
@@ -0,0 +1,75 @@
+package org.smartregister.sample.application;
+
+import org.smartregister.CoreLibrary;
+import org.smartregister.SyncConfiguration;
+import org.smartregister.SyncFilter;
+import org.smartregister.repository.AllSharedPreferences;
+import org.smartregister.sample.BuildConfig;
+
+import java.util.List;
+
+/**
+ * Created by ndegwamartin on 06/05/2020.
+ */
+public class SampleSyncConfiguration extends SyncConfiguration {
+ @Override
+ public int getSyncMaxRetries() {
+ return 0;
+ }
+
+ @Override
+ public SyncFilter getSyncFilterParam() {
+ return SyncFilter.LOCATION;
+ }
+
+ @Override
+ public String getSyncFilterValue() {
+ AllSharedPreferences sharedPreferences = CoreLibrary.getInstance().context().userService().getAllSharedPreferences();
+ return sharedPreferences.fetchDefaultLocalityId(sharedPreferences.fetchRegisteredANM());
+ }
+
+ @Override
+ public int getUniqueIdSource() {
+ return 0;
+ }
+
+ @Override
+ public int getUniqueIdBatchSize() {
+ return 0;
+ }
+
+ @Override
+ public int getUniqueIdInitialBatchSize() {
+ return 0;
+ }
+
+ @Override
+ public SyncFilter getEncryptionParam() {
+ return SyncFilter.TEAM;
+ }
+
+ @Override
+ public boolean updateClientDetailsTable() {
+ return false;
+ }
+
+ @Override
+ public List getSynchronizedLocationTags() {
+ return null;
+ }
+
+ @Override
+ public String getTopAllowedLocationLevel() {
+ return null;
+ }
+
+ @Override
+ public String getOauthClientId() {
+ return BuildConfig.OAUTH_CLIENT_ID;
+ }
+
+ @Override
+ public String getOauthClientSecret() {
+ return BuildConfig.OAUTH_CLIENT_SECRET;
+ }
+}
diff --git a/sample/src/main/java/org/smartregister/sample/interactor/LoginInteractor.java b/sample/src/main/java/org/smartregister/sample/interactor/LoginInteractor.java
new file mode 100644
index 000000000..3cea406d0
--- /dev/null
+++ b/sample/src/main/java/org/smartregister/sample/interactor/LoginInteractor.java
@@ -0,0 +1,19 @@
+package org.smartregister.sample.interactor;
+
+import org.smartregister.login.interactor.BaseLoginInteractor;
+import org.smartregister.view.contract.BaseLoginContract;
+
+/**
+ * Created by ndegwamartin on 08/05/2020.
+ */
+public class LoginInteractor extends BaseLoginInteractor {
+
+ public LoginInteractor(BaseLoginContract.Presenter loginPresenter) {
+ super(loginPresenter);
+ }
+
+ @Override
+ protected void scheduleJobsPeriodically() {
+ //Schedule your jobs here
+ }
+}
diff --git a/sample/src/main/java/org/smartregister/sample/presenter/LoginPresenter.java b/sample/src/main/java/org/smartregister/sample/presenter/LoginPresenter.java
new file mode 100644
index 000000000..1708c2383
--- /dev/null
+++ b/sample/src/main/java/org/smartregister/sample/presenter/LoginPresenter.java
@@ -0,0 +1,29 @@
+package org.smartregister.sample.presenter;
+
+import org.smartregister.login.model.BaseLoginModel;
+import org.smartregister.login.presenter.BaseLoginPresenter;
+import org.smartregister.sample.interactor.LoginInteractor;
+import org.smartregister.view.contract.BaseLoginContract;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Created by ndegwamartin on 08/05/2020.
+ */
+public class LoginPresenter extends BaseLoginPresenter {
+
+ public LoginPresenter(BaseLoginContract.View loginView) {
+ mLoginView = new WeakReference<>(loginView);
+ mLoginInteractor = new LoginInteractor(this);
+ mLoginModel = new BaseLoginModel();
+ }
+ @Override
+ public void processViewCustomizations() {
+ //Do nothing
+ }
+
+ @Override
+ public boolean isServerSettingsSet() {
+ return false;
+ }
+}
diff --git a/sample/src/main/res/xml/authenticator.xml b/sample/src/main/res/xml/authenticator.xml
new file mode 100644
index 000000000..f0e776cd2
--- /dev/null
+++ b/sample/src/main/res/xml/authenticator.xml
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/sample/src/main/res/xml/preferences.xml b/sample/src/main/res/xml/preferences.xml
new file mode 100644
index 000000000..9324fb624
--- /dev/null
+++ b/sample/src/main/res/xml/preferences.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
From e8382b2ab111640dddb25b969fc2e50ce9e08e2c Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Mon, 11 May 2020 19:00:10 +0300
Subject: [PATCH 07/70] Refactor HTTPAgent Fix failing unit tests
---
.../smartregister/repository/AllSettings.java | 9 +-
.../org/smartregister/service/HTTPAgent.java | 477 +++++++++++++++---
.../smartregister/TestSyncConfiguration.java | 10 +
.../repository/AllSettingsTest.java | 32 +-
.../smartregister/service/HTTPAgentTest.java | 74 ++-
.../service/UserServiceTest.java | 4 +-
.../fragment/BaseRegisterFragmentTest.java | 2 +
7 files changed, 503 insertions(+), 105 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/repository/AllSettings.java b/opensrp-app/src/main/java/org/smartregister/repository/AllSettings.java
index ebb697991..b063c8819 100644
--- a/opensrp-app/src/main/java/org/smartregister/repository/AllSettings.java
+++ b/opensrp-app/src/main/java/org/smartregister/repository/AllSettings.java
@@ -10,7 +10,6 @@ public class AllSettings {
public static final String APPLIED_VILLAGE_FILTER_SETTING_KEY = "appliedVillageFilter";
public static final String PREVIOUS_FETCH_INDEX_SETTING_KEY = "previousFetchIndex";
public static final String PREVIOUS_FORM_SYNC_INDEX_SETTING_KEY = "previousFormSyncIndex";
- private static final String ANM_PASSWORD_PREFERENCE_KEY = "anmPassword";
private static final String ANM_LOCATION = "anmLocation";
private static final String ANM_TEAM = "anmTeam";
private static final String USER_INFORMATION = "userInformation";
@@ -23,9 +22,8 @@ public AllSettings(AllSharedPreferences preferences, SettingsRepository settings
this.settingsRepository = settingsRepository;
}
- public void registerANM(String userName, String password) {
+ public void registerANM(String userName) {
preferences.updateANMUserName(userName);
- settingsRepository.updateSetting(ANM_PASSWORD_PREFERENCE_KEY, password);
}
public void savePreviousFetchIndex(String value) {
@@ -45,10 +43,6 @@ public String appliedVillageFilter(String defaultFilterValue) {
.querySetting(APPLIED_VILLAGE_FILTER_SETTING_KEY, defaultFilterValue);
}
- public String fetchANMPassword() {
- return settingsRepository.querySetting(ANM_PASSWORD_PREFERENCE_KEY, "");
- }
-
public String fetchPreviousFormSyncIndex() {
return settingsRepository.querySetting(PREVIOUS_FORM_SYNC_INDEX_SETTING_KEY, "0");
}
@@ -80,7 +74,6 @@ public String fetchUserInformation() {
public Map getAuthParams() {
Map authParams = new HashMap();
authParams.put("username", preferences.fetchRegisteredANM());
- authParams.put("password", fetchANMPassword());
return authParams;
}
diff --git a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
index c2f446ac3..251e3f1c5 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
@@ -1,13 +1,26 @@
package org.smartregister.service;
+import android.accounts.Account;
+import android.accounts.AccountManager;
import android.content.Context;
+import android.support.annotation.NonNull;
import android.util.Base64;
+import android.util.Log;
+
+import com.google.common.io.BaseEncoding;
+import com.google.gson.Gson;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
+import org.apache.http.util.ByteArrayBuffer;
import org.smartregister.AllConstants;
+import org.smartregister.CoreLibrary;
import org.smartregister.DristhiConfiguration;
+import org.smartregister.account.AccountAuthenticatorXml;
+import org.smartregister.account.AccountError;
+import org.smartregister.account.AccountHelper;
+import org.smartregister.account.AccountResponse;
import org.smartregister.compression.GZIPCompression;
import org.smartregister.domain.DownloadStatus;
import org.smartregister.domain.LoginResponse;
@@ -19,13 +32,15 @@
import org.smartregister.repository.AllSettings;
import org.smartregister.repository.AllSharedPreferences;
import org.smartregister.ssl.OpensrpSSLHelper;
-import org.smartregister.util.DownloadForm;
+import org.smartregister.util.SyncUtils;
import org.smartregister.util.Utils;
+import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -79,24 +94,21 @@ public class HTTPAgent {
private int connectTimeout = 60000;
private int readTimeout = 60000;
+ private static final String DETAILS_URL = "/user-details?anm-id=";
+
+ private SyncUtils syncUtils;
+
public HTTPAgent(Context context, AllSettings settings, AllSharedPreferences
allSharedPreferences, DristhiConfiguration configuration) {
this.context = context;
this.settings = settings;
this.allSharedPreferences = allSharedPreferences;
this.configuration = configuration;
- gzipCompression= new GZIPCompression();
+ gzipCompression = new GZIPCompression();
+ syncUtils = new SyncUtils(context.getApplicationContext());
}
- /**
- * @author Rodgers Andati
- * @since 2019-04-25
- * This method initializes httpurlconnection
- * @param requestURLPath This is the url to be open http connection to.
- * @param useBasicAuth This is whether to set up basic authentication or not.
- * @return HttpURLConnection This returns the http connection to opensrp server.
- */
- private HttpURLConnection initializeHttp(String requestURLPath, boolean useBasicAuth) throws IOException {
+ private HttpURLConnection initializeHttp(String requestURLPath) throws IOException {
URL url = new URL(requestURLPath);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
if (urlConnection instanceof HttpsURLConnection) {
@@ -105,19 +117,18 @@ private HttpURLConnection initializeHttp(String requestURLPath, boolean useBasic
}
urlConnection.setConnectTimeout(getConnectTimeout());
urlConnection.setReadTimeout(getReadTimeout());
+ AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
+ if (AccountHelper.getOauthAccountByType(authenticatorXml.getAccountType()) != null)
+ urlConnection.setRequestProperty("Authorization", new StringBuilder("Bearer ").append(AccountHelper.getOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).toString());
- if(useBasicAuth) {
- final String basicAuth = "Basic " + Base64.encodeToString((allSharedPreferences.fetchRegisteredANM() +
- ":" + settings.fetchANMPassword()).getBytes(), Base64.NO_WRAP);
- urlConnection.setRequestProperty("Authorization", basicAuth);
- }
return urlConnection;
}
public Response fetch(String requestURLPath) {
- HttpURLConnection urlConnection;
try {
- urlConnection = initializeHttp(requestURLPath, true);
+
+ HttpURLConnection urlConnection = httpURLConnectionTries(requestURLPath);
+
return handleResponse(urlConnection);
} catch (IOException ex) {
@@ -126,15 +137,39 @@ public Response fetch(String requestURLPath) {
}
}
+ @NonNull
+ private HttpURLConnection httpURLConnectionTries(String requestURLPath) throws IOException {
+
+ int authRetries = 0;
+ HttpURLConnection urlConnection = null;
+
+ while (authRetries < CoreLibrary.getInstance().getSyncConfiguration().getMaxAuthenticationRetries() + 1) {
+
+ authRetries++;
+
+ urlConnection = initializeHttp(requestURLPath);
+
+ if (urlConnection.getResponseCode() == HttpStatus.SC_UNAUTHORIZED) {
+
+ refreshAuthenticationToken(AccountHelper.getAccountManagerValue(AccountHelper.KEY_REFRESH_TOKEN, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType()));
+ }
+
+ }
+ return urlConnection;
+ }
+
public Response post(String postURLPath, String jsonPayload) {
HttpURLConnection urlConnection;
+ AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
try {
- urlConnection = initializeHttp(postURLPath, true);
+ urlConnection = initializeHttp(postURLPath);
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
urlConnection.setRequestProperty("Content-Encoding", "gzip");
+ urlConnection.setRequestProperty("Authorization", new StringBuilder("Bearer ").append(AccountHelper.getOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).toString());
+
OutputStream os = urlConnection.getOutputStream();
BufferedOutputStream writer = new BufferedOutputStream(os);
@@ -148,7 +183,7 @@ public Response post(String postURLPath, String jsonPayload) {
return handleResponse(urlConnection);
} catch (IOException ex) {
- Timber.e(ex, "EXCEPTION: %s", ex.toString());
+ Timber.e(ex, "EXCEPTION: %s", ex.toString());
return new Response<>(ResponseStatus.failure, null);
}
}
@@ -159,7 +194,7 @@ public Response postWithJsonResponse(String postURLPath, String jsonPayl
}
private void logResponse(String postURLPath, String jsonPayload) {
- Timber.d("postURLPath: %s and jsonPayLoad: %s", postURLPath,jsonPayload);
+ Timber.d("postURLPath: %s and jsonPayLoad: %s", postURLPath, jsonPayload);
}
public LoginResponse urlCanBeAccessWithGivenCredentials(String requestURL, String userName, String password) {
@@ -168,7 +203,7 @@ public LoginResponse urlCanBeAccessWithGivenCredentials(String requestURL, Strin
String url = null;
try {
url = requestURL.replaceAll("\\s+", "");
- urlConnection = initializeHttp(url, false);
+ urlConnection = initializeHttp(url);
final String basicAuth = "Basic " + Base64.encodeToString((userName + ":" + password).getBytes(), Base64.NO_WRAP);
urlConnection.setRequestProperty("Authorization", basicAuth);
@@ -200,14 +235,14 @@ public LoginResponse urlCanBeAccessWithGivenCredentials(String requestURL, Strin
loginResponse = UNKNOWN_RESPONSE;
}
} catch (MalformedURLException e) {
- Timber.e(e, "Failed to check credentials bad url %s", url);
+ Timber.e(e, "Failed to check credentials bad url %s", url);
loginResponse = MALFORMED_URL;
} catch (SocketTimeoutException e) {
- Timber.e(e,"SocketTimeoutException when authenticating %s", userName);
+ Timber.e(e, "SocketTimeoutException when authenticating %s", userName);
loginResponse = TIMEOUT;
- Timber.e(e,"Failed to check credentials of: %s using %s . Error: %s", userName, url, e.toString());
+ Timber.e(e, "Failed to check credentials of: %s using %s . Error: %s", userName, url, e.toString());
} catch (IOException e) {
- Timber.e(e,"Failed to check credentials of: %s using %s . Error: %s", userName, url, e.toString());
+ Timber.e(e, "Failed to check credentials of: %s using %s . Error: %s", userName, url, e.toString());
loginResponse = NO_INTERNET_CONNECTIVITY;
} finally {
if (urlConnection != null) {
@@ -218,41 +253,40 @@ public LoginResponse urlCanBeAccessWithGivenCredentials(String requestURL, Strin
}
public DownloadStatus downloadFromUrl(String url, String filename) {
- Response status = DownloadForm.downloadFromURL(url, filename,
- allSharedPreferences.fetchRegisteredANM(), settings.fetchANMPassword());
- Timber.d("downloading file name : %s and url %s", filename,url);
+
+ AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
+ Response status = downloadFromURL(url, filename, new StringBuilder("Bearer ").append(AccountHelper.getOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).toString());
+ Timber.d("downloading file name : %s and url %s", filename, url);
return status.payload();
}
- public Response fetchWithCredentials(String requestURL, String username, String password) {
- HttpURLConnection urlConnection = null;
+ public Response fetchWithCredentials(String requestURL) {
+
try {
- urlConnection = initializeHttp(requestURL, false);
- setCustomCredentials(urlConnection, username, password);
+ HttpURLConnection urlConnection = httpURLConnectionTries(requestURL);
return handleResponse(urlConnection);
} catch (IOException ex) {
- Timber.e(ex, "EXCEPTION %s", ex.toString());
+ Timber.e(ex, "EXCEPTION %s", ex.toString());
return new Response<>(ResponseStatus.failure, null);
}
}
- private void setCustomCredentials(HttpURLConnection urlConnection, String username, String password) {
- final String basicAuth = "Basic " + Base64.encodeToString((username + ":" + password).getBytes(),
- Base64.NO_WRAP);
- urlConnection.setRequestProperty("Authorization", basicAuth);
- }
-
private Response handleResponse(HttpURLConnection urlConnection) {
String responseString;
String totalRecords;
try {
int statusCode = urlConnection.getResponseCode();
- InputStream inputStream;
- if (statusCode >= HttpStatus.SC_BAD_REQUEST)
+ InputStream inputStream = null;
+
+ if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
+
+ syncUtils.logoutUser();
+
+ } else if (statusCode >= HttpStatus.SC_BAD_REQUEST)
inputStream = urlConnection.getErrorStream();
else
inputStream = urlConnection.getInputStream();
@@ -264,48 +298,51 @@ private Response handleResponse(HttpURLConnection urlConnection) {
Timber.d("response string: %s using url %s", responseString, urlConnection.getURL());
} catch (MalformedURLException e) {
- Timber.e(e, "%s %s", MALFORMED_URL, e.toString());
+ Timber.e(e, "%s %s", MALFORMED_URL, e.toString());
ResponseStatus.failure.setDisplayValue(ResponseErrorStatus.malformed_url.name());
return new Response<>(ResponseStatus.failure, null);
} catch (SocketTimeoutException e) {
- Timber.e(e, "%s %s", TIMEOUT, e.toString());
+ Timber.e(e, "%s %s", TIMEOUT, e.toString());
ResponseStatus.failure.setDisplayValue(ResponseErrorStatus.timeout.name());
return new Response<>(ResponseStatus.failure, null);
} catch (IOException e) {
- Timber.e(e, "%s %s", NO_INTERNET_CONNECTIVITY, e.toString());
+ Timber.e(e, "%s %s", NO_INTERNET_CONNECTIVITY, e.toString());
return new Response<>(ResponseStatus.failure, null);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
- return new Response<>(ResponseStatus.success, responseString).withTotalRecords(Utils.tryParseLong(totalRecords,0));
+ return new Response<>(ResponseStatus.success, responseString).withTotalRecords(Utils.tryParseLong(totalRecords, 0));
}
/**
- * @author Rodgers Andati
- * @since 2019-04-25
- * This method uploads an image to opensrp server. Migration from the old method that used httpclient
* @param urlString This is the url of the image, TAG,
- * @param image This is the image to be uploaded to opensrp server.
+ * @param image This is the image to be uploaded to opensrp server.
* @return String This returns the response obtained from the opensrp server.
+ * @author Rodgers Andati
+ * @since 2019-04-25
+ * This method uploads an image to opensrp server. Migration from the old method that used httpclient
*/
public String httpImagePost(String urlString, ProfileImage image) {
OutputStream outputStream;
PrintWriter writer;
String responseString = "";
+ AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
try {
- HttpURLConnection httpUrlConnection = initializeHttp(urlString, true);
+ HttpURLConnection httpUrlConnection = initializeHttp(urlString);
httpUrlConnection.setUseCaches(false);
httpUrlConnection.setDoInput(true);
httpUrlConnection.setDoOutput(true);
httpUrlConnection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
+ httpUrlConnection.setRequestProperty("Authorization", new StringBuilder("Bearer ").append(AccountHelper.getOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).toString());
+
outputStream = httpUrlConnection.getOutputStream();
- writer = new PrintWriter(new OutputStreamWriter(outputStream, "UTF-8"),true);
+ writer = new PrintWriter(new OutputStreamWriter(outputStream, "UTF-8"), true);
// attach image
attachImage(writer, image, outputStream);
@@ -346,7 +383,7 @@ public String httpImagePost(String urlString, ProfileImage image) {
} catch (ProtocolException e) {
Timber.e(e, "Protocol exception %s", e.toString());
} catch (SocketTimeoutException e) {
- Timber.e(e, "SocketTimeout %s %s", TIMEOUT, e.toString());
+ Timber.e(e, "SocketTimeout %s %s", TIMEOUT, e.toString());
} catch (MalformedURLException e) {
Timber.e(e, "MalformedUrl %s %s", MALFORMED_URL, e.toString());
} catch (IOException e) {
@@ -381,13 +418,13 @@ private void attachImage(PrintWriter writer, ProfileImage image, OutputStream ou
private void addParameter(PrintWriter writer, String paramName, String paramValue) {
writer.append(twoHyphens + boundary).append(crlf);
- writer.append("Content-Disposition: form-data; name=\""+ paramName +"\"").append(crlf);
+ writer.append("Content-Disposition: form-data; name=\"" + paramName + "\"").append(crlf);
writer.append("Content-Type: text/plain; charset=" + "UTF-8").append(crlf);
writer.append(crlf);
writer.append(paramValue).append(crlf);
writer.flush();
- Timber.d("http agent param name: %s and param value %s ", paramName,paramValue);
+ Timber.d("http agent param name: %s and param value %s ", paramName, paramValue);
}
@@ -463,7 +500,7 @@ public int getConnectTimeout() {
/**
* Sets the connection timeout in milliseconds
- *
+ *
* Setting this will call {@link java.net.HttpURLConnection#setConnectTimeout(int)}
* on the {@link java.net.HttpURLConnection} instance in {@link org.smartregister.service.HTTPAgent}
*/
@@ -473,11 +510,335 @@ public void setConnectTimeout(int connectTimeout) {
/**
* Sets the read timeout in milliseconds
- *
+ *
* Setting this will call {@link java.net.HttpURLConnection#setReadTimeout(int)}
* on the {@link java.net.HttpURLConnection} instance in {@link org.smartregister.service.HTTPAgent}
*/
public void setReadTimeout(int readTimeout) {
this.readTimeout = readTimeout;
}
-}
+
+ public AccountResponse oauth2authenticate(String username, String password, String grantType) {
+
+ AccountError accountError = null;
+ HttpURLConnection urlConnection = null;
+ String url = null;
+ String baseURL = configuration.dristhiBaseURL() + AccountHelper.OAUTH.TOKEN_ENDPOINT;
+ String requestURL = baseURL + "?&grant_type=" + grantType + "&username=" + username + "&password=" + password;
+ try {
+ url = requestURL.replaceAll("\\s+", "");
+ urlConnection = initializeHttp(url);
+
+ String clientId = CoreLibrary.getInstance().getSyncConfiguration().getOauthClientId();
+ String clientSecret = CoreLibrary.getInstance().getSyncConfiguration().getOauthClientSecret();
+
+ final String base64Auth = BaseEncoding.base64().encode(new String(clientId + ":" + clientSecret).getBytes());
+
+ urlConnection.setRequestMethod("POST");
+ urlConnection.addRequestProperty("client_id", clientId);
+ urlConnection.addRequestProperty("client_secret", clientSecret);
+ urlConnection.setRequestProperty("Authorization", "Basic " + base64Auth);
+
+
+ int statusCode = urlConnection.getResponseCode();
+ InputStream inputStream;
+ if (statusCode >= HttpStatus.SC_BAD_REQUEST)
+ inputStream = urlConnection.getErrorStream();
+ else
+ inputStream = urlConnection.getInputStream();
+ String responseString = IOUtils.toString(inputStream);
+ if (statusCode == HttpStatus.SC_OK) {
+
+ Timber.d("response String: %s using request url %s", responseString, url);
+
+ AccountResponse accountResponse = new Gson().fromJson(responseString, AccountResponse.class);
+ return accountResponse;
+
+ } else {
+
+ accountError = new Gson().fromJson(responseString, AccountError.class);
+ return new AccountResponse(statusCode, accountError);
+
+ }
+ } catch (MalformedURLException e) {
+ Timber.e(e, "Failed to check credentials bad url %s", url);
+ accountError = new AccountError(0, MALFORMED_URL.name());
+
+ } catch (SocketTimeoutException e) {
+ Timber.e(e, "SocketTimeoutException when authenticating %s", username);
+
+ accountError = new AccountError(0, TIMEOUT.name());
+
+ Timber.e(e, "Failed to check credentials of: %s using %s . Error: %s", username, url, e.toString());
+ } catch (IOException e) {
+ Timber.e(e, "Failed to check credentials of: %s using %s . Error: %s", username, url, e.toString());
+ accountError = new AccountError(0, NO_INTERNET_CONNECTIVITY.name());
+
+ } finally {
+ if (urlConnection != null) {
+ urlConnection.disconnect();
+ }
+
+ }
+
+ //If we got here there was an issue with no server status code
+ return new AccountResponse(0, accountError);
+
+ }
+
+ public LoginResponse fetchUserDetails(String requestURL, String oauthAccessToken) {
+ LoginResponse loginResponse = null;
+ String url = null;
+ HttpURLConnection urlConnection = null;
+ try {
+ url = requestURL.replaceAll("\\s+", "");
+
+ urlConnection = httpURLConnectionTries(url);
+
+ int statusCode = urlConnection.getResponseCode();
+
+ InputStream inputStream;
+ if (statusCode >= HttpStatus.SC_BAD_REQUEST)
+ inputStream = urlConnection.getErrorStream();
+ else
+ inputStream = urlConnection.getInputStream();
+ String responseString = IOUtils.toString(inputStream);
+ if (statusCode == HttpStatus.SC_OK) {
+
+ Timber.d("response String: %s using request url %s", responseString, url);
+ LoginResponseData responseData = getResponseBody(responseString);
+ loginResponse = retrieveResponse(responseData);
+ } else if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
+ Timber.e("Invalid credentials for: %s using token %s", oauthAccessToken, url);
+ loginResponse = UNAUTHORIZED;
+ } else if (StringUtils.isNotBlank(responseString)) {
+ //extract message string from the default tomcat server response which is usually between
message and
+ responseString = StringUtils.substringBetween(responseString, "message", "
");
+ if (StringUtils.isNotBlank(responseString)) {
+ //remove the underline tag from the responseString
+ responseString = responseString.replace("", "").trim();
+ loginResponse = CUSTOM_SERVER_RESPONSE.withMessage(responseString);
+ }
+ } else {
+ Timber.e("Bad response from Server. Status code: %s using %s ", statusCode, url);
+ loginResponse = UNKNOWN_RESPONSE;
+ }
+ } catch (MalformedURLException e) {
+ Timber.e(e, "Failed to check credentials bad url %s", url);
+ loginResponse = MALFORMED_URL;
+ } catch (SocketTimeoutException e) {
+ Timber.e(e, "SocketTimeoutException when authenticating");
+ loginResponse = TIMEOUT;
+ } catch (IOException e) {
+ Timber.e(e, "Failed to connect to %s check, check internet connection. Error: %s", url, e.toString());
+ loginResponse = NO_INTERNET_CONNECTIVITY;
+ } finally {
+ if (urlConnection != null) {
+ urlConnection.disconnect();
+ }
+ }
+ return loginResponse;
+ }
+
+ /**
+ * @param downloadURL_ This is the url of the image
+ * @param fileName This is how the image should be name after it has been downloaded.
+ * @param accessToken This is the access token used to authenticate when accessing the url endpoint.
+ * @return Response This returns whether the download succeeded or failed.
+ */
+ public Response downloadFromURL(String downloadURL_, String fileName, String accessToken) {
+ HttpURLConnection httpUrlConnection;
+ try {
+ File dir = new File(FormPathService.sdcardPathDownload);
+
+ if (!dir.exists()) {
+ dir.mkdirs();
+ }
+
+ File file = new File(dir, fileName);
+
+ long startTime = System.currentTimeMillis();
+ Log.d("DownloadFormService", "download begin");
+ Log.d("DownloadFormService", "download url: " + downloadURL_);
+ Log.d("DownloadFormService", "download file name: " + fileName);
+
+
+ String downloadURL = downloadURL_.replaceAll("\\s+", "");
+
+ /* Open connection to URL */
+ URL url = new URL(downloadURL);
+
+ httpUrlConnection = (HttpURLConnection) url.openConnection();
+
+ if (httpUrlConnection instanceof HttpsURLConnection) {
+ OpensrpSSLHelper opensrpSSLHelper = new OpensrpSSLHelper(context, configuration);
+ ((HttpsURLConnection) httpUrlConnection).setSSLSocketFactory(opensrpSSLHelper.getSSLSocketFactory());
+ }
+
+ httpUrlConnection.setRequestProperty("Authorization", accessToken);
+
+ httpUrlConnection.connect();
+
+ int status = httpUrlConnection.getResponseCode();
+ if (status == HttpURLConnection.HTTP_OK) {
+
+ InputStream inputStream = httpUrlConnection.getInputStream();
+ BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
+
+ long fileLength = bufferedInputStream.available();
+ if (fileLength == 0) {
+ return new Response(ResponseStatus.success,
+ DownloadStatus.nothingDownloaded);
+ }
+ Log.d("DownloadFormService", "file length : " + fileLength);
+
+ ByteArrayBuffer baf = new ByteArrayBuffer(9999);
+ int current = 0;
+ while ((current = bufferedInputStream.read()) != -1) {
+ baf.append((byte) current);
+ }
+
+ /* Convert the bytes to String */
+ FileOutputStream fos = new FileOutputStream(file);
+ fos.write(baf.toByteArray());
+ fos.flush();
+ fos.close();
+
+ Log.d("DownloadFormService",
+ "download finished in " + ((System.currentTimeMillis() - startTime) / 1000)
+ + " sec");
+ httpUrlConnection.disconnect();
+
+ } else {
+ Log.d("RESPONSE", "Server returned non-OK status: " + status);
+ return new Response(ResponseStatus.failure, DownloadStatus.failedDownloaded);
+ }
+
+ } catch (IOException e) {
+ Log.d("DownloadFormService", "download error : " + e);
+ return new Response(ResponseStatus.success, DownloadStatus.failedDownloaded);
+ }
+
+ return new Response(ResponseStatus.success, DownloadStatus.downloaded);
+ }
+
+ public boolean verifyAuthorization() {
+ String baseUrl = configuration.dristhiBaseURL();
+
+ if (baseUrl.endsWith("/")) {
+ baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
+ }
+ final String username = allSharedPreferences.fetchRegisteredANM();
+ baseUrl = baseUrl + DETAILS_URL + username;
+ try {
+ URL url = new URL(baseUrl);
+ HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
+
+ AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
+ if (AccountHelper.getOauthAccountByType(authenticatorXml.getAccountType()) != null)
+ urlConnection.setRequestProperty("Authorization", new StringBuilder("Bearer ").append(AccountHelper.getOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).toString());
+ int statusCode = urlConnection.getResponseCode();
+ urlConnection.disconnect();
+ if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
+ Timber.i("User not authorized. User access was revoked, will log off user");
+ return false;
+ } else if (statusCode != HttpStatus.SC_OK) {
+ Timber.w("Error occurred verifying authorization, User will not be logged off");
+ } else {
+ Timber.i("User is Authorized");
+ }
+
+ } catch (IOException e) {
+ Timber.e(e);
+ }
+ return true;
+ }
+
+ public AccountResponse oauth2authenticateRefreshToken(String refreshToken) {
+
+ AccountError accountError = null;
+ HttpURLConnection urlConnection = null;
+ String url = null;
+
+ String clientId = CoreLibrary.getInstance().getSyncConfiguration().getOauthClientId();
+ String clientSecret = CoreLibrary.getInstance().getSyncConfiguration().getOauthClientSecret();
+
+ String baseURL = configuration.dristhiBaseURL() + AccountHelper.OAUTH.TOKEN_ENDPOINT;
+ String requestURL = baseURL + "?&grant_type=" + AccountHelper.OAUTH.GRANT_TYPE.REFRESH_TOKEN + "&refresh_token=" + refreshToken + "&client_id=" + clientId;
+ try {
+ url = requestURL.replaceAll("\\s+", "");
+ urlConnection = initializeHttp(url);
+
+
+ final String base64Auth = BaseEncoding.base64().encode(new String(clientId + ":" + clientSecret).getBytes());
+
+ urlConnection.setRequestMethod("POST");
+ urlConnection.addRequestProperty("client_id", clientId);
+ urlConnection.addRequestProperty("client_secret", clientSecret);
+ urlConnection.setRequestProperty("Authorization", "Bearer " + base64Auth);
+
+
+ int statusCode = urlConnection.getResponseCode();
+ InputStream inputStream;
+ if (statusCode >= HttpStatus.SC_BAD_REQUEST)
+ inputStream = urlConnection.getErrorStream();
+ else
+ inputStream = urlConnection.getInputStream();
+ String responseString = IOUtils.toString(inputStream);
+ if (statusCode == HttpStatus.SC_OK) {
+
+ Timber.d("response String: %s using request url %s", responseString, url);
+
+ AccountResponse accountResponse = new Gson().fromJson(responseString, AccountResponse.class);
+ return accountResponse;
+
+ } else {
+
+ accountError = new Gson().fromJson(responseString, AccountError.class);
+ return new AccountResponse(statusCode, accountError);
+
+ }
+ } catch (MalformedURLException e) {
+ Timber.e(e, "Failed to check credentials bad url %s", url);
+ accountError = new AccountError(0, MALFORMED_URL.name());
+
+ } catch (SocketTimeoutException e) {
+ Timber.e(e, "SocketTimeoutException when authenticating %s", refreshToken);
+
+ accountError = new AccountError(0, TIMEOUT.name());
+
+ Timber.e(e, "Failed to check credentials of: %s using %s . Error: %s", refreshToken, url, e.toString());
+ } catch (IOException e) {
+ Timber.e(e, "Failed to check credentials of: %s using %s . Error: %s", refreshToken, url, e.toString());
+ accountError = new AccountError(0, NO_INTERNET_CONNECTIVITY.name());
+
+ } finally {
+ if (urlConnection != null) {
+ urlConnection.disconnect();
+ }
+
+ }
+
+ //If we got here there was an issue with no server status code
+ return new AccountResponse(0, accountError);
+
+ }
+
+ private String refreshAuthenticationToken(String refreshToken) {
+
+ AccountResponse response = CoreLibrary.getInstance().context().getHttpAgent().oauth2authenticateRefreshToken(refreshToken);
+
+ AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
+ AccountManager accountManager = CoreLibrary.getInstance().getAccountManager();
+
+
+ Account account = AccountHelper.getOauthAccountByType(authenticatorXml.getAccountType());
+ String authToken = accountManager.peekAuthToken(account, AccountHelper.TOKEN_TYPE.PROVIDER);
+
+ accountManager.setAuthToken(account, AccountHelper.TOKEN_TYPE.PROVIDER, response.getAccessToken());
+ accountManager.setPassword(account, response.getAccessToken());
+ accountManager.setUserData(account, AccountHelper.KEY_REFRESH_TOKEN, response.getRefreshToken());
+
+ return response.getAccessToken();
+ }
+}
\ No newline at end of file
diff --git a/opensrp-app/src/test/java/org/smartregister/TestSyncConfiguration.java b/opensrp-app/src/test/java/org/smartregister/TestSyncConfiguration.java
index 1ff906ecd..675cbc9ff 100644
--- a/opensrp-app/src/test/java/org/smartregister/TestSyncConfiguration.java
+++ b/opensrp-app/src/test/java/org/smartregister/TestSyncConfiguration.java
@@ -55,4 +55,14 @@ public List getSynchronizedLocationTags() {
public String getTopAllowedLocationLevel() {
return null;
}
+
+ @Override
+ public String getOauthClientId() {
+ return "opensrp-client-id";
+ }
+
+ @Override
+ public String getOauthClientSecret() {
+ return "$om3cl13nt$3cret";
+ }
}
diff --git a/opensrp-app/src/test/java/org/smartregister/repository/AllSettingsTest.java b/opensrp-app/src/test/java/org/smartregister/repository/AllSettingsTest.java
index 37e327cde..c3bca5f8a 100644
--- a/opensrp-app/src/test/java/org/smartregister/repository/AllSettingsTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/repository/AllSettingsTest.java
@@ -1,7 +1,6 @@
package org.smartregister.repository;
import org.junit.Assert;
-
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -35,16 +34,6 @@ public void setUp() throws Exception {
allSettings = new AllSettings(allSharedPreferences, settingsRepository);
}
- @Test
- public void shouldFetchANMPassword() throws Exception {
- Mockito.when(settingsRepository.querySetting("anmPassword", "")).thenReturn("actual password");
-
- String actual = allSettings.fetchANMPassword();
-
- Mockito.verify(settingsRepository).querySetting("anmPassword", "");
- Assert.assertEquals("actual password", actual);
- }
-
@Test
public void shouldSavePreviousFetchIndex() throws Exception {
allSettings.savePreviousFetchIndex("1234");
@@ -96,10 +85,8 @@ public void shouldGetAppliedVillageFilter() throws Exception {
@Test
public void assertRegisterANMCallsPreferenceAndRepositoryUpdate() throws Exception {
Mockito.doNothing().when(allSharedPreferences).updateANMUserName(Mockito.anyString());
- Mockito.doNothing().doNothing().when(settingsRepository).updateSetting(Mockito.anyString(), Mockito.anyString());
- allSettings.registerANM("", "");
+ allSettings.registerANM("");
Mockito.verify(allSharedPreferences, Mockito.times(1)).updateANMUserName(Mockito.anyString());
- Mockito.verify(settingsRepository, Mockito.times(1)).updateSetting(Mockito.anyString(), Mockito.anyString());
}
@Test
@@ -133,10 +120,8 @@ public void assertSaveUserInformationCallsRepositoryUpdate() {
@Test
public void assertGetAuthParamsReturnsUserNamePassword() {
Mockito.when(allSharedPreferences.fetchRegisteredANM()).thenReturn("username");
- Mockito.when(settingsRepository.querySetting(Mockito.anyString(), Mockito.anyString())).thenReturn("password");
Map auth = allSettings.getAuthParams();
Assert.assertEquals("username", auth.get("username"));
- Assert.assertEquals("password", auth.get("password"));
}
@Test
@@ -177,7 +162,8 @@ public void testGet() {
@Test
public void testGetSetting() {
Setting s = new Setting();
- s.setKey("testKey"); s.setValue("testValue");
+ s.setKey("testKey");
+ s.setValue("testValue");
Mockito.when(settingsRepository.querySetting("testKey")).thenReturn(s);
@@ -193,10 +179,12 @@ public void testGetSettingsByType() {
List ls = new ArrayList<>();
Setting s = new Setting();
- s.setKey("testKey"); s.setValue("testValue");
+ s.setKey("testKey");
+ s.setValue("testValue");
ls.add(s);
Setting s2 = new Setting();
- s2.setKey("testKey2"); s2.setValue("testValue2");
+ s2.setKey("testKey2");
+ s2.setValue("testValue2");
ls.add(s2);
Mockito.when(settingsRepository.querySettingsByType("testType")).thenReturn(ls);
@@ -211,7 +199,8 @@ public void testGetSettingsByType() {
@Test
public void testPutSetting() {
Setting s = new Setting();
- s.setKey("testKey"); s.setValue("testValue");
+ s.setKey("testKey");
+ s.setValue("testValue");
allSettings.putSetting(s);
@@ -223,7 +212,8 @@ public void testGetUnsyncedSettings() {
List ls = new ArrayList<>();
Setting s = new Setting();
- s.setKey("testUnsyncedKey"); s.setValue("testUnsyncedValue");
+ s.setKey("testUnsyncedKey");
+ s.setValue("testUnsyncedValue");
ls.add(s);
Mockito.when(settingsRepository.queryUnsyncedSettings()).thenReturn(ls);
diff --git a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
index 7018abce6..8a9022051 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
@@ -1,6 +1,7 @@
package org.smartregister.service;
-import android.content.Context;
+import android.accounts.Account;
+import android.accounts.AccountManager;
import android.util.Base64;
import org.json.JSONObject;
@@ -12,11 +13,17 @@
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
+import org.smartregister.Context;
+import org.smartregister.CoreLibrary;
import org.smartregister.DristhiConfiguration;
+import org.smartregister.SyncConfiguration;
+import org.smartregister.account.AccountAuthenticatorXml;
+import org.smartregister.account.AccountHelper;
import org.smartregister.domain.LoginResponse;
import org.smartregister.domain.ProfileImage;
import org.smartregister.domain.Response;
@@ -29,10 +36,13 @@
import java.util.HashMap;
@RunWith(PowerMockRunner.class)
-@PrepareForTest({Base64.class, File.class, FileInputStream.class})
+@PrepareForTest({Base64.class, File.class, FileInputStream.class, Context.class, AccountHelper.class, CoreLibrary.class})
public class HTTPAgentTest {
@Mock
- private Context context;
+ private android.content.Context context;
+
+ @Mock
+ private Context openSrpContext;
@Mock
private AllSettings allSettings;
@@ -46,6 +56,21 @@ public class HTTPAgentTest {
@Mock
private ProfileImage profileImage;
+ @Mock
+ private AccountAuthenticatorXml accountAuthenticatorXml;
+
+ @Mock
+ private Account account;
+
+ @Mock
+ private CoreLibrary coreLibrary;
+
+ @Mock
+ private AccountManager accountManager;
+
+ @Mock
+ private SyncConfiguration syncConfiguration;
+
@Rule
private TemporaryFolder folder = new TemporaryFolder();
@@ -54,24 +79,41 @@ public class HTTPAgentTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+
+ PowerMockito.mockStatic(Context.class);
+ PowerMockito.when(Context.getInstance()).thenReturn(openSrpContext);
+ Mockito.doReturn(context).when(context).getApplicationContext();
+
+ PowerMockito.mockStatic(CoreLibrary.class);
+ PowerMockito.when(CoreLibrary.getInstance()).thenReturn(coreLibrary);
+ Mockito.doReturn(accountManager).when(coreLibrary).getAccountManager();
+ Mockito.doReturn(accountAuthenticatorXml).when(coreLibrary).getAccountAuthenticatorXml();
+
+ Mockito.doReturn(accountManager).when(coreLibrary).getAccountManager();
+ Mockito.doReturn(syncConfiguration).when(coreLibrary).getSyncConfiguration();
+ Mockito.doReturn(1).when(syncConfiguration).getMaxAuthenticationRetries();
+
+ PowerMockito.mockStatic(AccountHelper.class);
+ PowerMockito.when(AccountHelper.getOauthAccountByType(accountAuthenticatorXml.getAccountType())).thenReturn(account);
+
httpAgent = new HTTPAgent(context, allSettings, allSharedPreferences, dristhiConfiguration);
}
@Test
- public void testFetchFailsGivenWrongUrl(){
+ public void testFetchFailsGivenWrongUrl() {
Response resp = httpAgent.fetch("wrong.url");
Assert.assertEquals(ResponseStatus.failure, resp.status());
}
@Test
- public void testFetchPassesGivenCorrectUrl(){
+ public void testFetchPassesGivenCorrectUrl() {
PowerMockito.mockStatic(Base64.class);
Response resp = httpAgent.fetch("https://google.com");
Assert.assertEquals(ResponseStatus.success, resp.status());
}
@Test
- public void testPostFailsGivenWrongUrl(){
+ public void testPostFailsGivenWrongUrl() {
HashMap map = new HashMap<>();
map.put("title", "OpenSRP Testing Tuesdays");
JSONObject jObject = new JSONObject(map);
@@ -80,7 +122,7 @@ public void testPostFailsGivenWrongUrl(){
}
@Test
- public void testPostPassesGivenCorrectUrl(){
+ public void testPostPassesGivenCorrectUrl() {
PowerMockito.mockStatic(Base64.class);
HashMap map = new HashMap<>();
map.put("title", "OpenSRP Testing Tuesdays");
@@ -90,14 +132,14 @@ public void testPostPassesGivenCorrectUrl(){
}
@Test
- public void testUrlCanBeAccessWithGivenCredentials(){
+ public void testUrlCanBeAccessWithGivenCredentials() {
PowerMockito.mockStatic(Base64.class);
LoginResponse resp = httpAgent.urlCanBeAccessWithGivenCredentials("http://www.mocky.io/v2/5e54de89310000d559eb33d9", "", "");
Assert.assertEquals(LoginResponse.SUCCESS.message(), resp.message());
}
@Test
- public void testUrlCanBeAccessWithGivenCredentialsGivenWrongUrl(){
+ public void testUrlCanBeAccessWithGivenCredentialsGivenWrongUrl() {
PowerMockito.mockStatic(Base64.class);
LoginResponse resp = httpAgent.urlCanBeAccessWithGivenCredentials("wrong.url", "", "");
Assert.assertEquals(LoginResponse.MALFORMED_URL.message(), resp.message());
@@ -105,27 +147,27 @@ public void testUrlCanBeAccessWithGivenCredentialsGivenWrongUrl(){
@Test
@Ignore
- public void testUrlCanBeAccessWithGivenCredentialsGivenEmptyResp(){
+ public void testUrlCanBeAccessWithGivenCredentialsGivenEmptyResp() {
PowerMockito.mockStatic(Base64.class);
LoginResponse resp = httpAgent.urlCanBeAccessWithGivenCredentials("http://mockbin.org/bin/e42f7256-18b2-40b9-a20c-40fdc564d06f", "", "");
Assert.assertEquals(LoginResponse.SUCCESS_WITH_EMPTY_RESPONSE.message(), resp.message());
}
@Test
- public void testfetchWithCredentialsFailsGivenWrongUrl(){
- Response resp = httpAgent.fetchWithCredentials("wrong.url", "", "");
+ public void testfetchWithCredentialsFailsGivenWrongUrl() {
+ Response resp = httpAgent.fetchWithCredentials("wrong.url");
Assert.assertEquals(ResponseStatus.failure, resp.status());
}
@Test
- public void testfetchWithCredentialsPassesGivenCorrectUrl(){
+ public void testfetchWithCredentialsPassesGivenCorrectUrl() {
PowerMockito.mockStatic(Base64.class);
- Response resp = httpAgent.fetchWithCredentials("https://google.com", "", "");
+ Response resp = httpAgent.fetchWithCredentials("https://google.com");
Assert.assertEquals(ResponseStatus.success, resp.status());
}
@Test
- public void testHttpImagePostGivenWrongUrl(){
+ public void testHttpImagePostGivenWrongUrl() {
String resp = httpAgent.httpImagePost("wrong.url", profileImage);
Assert.assertEquals("", resp);
}
@@ -144,7 +186,7 @@ public void testHttpImagePostTimeout() {
}
@Test
- public void testPostWithJsonResponse(){
+ public void testPostWithJsonResponse() {
PowerMockito.mockStatic(Base64.class);
HashMap map = new HashMap<>();
map.put("title", "OpenSRP Testing Tuesdays");
diff --git a/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java b/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java
index 63387dd84..315fdf46b 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java
@@ -211,7 +211,7 @@ public void logoutCurrentUser() {
userService.remoteLogin("user X", "password Y", userInfo);
- verify(allSettings).registerANM("user X", "password Y");
+ verify(allSettings).registerANM("user X");
verify(session).setPassword("password Y");
}
@@ -227,7 +227,7 @@ public void shouldDeleteDataAndSettingsWhenLogoutHappens() throws Exception {
verify(repository).deleteRepository();
verify(repository).deleteRepository();
verify(allSettings).savePreviousFetchIndex("0");
- verify(allSettings).registerANM("", "");
+ verify(allSettings).registerANM("");
}
@Test
diff --git a/opensrp-app/src/test/java/org/smartregister/view/fragment/BaseRegisterFragmentTest.java b/opensrp-app/src/test/java/org/smartregister/view/fragment/BaseRegisterFragmentTest.java
index ac9d4affb..61eff77ad 100644
--- a/opensrp-app/src/test/java/org/smartregister/view/fragment/BaseRegisterFragmentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/view/fragment/BaseRegisterFragmentTest.java
@@ -223,6 +223,7 @@ public void setSearchTermInitsCorrectValue() {
}
@Test
+ @Ignore
public void assertOnQRCodeSucessfullyScannedInvokesFilterWithCorrectParams() {
String OPENSRP_ID = "8232-372-8L";
@@ -251,6 +252,7 @@ public void assertOnQRCodeSucessfullyScannedInvokesFilterWithCorrectParams() {
}
@Test
+ @Ignore
public void assertOnQRCodeSucessfullyScannedInvokessetUniqueIDWithCorrectParams() {
String OPENSRP_ID = "8232-372-8L";
From 721420632c4e2d097074b390ea117706214f8f98 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Tue, 12 May 2020 21:19:21 +0300
Subject: [PATCH 08/70] Code clean up
---
opensrp-app/src/main/java/org/smartregister/Context.java | 3 +--
.../org/smartregister/account/AccountAuthenticator.java | 3 +--
.../main/java/org/smartregister/account/AccountError.java | 8 ++++++++
.../main/java/org/smartregister/service/HTTPAgent.java | 7 +------
.../sync/helper/SyncSettingsServiceHelper.java | 2 --
.../smartregister/view/activity/BaseLoginActivity.java | 7 ++++---
.../org/smartregister/view/activity/LoginActivity.java | 1 -
.../java/org/smartregister/service/HTTPAgentTest.java | 2 +-
8 files changed, 16 insertions(+), 17 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/Context.java b/opensrp-app/src/main/java/org/smartregister/Context.java
index 866c3499f..2ea9f16c9 100755
--- a/opensrp-app/src/main/java/org/smartregister/Context.java
+++ b/opensrp-app/src/main/java/org/smartregister/Context.java
@@ -520,8 +520,7 @@ public FormSubmissionSyncService formSubmissionSyncService() {
public HTTPAgent httpAgent() {
if (httpAgent == null) {
- httpAgent = new HTTPAgent(applicationContext, allSettings(), allSharedPreferences(),
- configuration());
+ httpAgent = new HTTPAgent(applicationContext, allSharedPreferences(), configuration());
}
return httpAgent;
}
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java b/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
index 48d8158ac..abacdba00 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
@@ -19,7 +19,6 @@
*/
public class AccountAuthenticator extends AbstractAccountAuthenticator {
- private String TAG = AccountAuthenticator.class.getCanonicalName();
private final Context mContext;
public AccountAuthenticator(Context context) {
@@ -117,6 +116,6 @@ public Bundle updateCredentials(AccountAuthenticatorResponse response, Account a
@Override
public String getRefreshToken() {
- return AccountHelper.getAccountManagerValue(AccountHelper.KEY_REFRESH_TOKEN,CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType());
+ return AccountHelper.getAccountManagerValue(AccountHelper.KEY_REFRESH_TOKEN, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType());
}
}
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountError.java b/opensrp-app/src/main/java/org/smartregister/account/AccountError.java
index 87cefcd8a..6b972cbfa 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountError.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountError.java
@@ -15,4 +15,12 @@ public AccountError(int statusCode, String error) {
this.error = error;
}
+
+ public int getStatusCode() {
+ return statusCode;
+ }
+
+ public String getError() {
+ return error;
+ }
}
diff --git a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
index 251e3f1c5..3ddf4cc0e 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
@@ -29,7 +29,6 @@
import org.smartregister.domain.ResponseErrorStatus;
import org.smartregister.domain.ResponseStatus;
import org.smartregister.domain.jsonmapping.LoginResponseData;
-import org.smartregister.repository.AllSettings;
import org.smartregister.repository.AllSharedPreferences;
import org.smartregister.ssl.OpensrpSSLHelper;
import org.smartregister.util.SyncUtils;
@@ -82,7 +81,6 @@
public class HTTPAgent {
private Context context;
- private AllSettings settings;
private AllSharedPreferences allSharedPreferences;
private DristhiConfiguration configuration;
private GZIPCompression gzipCompression;
@@ -98,10 +96,9 @@ public class HTTPAgent {
private SyncUtils syncUtils;
- public HTTPAgent(Context context, AllSettings settings, AllSharedPreferences
+ public HTTPAgent(Context context, AllSharedPreferences
allSharedPreferences, DristhiConfiguration configuration) {
this.context = context;
- this.settings = settings;
this.allSharedPreferences = allSharedPreferences;
this.configuration = configuration;
gzipCompression = new GZIPCompression();
@@ -831,9 +828,7 @@ private String refreshAuthenticationToken(String refreshToken) {
AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
AccountManager accountManager = CoreLibrary.getInstance().getAccountManager();
-
Account account = AccountHelper.getOauthAccountByType(authenticatorXml.getAccountType());
- String authToken = accountManager.peekAuthToken(account, AccountHelper.TOKEN_TYPE.PROVIDER);
accountManager.setAuthToken(account, AccountHelper.TOKEN_TYPE.PROVIDER, response.getAccessToken());
accountManager.setPassword(account, response.getAccessToken());
diff --git a/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java b/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java
index 293a7cc93..a3f5b51fd 100644
--- a/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java
@@ -28,8 +28,6 @@ public class SyncSettingsServiceHelper {
private HTTPAgent httpAgent;
private String baseUrl;
- private String username;
- private String password;
private AllSharedPreferences sharedPreferences;
public SyncSettingsServiceHelper(String baseUrl, HTTPAgent httpAgent) {
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java b/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java
index a2754f541..43b6cc123 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java
@@ -64,6 +64,9 @@ protected void onCreate(Bundle savedInstanceState) {
mLoginPresenter.setLanguage();
setupViews(mLoginPresenter);
syncUtils = new SyncUtils(this);
+
+ authTokenType = getIntent().getStringExtra(AccountHelper.INTENT_KEY.AUTH_TYPE);
+
}
@Override
@@ -292,15 +295,13 @@ public void showClearDataDialog(@NonNull DialogInterface.OnClickListener onClick
public String getAuthTokenType() {
- authTokenType = getIntent().getStringExtra(AccountHelper.INTENT_KEY.AUTH_TYPE);
-
if (authTokenType == null)
authTokenType = AccountHelper.TOKEN_TYPE.PROVIDER;
return authTokenType;
}
- public boolean isNewAccount(){
+ public boolean isNewAccount() {
return getIntent().getBooleanExtra(AccountHelper.INTENT_KEY.IS_NEW_ACCOUNT, false);
}
}
\ No newline at end of file
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java b/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java
index a676faae7..a18656057 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java
@@ -1,6 +1,5 @@
package org.smartregister.view.activity;
-import android.accounts.AccountAuthenticatorActivity;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
diff --git a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
index 8a9022051..c54f82a8f 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
@@ -96,7 +96,7 @@ public void setUp() {
PowerMockito.mockStatic(AccountHelper.class);
PowerMockito.when(AccountHelper.getOauthAccountByType(accountAuthenticatorXml.getAccountType())).thenReturn(account);
- httpAgent = new HTTPAgent(context, allSettings, allSharedPreferences, dristhiConfiguration);
+ httpAgent = new HTTPAgent(context, allSharedPreferences, dristhiConfiguration);
}
@Test
From 75290cf10f76b59957c48a8ae4f4486ddcbb90fd Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Thu, 14 May 2020 22:19:19 +0300
Subject: [PATCH 09/70] Refactor authentication mechanism to work for Keycloak
---
.../account/AccountAuthenticator.java | 2 +-
.../account/AccountConfiguration.java | 39 ++++
.../smartregister/account/AccountHelper.java | 10 +
.../account/AccountResponse.java | 10 +-
.../login/task/RemoteLoginTask.java | 64 ++++--
.../org/smartregister/service/HTTPAgent.java | 207 ++++++++++++++----
.../view/activity/DrishtiApplication.java | 6 +-
7 files changed, 266 insertions(+), 72 deletions(-)
create mode 100644 opensrp-app/src/main/java/org/smartregister/account/AccountConfiguration.java
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java b/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
index abacdba00..229bd8449 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
@@ -59,7 +59,7 @@ public Bundle getAuthToken(AccountAuthenticatorResponse response, Account accoun
Timber.d("Authenticate with saved credentials");
- AccountResponse accountResponse = CoreLibrary.getInstance().context().getHttpAgent().oauth2authenticate(account.name, password, AccountHelper.OAUTH.GRANT_TYPE.PASSWORD);
+ AccountResponse accountResponse = CoreLibrary.getInstance().context().getHttpAgent().oauth2authenticate(account.name, password, AccountHelper.OAUTH.GRANT_TYPE.PASSWORD, CoreLibrary.getInstance().context().allSettings().get(AccountHelper.CONFIGURATION_CONSTANTS.TOKEN_ENDPOINT_URL));
authToken = accountResponse.getAccessToken();
refreshToken = accountResponse.getRefreshToken();
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountConfiguration.java b/opensrp-app/src/main/java/org/smartregister/account/AccountConfiguration.java
new file mode 100644
index 000000000..72aeabfad
--- /dev/null
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountConfiguration.java
@@ -0,0 +1,39 @@
+package org.smartregister.account;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.util.List;
+
+/**
+ * Created by ndegwamartin on 14/05/2020.
+ */
+public class AccountConfiguration {
+
+ @SerializedName("issuer")
+ private String issuerEndpoint;
+
+ @SerializedName("authorization_endpoint")
+ private String authorizationEndpoint;
+
+ @SerializedName("token_endpoint")
+ private String tokenEndpoint;
+
+ @SerializedName("grant_types_supported")
+ private List grantTypesSupported;
+
+ public String getIssuerEndpoint() {
+ return issuerEndpoint;
+ }
+
+ public String getAuthorizationEndpoint() {
+ return authorizationEndpoint;
+ }
+
+ public String getTokenEndpoint() {
+ return tokenEndpoint;
+ }
+
+ public List getGrantTypesSupported() {
+ return grantTypesSupported;
+ }
+}
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
index df0dda4bb..de97ca9c3 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
@@ -15,7 +15,17 @@ public class AccountHelper {
public final static String KEY_REFRESH_TOKEN = "KEY_REFRESH_TOKEN";
public final static int MAX_AUTH_RETRIES = 1;
+
+ public static final class CONFIGURATION_CONSTANTS {
+
+ public final static String TOKEN_ENDPOINT_URL = "token_endpoint_url";
+ public final static String AUTHORIZATION_ENDPOINT_URL = "authorization_endpoint_url";
+ public final static String ISSUER_ENDPOINT_URL = "issuer_endpoint_url";
+ }
+
public static final class OAUTH {
+
+ public final static String ACCOUNT_CONFIGURATION_ENDPOINT = "/rest/config/keycloak";
public final static String TOKEN_ENDPOINT = "/oauth/token";
public static final class GRANT_TYPE {
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountResponse.java b/opensrp-app/src/main/java/org/smartregister/account/AccountResponse.java
index 0ece15cfc..9c7759cb9 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountResponse.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountResponse.java
@@ -16,9 +16,11 @@ public class AccountResponse {
@SerializedName("refresh_token")
private String refreshToken;
+ @SerializedName("refresh_expires_in")
+ private Integer refreshExpiresIn;
@SerializedName("expires_in")
- private Integer ExpiresIn;
+ private Integer expiresIn;
@SerializedName("scope")
private String Scope;
@@ -53,10 +55,14 @@ public String getRefreshToken() {
}
public Integer getExpiresIn() {
- return ExpiresIn;
+ return expiresIn;
}
public String getScope() {
return Scope;
}
+
+ public Integer getRefreshExpiresIn() {
+ return refreshExpiresIn;
+ }
}
diff --git a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
index 4dc1580ea..a23b1855f 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
@@ -2,6 +2,8 @@
import android.accounts.Account;
import android.accounts.AccountManager;
+import android.accounts.AccountsException;
+import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -13,6 +15,7 @@
import org.smartregister.CoreLibrary;
import org.smartregister.R;
import org.smartregister.account.AccountAuthenticatorXml;
+import org.smartregister.account.AccountConfiguration;
import org.smartregister.account.AccountHelper;
import org.smartregister.account.AccountResponse;
import org.smartregister.domain.LoginResponse;
@@ -58,41 +61,60 @@ protected LoginResponse doInBackground(Void... params) {
Bundle userData = null;
try {
- AccountResponse response = CoreLibrary.getInstance().context().getHttpAgent().oauth2authenticate(mUsername, mPassword, AccountHelper.OAUTH.GRANT_TYPE.PASSWORD);
+ AccountConfiguration accountConfiguration = CoreLibrary.getInstance().context().getHttpAgent().fetchOAuthConfiguration();
- AccountManager mAccountManager = CoreLibrary.getInstance().getAccountManager();
+ if (accountConfiguration != null) {
- final Account account = new Account(mUsername, mAccountAuthenticatorXml.getAccountType());
+ if (!accountConfiguration.getGrantTypesSupported().contains(AccountHelper.OAUTH.GRANT_TYPE.PASSWORD))
+ throw new AccountsException("OAuth configuration DOES NOT support the Password Grant Type");
- loginResponse = getOpenSRPContext().userService().fetchUserDetails(response.getAccessToken());
+ //Persist config resources
+ SharedPreferences.Editor sharedPrefEditor = CoreLibrary.getInstance().context().allSharedPreferences().getPreferences().edit();
- if (loginResponse != null && loginResponse.equals(LoginResponse.SUCCESS)) {
+ sharedPrefEditor.putString(AccountHelper.CONFIGURATION_CONSTANTS.TOKEN_ENDPOINT_URL, accountConfiguration.getTokenEndpoint());
+ sharedPrefEditor.putString(AccountHelper.CONFIGURATION_CONSTANTS.AUTHORIZATION_ENDPOINT_URL, accountConfiguration.getAuthorizationEndpoint());
+ sharedPrefEditor.putString(AccountHelper.CONFIGURATION_CONSTANTS.ISSUER_ENDPOINT_URL, accountConfiguration.getIssuerEndpoint());
+ sharedPrefEditor.apply();
- userData = getOpenSRPContext().userService().saveUserGroup(mUsername, mPassword, loginResponse.payload());
+ AccountResponse response = CoreLibrary.getInstance().context().getHttpAgent().oauth2authenticate(mUsername, mPassword, AccountHelper.OAUTH.GRANT_TYPE.PASSWORD, accountConfiguration.getTokenEndpoint());
- if (getOpenSRPContext().userService().getGroupId(mUsername) != null && CoreLibrary.getInstance().getSyncConfiguration().isSyncSettings()) {
+ AccountManager mAccountManager = CoreLibrary.getInstance().getAccountManager();
+ final Account account = new Account(mUsername, mAccountAuthenticatorXml.getAccountType());
- publishProgress(R.string.loading_client_settings);
+ loginResponse = getOpenSRPContext().userService().fetchUserDetails(response.getAccessToken());
+ if (loginResponse != null && loginResponse.equals(LoginResponse.SUCCESS)) {
- SyncSettingsServiceHelper syncSettingsServiceHelper = new SyncSettingsServiceHelper(getOpenSRPContext().configuration().dristhiBaseURL(), getOpenSRPContext().getHttpAgent());
- try {
- JSONArray settings = pullSetting(syncSettingsServiceHelper, loginResponse);
- JSONObject data = new JSONObject();
- data.put(AllConstants.PREF_KEY.SETTINGS, settings);
- loginResponse.setRawData(data);
- } catch (JSONException e) {
- Timber.e(e);
- }
+ userData = getOpenSRPContext().userService().saveUserGroup(mUsername, mPassword, loginResponse.payload());
+
+ if (getOpenSRPContext().userService().getGroupId(mUsername) != null && CoreLibrary.getInstance().getSyncConfiguration().isSyncSettings()) {
+
+ publishProgress(R.string.loading_client_settings);
+
+ SyncSettingsServiceHelper syncSettingsServiceHelper = new SyncSettingsServiceHelper(getOpenSRPContext().configuration().dristhiBaseURL(), getOpenSRPContext().getHttpAgent());
+
+ try {
+ JSONArray settings = syncSettingsServiceHelper.pullSettingsFromServer(Utils.getFilterValue(loginResponse, CoreLibrary.getInstance().getSyncConfiguration().getSyncFilterParam()));
+ JSONObject prefSettingsData = new JSONObject();
+ prefSettingsData.put(AllConstants.PREF_KEY.SETTINGS, settings);
+ loginResponse.setRawData(prefSettingsData);
+
+ } catch (JSONException e) {
+ Timber.e(e);
+ }
+
+ }
}
- }
- mAccountManager.addAccountExplicitly(account, response.getAccessToken(), userData);
- mAccountManager.setAuthToken(account, mLoginView.getAuthTokenType(), response.getAccessToken());
- mAccountManager.setUserData(account, AccountHelper.KEY_REFRESH_TOKEN, response.getRefreshToken());
+ mAccountManager.addAccountExplicitly(account, response.getAccessToken(), userData);
+ mAccountManager.setAuthToken(account, mLoginView.getAuthTokenType(), response.getAccessToken());
+ mAccountManager.setUserData(account, AccountHelper.KEY_REFRESH_TOKEN, response.getRefreshToken());
+ } else {
+ throw new AccountsException("Could not fetch OAuth Configuration");
+ }
} catch (Exception e) {
diff --git a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
index 3ddf4cc0e..eae885525 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
@@ -11,6 +11,7 @@
import com.google.gson.Gson;
import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.CharEncoding;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.util.ByteArrayBuffer;
@@ -18,6 +19,7 @@
import org.smartregister.CoreLibrary;
import org.smartregister.DristhiConfiguration;
import org.smartregister.account.AccountAuthenticatorXml;
+import org.smartregister.account.AccountConfiguration;
import org.smartregister.account.AccountError;
import org.smartregister.account.AccountHelper;
import org.smartregister.account.AccountResponse;
@@ -37,6 +39,7 @@
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
+import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -91,6 +94,7 @@ public class HTTPAgent {
private int connectTimeout = 60000;
private int readTimeout = 60000;
+ private Gson gson;
private static final String DETAILS_URL = "/user-details?anm-id=";
@@ -101,6 +105,7 @@ public HTTPAgent(Context context, AllSharedPreferences
this.context = context;
this.allSharedPreferences = allSharedPreferences;
this.configuration = configuration;
+ gson = new Gson();
gzipCompression = new GZIPCompression();
syncUtils = new SyncUtils(context.getApplicationContext());
}
@@ -149,6 +154,9 @@ private HttpURLConnection httpURLConnectionTries(String requestURLPath) throws I
if (urlConnection.getResponseCode() == HttpStatus.SC_UNAUTHORIZED) {
refreshAuthenticationToken(AccountHelper.getAccountManagerValue(AccountHelper.KEY_REFRESH_TOKEN, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType()));
+
+ } else {
+ break;
}
}
@@ -242,9 +250,7 @@ public LoginResponse urlCanBeAccessWithGivenCredentials(String requestURL, Strin
Timber.e(e, "Failed to check credentials of: %s using %s . Error: %s", userName, url, e.toString());
loginResponse = NO_INTERNET_CONNECTIVITY;
} finally {
- if (urlConnection != null) {
- urlConnection.disconnect();
- }
+ closeConnection(urlConnection);
}
return loginResponse;
}
@@ -306,9 +312,7 @@ private Response handleResponse(HttpURLConnection urlConnection) {
Timber.e(e, "%s %s", NO_INTERNET_CONNECTIVITY, e.toString());
return new Response<>(ResponseStatus.failure, null);
} finally {
- if (urlConnection != null) {
- urlConnection.disconnect();
- }
+ closeConnection(urlConnection);
}
return new Response<>(ResponseStatus.success, responseString).withTotalRecords(Utils.tryParseLong(totalRecords, 0));
}
@@ -327,9 +331,11 @@ public String httpImagePost(String urlString, ProfileImage image) {
PrintWriter writer;
String responseString = "";
AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
+ HttpURLConnection httpUrlConnection = null;
try {
- HttpURLConnection httpUrlConnection = initializeHttp(urlString);
+
+ httpUrlConnection = initializeHttp(urlString);
httpUrlConnection.setUseCaches(false);
httpUrlConnection.setDoInput(true);
@@ -375,7 +381,6 @@ public String httpImagePost(String urlString, ProfileImage image) {
}
reader.close();
}
- httpUrlConnection.disconnect();
} catch (ProtocolException e) {
Timber.e(e, "Protocol exception %s", e.toString());
@@ -385,6 +390,9 @@ public String httpImagePost(String urlString, ProfileImage image) {
Timber.e(e, "MalformedUrl %s %s", MALFORMED_URL, e.toString());
} catch (IOException e) {
Timber.e(e, "IOException %s %s", NO_INTERNET_CONNECTIVITY, e.toString());
+ } finally {
+
+ closeConnection(httpUrlConnection);
}
return responseString;
}
@@ -515,27 +523,45 @@ public void setReadTimeout(int readTimeout) {
this.readTimeout = readTimeout;
}
- public AccountResponse oauth2authenticate(String username, String password, String grantType) {
+ public AccountResponse oauth2authenticate(String username, String password, String grantType, String tokenEndpointURL) {
AccountError accountError = null;
HttpURLConnection urlConnection = null;
String url = null;
- String baseURL = configuration.dristhiBaseURL() + AccountHelper.OAUTH.TOKEN_ENDPOINT;
- String requestURL = baseURL + "?&grant_type=" + grantType + "&username=" + username + "&password=" + password;
+ OutputStream outputStream = null;
+ BufferedOutputStream writer = null;
+
try {
- url = requestURL.replaceAll("\\s+", "");
- urlConnection = initializeHttp(url);
+ urlConnection = initializeHttp(tokenEndpointURL);
String clientId = CoreLibrary.getInstance().getSyncConfiguration().getOauthClientId();
String clientSecret = CoreLibrary.getInstance().getSyncConfiguration().getOauthClientSecret();
final String base64Auth = BaseEncoding.base64().encode(new String(clientId + ":" + clientSecret).getBytes());
+ StringBuilder requestParamBuilder = new StringBuilder();
+ requestParamBuilder.append("&grant_type=").append(grantType);
+ requestParamBuilder.append("&username=").append(username);
+ requestParamBuilder.append("&password=").append(password);
+ requestParamBuilder.append("&client_id=").append(clientId);
+ requestParamBuilder.append("&client_secret=").append(clientSecret);
+
+ byte[] postData = requestParamBuilder.toString().getBytes(CharEncoding.UTF_8);
+ int postDataLength = postData.length;
+
+ urlConnection.setDoOutput(true);
+ urlConnection.setInstanceFollowRedirects(false);
urlConnection.setRequestMethod("POST");
- urlConnection.addRequestProperty("client_id", clientId);
- urlConnection.addRequestProperty("client_secret", clientSecret);
+ urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+ urlConnection.setRequestProperty("charset", "utf-8");
+ urlConnection.setRequestProperty("Content-Length", Integer.toString(postDataLength));
urlConnection.setRequestProperty("Authorization", "Basic " + base64Auth);
+ urlConnection.setUseCaches(false);
+ outputStream = urlConnection.getOutputStream();
+ writer = new BufferedOutputStream(outputStream);
+ writer.write(postData);
+ writer.flush();
int statusCode = urlConnection.getResponseCode();
InputStream inputStream;
@@ -548,12 +574,12 @@ public AccountResponse oauth2authenticate(String username, String password, Stri
Timber.d("response String: %s using request url %s", responseString, url);
- AccountResponse accountResponse = new Gson().fromJson(responseString, AccountResponse.class);
+ AccountResponse accountResponse = gson.fromJson(responseString, AccountResponse.class);
return accountResponse;
} else {
- accountError = new Gson().fromJson(responseString, AccountError.class);
+ accountError = gson.fromJson(responseString, AccountError.class);
return new AccountResponse(statusCode, accountError);
}
@@ -572,9 +598,10 @@ public AccountResponse oauth2authenticate(String username, String password, Stri
accountError = new AccountError(0, NO_INTERNET_CONNECTIVITY.name());
} finally {
- if (urlConnection != null) {
- urlConnection.disconnect();
- }
+
+ closeConnection(urlConnection);
+ closeIOStream(writer);
+ closeIOStream(outputStream);
}
@@ -630,9 +657,8 @@ public LoginResponse fetchUserDetails(String requestURL, String oauthAccessToken
Timber.e(e, "Failed to connect to %s check, check internet connection. Error: %s", url, e.toString());
loginResponse = NO_INTERNET_CONNECTIVITY;
} finally {
- if (urlConnection != null) {
- urlConnection.disconnect();
- }
+
+ closeConnection(urlConnection);
}
return loginResponse;
}
@@ -644,7 +670,8 @@ public LoginResponse fetchUserDetails(String requestURL, String oauthAccessToken
* @return Response This returns whether the download succeeded or failed.
*/
public Response downloadFromURL(String downloadURL_, String fileName, String accessToken) {
- HttpURLConnection httpUrlConnection;
+
+ HttpURLConnection httpUrlConnection = null;
try {
File dir = new File(FormPathService.sdcardPathDownload);
@@ -704,7 +731,6 @@ public Response downloadFromURL(String downloadURL_, String file
Log.d("DownloadFormService",
"download finished in " + ((System.currentTimeMillis() - startTime) / 1000)
+ " sec");
- httpUrlConnection.disconnect();
} else {
Log.d("RESPONSE", "Server returned non-OK status: " + status);
@@ -714,6 +740,9 @@ public Response downloadFromURL(String downloadURL_, String file
} catch (IOException e) {
Log.d("DownloadFormService", "download error : " + e);
return new Response(ResponseStatus.success, DownloadStatus.failedDownloaded);
+ } finally {
+
+ closeConnection(httpUrlConnection);
}
return new Response(ResponseStatus.success, DownloadStatus.downloaded);
@@ -727,15 +756,18 @@ public boolean verifyAuthorization() {
}
final String username = allSharedPreferences.fetchRegisteredANM();
baseUrl = baseUrl + DETAILS_URL + username;
+
+ HttpURLConnection urlConnection = null;
+
try {
URL url = new URL(baseUrl);
- HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
+ urlConnection = (HttpURLConnection) url.openConnection();
AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
if (AccountHelper.getOauthAccountByType(authenticatorXml.getAccountType()) != null)
urlConnection.setRequestProperty("Authorization", new StringBuilder("Bearer ").append(AccountHelper.getOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).toString());
int statusCode = urlConnection.getResponseCode();
- urlConnection.disconnect();
+
if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
Timber.i("User not authorized. User access was revoked, will log off user");
return false;
@@ -747,6 +779,9 @@ public boolean verifyAuthorization() {
} catch (IOException e) {
Timber.e(e);
+ } finally {
+
+ closeConnection(urlConnection);
}
return true;
}
@@ -755,28 +790,44 @@ public AccountResponse oauth2authenticateRefreshToken(String refreshToken) {
AccountError accountError = null;
HttpURLConnection urlConnection = null;
- String url = null;
+ OutputStream outputStream = null;
+ BufferedOutputStream writer = null;
+ InputStream inputStream = null;
String clientId = CoreLibrary.getInstance().getSyncConfiguration().getOauthClientId();
String clientSecret = CoreLibrary.getInstance().getSyncConfiguration().getOauthClientSecret();
-
- String baseURL = configuration.dristhiBaseURL() + AccountHelper.OAUTH.TOKEN_ENDPOINT;
- String requestURL = baseURL + "?&grant_type=" + AccountHelper.OAUTH.GRANT_TYPE.REFRESH_TOKEN + "&refresh_token=" + refreshToken + "&client_id=" + clientId;
+ String tokenEndpointURL = null;
try {
- url = requestURL.replaceAll("\\s+", "");
- urlConnection = initializeHttp(url);
+ tokenEndpointURL = CoreLibrary.getInstance().context().allSharedPreferences().getPreferences().getString(AccountHelper.CONFIGURATION_CONSTANTS.TOKEN_ENDPOINT_URL, "");
+ urlConnection = initializeHttp(tokenEndpointURL);
final String base64Auth = BaseEncoding.base64().encode(new String(clientId + ":" + clientSecret).getBytes());
+ StringBuilder requestParamBuilder = new StringBuilder();
+ requestParamBuilder.append("&grant_type=").append(AccountHelper.OAUTH.GRANT_TYPE.REFRESH_TOKEN);
+ requestParamBuilder.append("&refresh_token=").append(refreshToken);
+ requestParamBuilder.append("&client_id=").append(clientId);
+ requestParamBuilder.append("&client_secret=").append(clientSecret);
+
+ byte[] postData = requestParamBuilder.toString().getBytes(CharEncoding.UTF_8);
+ int postDataLength = postData.length;
+
+ urlConnection.setDoOutput(true);
+ urlConnection.setInstanceFollowRedirects(false);
urlConnection.setRequestMethod("POST");
- urlConnection.addRequestProperty("client_id", clientId);
- urlConnection.addRequestProperty("client_secret", clientSecret);
- urlConnection.setRequestProperty("Authorization", "Bearer " + base64Auth);
+ urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+ urlConnection.setRequestProperty("charset", "utf-8");
+ urlConnection.setRequestProperty("Content-Length", Integer.toString(postDataLength));
+ urlConnection.setRequestProperty("Authorization", "Basic " + base64Auth);
+ urlConnection.setUseCaches(false);
+ outputStream = urlConnection.getOutputStream();
+ writer = new BufferedOutputStream(outputStream);//Bearer
+ writer.write(postData);
+ writer.flush();
int statusCode = urlConnection.getResponseCode();
- InputStream inputStream;
if (statusCode >= HttpStatus.SC_BAD_REQUEST)
inputStream = urlConnection.getErrorStream();
else
@@ -784,19 +835,19 @@ public AccountResponse oauth2authenticateRefreshToken(String refreshToken) {
String responseString = IOUtils.toString(inputStream);
if (statusCode == HttpStatus.SC_OK) {
- Timber.d("response String: %s using request url %s", responseString, url);
+ Timber.d("response String: %s using request url %s", responseString, tokenEndpointURL);
- AccountResponse accountResponse = new Gson().fromJson(responseString, AccountResponse.class);
+ AccountResponse accountResponse = gson.fromJson(responseString, AccountResponse.class);
return accountResponse;
} else {
- accountError = new Gson().fromJson(responseString, AccountError.class);
+ accountError = gson.fromJson(responseString, AccountError.class);
return new AccountResponse(statusCode, accountError);
}
} catch (MalformedURLException e) {
- Timber.e(e, "Failed to check credentials bad url %s", url);
+ Timber.e(e, "Failed to check credentials bad url %s", tokenEndpointURL);
accountError = new AccountError(0, MALFORMED_URL.name());
} catch (SocketTimeoutException e) {
@@ -804,15 +855,15 @@ public AccountResponse oauth2authenticateRefreshToken(String refreshToken) {
accountError = new AccountError(0, TIMEOUT.name());
- Timber.e(e, "Failed to check credentials of: %s using %s . Error: %s", refreshToken, url, e.toString());
+ Timber.e(e, "Failed to check credentials of: %s using %s . Error: %s", refreshToken, tokenEndpointURL, e.toString());
} catch (IOException e) {
- Timber.e(e, "Failed to check credentials of: %s using %s . Error: %s", refreshToken, url, e.toString());
+ Timber.e(e, "Failed to check credentials of: %s using %s . Error: %s", refreshToken, tokenEndpointURL, e.toString());
accountError = new AccountError(0, NO_INTERNET_CONNECTIVITY.name());
} finally {
- if (urlConnection != null) {
- urlConnection.disconnect();
- }
+ closeConnection(urlConnection);
+ closeIOStream(writer);
+ closeIOStream(outputStream);
}
@@ -836,4 +887,68 @@ private String refreshAuthenticationToken(String refreshToken) {
return response.getAccessToken();
}
+
+ public AccountConfiguration fetchOAuthConfiguration() {
+
+ String baseUrl = configuration.dristhiBaseURL();
+
+ if (baseUrl.endsWith("/")) {
+ baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
+ }
+
+ baseUrl = baseUrl + AccountHelper.OAUTH.ACCOUNT_CONFIGURATION_ENDPOINT;
+
+ HttpURLConnection urlConnection = null;
+
+ InputStream inputStream = null;
+ try {
+ URL url = new URL(baseUrl);
+ urlConnection = (HttpURLConnection) url.openConnection();
+
+ int statusCode = urlConnection.getResponseCode();
+ if (statusCode == HttpStatus.SC_OK) {
+
+ inputStream = urlConnection.getInputStream();
+
+ String responseString = IOUtils.toString(inputStream);
+
+ return gson.fromJson(responseString, AccountConfiguration.class);
+ }
+
+ } catch (IOException e) {
+ Timber.e(e);
+ } finally {
+
+ closeConnection(urlConnection);
+ closeIOStream(inputStream);
+
+ }
+ return null;
+ }
+
+ private void closeConnection(HttpURLConnection urlConnection) {
+ if (urlConnection != null) {
+ try {
+ urlConnection.disconnect();
+ } catch (Exception ex) {
+ Timber.e(ex, "Error closing input HttpUrlConnection");
+ }
+
+ }
+
+ }
+
+ private void closeIOStream(Closeable inputStream) {
+ if (inputStream != null) {
+
+ try {
+ inputStream.close();
+ } catch (IOException ex) {
+
+ Timber.e(ex, "Error closing input stream");
+ }
+
+ }
+
+ }
}
\ No newline at end of file
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java b/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java
index 01262dc70..e463bda49 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java
@@ -14,6 +14,7 @@
import org.smartregister.Context;
import org.smartregister.CoreLibrary;
import org.smartregister.R;
+import org.smartregister.account.AccountHelper;
import org.smartregister.repository.DrishtiRepository;
import org.smartregister.repository.Repository;
import org.smartregister.sync.ClientProcessorForJava;
@@ -122,8 +123,9 @@ public Repository getRepository() {
public String getPassword() {
if (password == null) {
- String username = context.userService().getAllSharedPreferences().fetchRegisteredANM();
- password = context.userService().getGroupId(username);
+
+ password = AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType());
+
}
return password;
}
From b1eeee1d6ac9d9f7644fe3766cb6f87351d6d138 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Fri, 15 May 2020 15:22:28 +0300
Subject: [PATCH 10/70] Remove all stored password references
---
.../repository/AllSharedPreferences.java | 14 ---------
.../smartregister/service/UserService.java | 25 ++++------------
.../view/activity/DrishtiApplication.java | 6 ++--
.../repository/AllSharedPreferencesTest.java | 29 +++++--------------
4 files changed, 17 insertions(+), 57 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/repository/AllSharedPreferences.java b/opensrp-app/src/main/java/org/smartregister/repository/AllSharedPreferences.java
index cea320ed8..8647aee7c 100644
--- a/opensrp-app/src/main/java/org/smartregister/repository/AllSharedPreferences.java
+++ b/opensrp-app/src/main/java/org/smartregister/repository/AllSharedPreferences.java
@@ -16,7 +16,6 @@
import static org.smartregister.AllConstants.DEFAULT_TEAM_PREFIX;
import static org.smartregister.AllConstants.DRISHTI_BASE_URL;
import static org.smartregister.AllConstants.ENCRYPTED_GROUP_ID_PREFIX;
-import static org.smartregister.AllConstants.ENCRYPTED_PASSWORD_PREFIX;
import static org.smartregister.AllConstants.FORCE_REMOTE_LOGIN;
import static org.smartregister.AllConstants.IS_SYNC_INITIAL_KEY;
import static org.smartregister.AllConstants.IS_SYNC_IN_PROGRESS_PREFERENCE_KEY;
@@ -73,19 +72,6 @@ public void saveServerTimeZone(String serverTimeZone) {
preferences.edit().putString(SERVER_TIMEZONE, serverTimeZone).commit();
}
- public String fetchEncryptedPassword(String username) {
- if (username != null) {
- return preferences.getString(ENCRYPTED_PASSWORD_PREFIX + username, null);
- }
- return null;
- }
-
- public void saveEncryptedPassword(String username, String password) {
- if (username != null) {
- preferences.edit().putString(ENCRYPTED_PASSWORD_PREFIX + username, password).commit();
- }
- }
-
public String fetchPioneerUser() {
return preferences.getString(PIONEER_USER, null);
}
diff --git a/opensrp-app/src/main/java/org/smartregister/service/UserService.java b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
index 6ec940e33..117aa9738 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/UserService.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
@@ -214,22 +214,14 @@ public boolean isValidLocalLogin(String userName, String password) {
public boolean isUserInValidGroup(final String userName, final String password) {
// Check if everything OK for local login
- if (keyStore != null && userName != null && password != null && !allSharedPreferences
- .fetchForceRemoteLogin()) {
+ if (keyStore != null && userName != null && password != null && !allSharedPreferences.fetchForceRemoteLogin()) {
String username = userName.equalsIgnoreCase(allSharedPreferences.fetchRegisteredANM()) ? allSharedPreferences.fetchRegisteredANM() : userName;
try {
KeyStore.PrivateKeyEntry privateKeyEntry = getUserKeyPair(username);
if (privateKeyEntry != null) {
- // Compare stored encrypted password with provided password
- String encryptedPassword = allSharedPreferences.
- fetchEncryptedPassword(username);
- String decryptedPassword = decryptString(privateKeyEntry, encryptedPassword);
-
- if (password.equals(decryptedPassword)) {
- String groupId = getGroupId(username, privateKeyEntry);
- if (groupId != null) {
- return isValidGroupId(groupId);
- }
+ String groupId = getGroupId(username, privateKeyEntry);
+ if (groupId != null) {
+ return isValidGroupId(groupId);
}
}
} catch (Exception e) {
@@ -587,7 +579,6 @@ public Bundle saveUserGroup(String userName, String password, LoginResponseData
return null;
}
-
String groupId = null;
SyncConfiguration syncConfiguration = CoreLibrary.getInstance().getSyncConfiguration();
@@ -604,20 +595,16 @@ public Bundle saveUserGroup(String userName, String password, LoginResponseData
if (StringUtils.isBlank(groupId)) {
return null;
- } else {
-
- bundle.putString(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, groupId);
}
if (privateKeyEntry != null) {
- // First save the encrypted password
- String encryptedPassword = encryptString(privateKeyEntry, password);
- allSharedPreferences.saveEncryptedPassword(username, encryptedPassword);
// Then save the encrypted group
String encryptedGroupId = encryptString(privateKeyEntry, groupId);
allSharedPreferences.saveEncryptedGroupId(username, encryptedGroupId);
+ bundle.putString(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, encryptedGroupId);
+
// Finally, save the pioneer user
if (allSharedPreferences.fetchPioneerUser() == null) {
allSharedPreferences.savePioneerUser(username);
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java b/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java
index e463bda49..d548dc714 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java
@@ -14,7 +14,6 @@
import org.smartregister.Context;
import org.smartregister.CoreLibrary;
import org.smartregister.R;
-import org.smartregister.account.AccountHelper;
import org.smartregister.repository.DrishtiRepository;
import org.smartregister.repository.Repository;
import org.smartregister.sync.ClientProcessorForJava;
@@ -124,9 +123,10 @@ public Repository getRepository() {
public String getPassword() {
if (password == null) {
- password = AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType());
-
+ String username = context.userService().getAllSharedPreferences().fetchRegisteredANM();
+ password = context.userService().getGroupId(username);
}
+
return password;
}
diff --git a/opensrp-app/src/test/java/org/smartregister/repository/AllSharedPreferencesTest.java b/opensrp-app/src/test/java/org/smartregister/repository/AllSharedPreferencesTest.java
index e584bbfe4..0aeed77cf 100644
--- a/opensrp-app/src/test/java/org/smartregister/repository/AllSharedPreferencesTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/repository/AllSharedPreferencesTest.java
@@ -74,19 +74,6 @@ public void assertSaveServerTimeZone() {
Mockito.verify(preferences, Mockito.times(1)).edit();
}
- @Test
- public void assertFetchEncryptedPassword() {
- Assert.assertNull(allSharedPreferences.fetchEncryptedPassword(null));
- Assert.assertNotNull(allSharedPreferences.fetchEncryptedPassword(""));
- Assert.assertEquals(allSharedPreferences.fetchEncryptedPassword(""), str);
- }
-
- @Test
- public void assertSaveEncryptedPassword() {
- allSharedPreferences.saveEncryptedPassword("uname", "pword");
- Mockito.verify(preferences, Mockito.times(1)).edit();
- }
-
@Test
public void assertFetchPioneerUser() {
Assert.assertEquals(allSharedPreferences.fetchPioneerUser(), str);
@@ -309,7 +296,7 @@ public void testFetchDefaultTeamId() {
@Test
public void testFetchLastSyncDate() {
- Mockito.when(preferences.getLong(any(), anyLong())).thenReturn(2000L);
+ Mockito.when(preferences.getLong(any(), anyLong())).thenReturn(2000L);
assertEquals((Long) 2000L, allSharedPreferences.fetchLastSyncDate(1000L));
}
@@ -340,7 +327,7 @@ public void testSaveIsSyncInitial() {
@Test
public void testFetchLastCheckTimeStamp() {
- Mockito.when(preferences.getLong(any(), anyLong())).thenReturn(2000L);
+ Mockito.when(preferences.getLong(any(), anyLong())).thenReturn(2000L);
assertEquals(2000L, allSharedPreferences.fetchLastCheckTimeStamp());
}
@@ -371,14 +358,14 @@ public void testUpdateLastSettingsSyncTimeStamp() {
@Test
public void testFetchLastSettingsSyncTimeStamp() {
- Mockito.when(preferences.getLong(any(), anyLong())).thenReturn(2000L);
+ Mockito.when(preferences.getLong(any(), anyLong())).thenReturn(2000L);
assertEquals(2000L, allSharedPreferences.fetchLastSettingsSyncTimeStamp());
}
@Test
public void testIsMigratedToSqlite4() {
- Mockito.when(preferences.getBoolean(any(), anyBoolean())).thenReturn(true);
+ Mockito.when(preferences.getBoolean(any(), anyBoolean())).thenReturn(true);
assertTrue(allSharedPreferences.isMigratedToSqlite4());
}
@@ -397,7 +384,7 @@ public void testSetMigratedToSqlite4() {
@Test
public void testGetLastPeerToPeerSyncProcessedEvent() {
- Mockito.when(preferences.getInt(any(), anyInt())).thenReturn(10);
+ Mockito.when(preferences.getInt(any(), anyInt())).thenReturn(10);
assertEquals(10, allSharedPreferences.getLastPeerToPeerSyncProcessedEvent());
}
@@ -416,7 +403,7 @@ public void testSetLastPeerToPeerSyncProcessedEvent() {
@Test
public void isPeerToPeerUnprocessedEvents() {
- Mockito.when(preferences.getBoolean(any(), anyBoolean())).thenReturn(true);
+ Mockito.when(preferences.getBoolean(any(), anyBoolean())).thenReturn(true);
assertTrue(allSharedPreferences.isPeerToPeerUnprocessedEvents());
}
@@ -447,7 +434,7 @@ public void testUpdateLastClientProcessedTimeStamp() {
@Test
public void testFetchLastClientProcessedTimeStamp() {
- Mockito.when(preferences.getLong(any(), anyLong())).thenReturn(2000L);
+ Mockito.when(preferences.getLong(any(), anyLong())).thenReturn(2000L);
assertEquals(2000L, allSharedPreferences.fetchLastClientProcessedTimeStamp());
}
@@ -466,7 +453,7 @@ public void testUpdateTransactionsKilledFlag() {
@Test
public void testFetchTransactionsKilledFlag() {
- Mockito.when(preferences.getBoolean(any(), anyBoolean())).thenReturn(true);
+ Mockito.when(preferences.getBoolean(any(), anyBoolean())).thenReturn(true);
assertTrue(allSharedPreferences.fetchTransactionsKilledFlag());
}
From 60a1c78fccc227261316e351057b94c8c4c5e9c2 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Sat, 16 May 2020 09:51:17 +0300
Subject: [PATCH 11/70] Fix error dialog rendering multiple times at a go
---
.../view/activity/BaseLoginActivity.java | 24 +++++++++++--------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java b/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java
index 43b6cc123..cb45af0dc 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java
@@ -51,6 +51,7 @@ public abstract class BaseLoginActivity extends MultiLanguageActivity implements
private Boolean showPasswordChecked = false;
private SyncUtils syncUtils;
private String authTokenType;
+ private AlertDialog alertDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -166,16 +167,19 @@ public void showErrorDialog(String message) {
}
public void showErrorDialog(@StringRes int title, String message) {
- AlertDialog alertDialog = new AlertDialog.Builder(this)
- .setTitle(title)
- .setMessage(message)
- .setPositiveButton("OK", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- dialogInterface.dismiss();
- }
- })
- .create();
+
+ if (alertDialog == null) {
+ alertDialog = new AlertDialog.Builder(this)
+ .setPositiveButton("OK", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ dialogInterface.dismiss();
+ }
+ }).create();
+ }
+
+ alertDialog.setTitle(title);
+ alertDialog.setMessage(message);
alertDialog.show();
}
From 4e2823378363582c6595ac425e43625297a61cd7 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Tue, 19 May 2020 11:17:50 +0300
Subject: [PATCH 12/70] Fix bug missing tag level on location hierarchy
---
.../java/org/smartregister/location/helper/LocationHelper.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/location/helper/LocationHelper.java b/opensrp-app/src/main/java/org/smartregister/location/helper/LocationHelper.java
index a8993bb5f..0e0839ec9 100644
--- a/opensrp-app/src/main/java/org/smartregister/location/helper/LocationHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/location/helper/LocationHelper.java
@@ -471,7 +471,7 @@ private List getFormJsonData(TreeNode openMrsLoc
formLocation.key = name;
Set levels = node.getTags();
- formLocation.level = "";
+ formLocation.level = levels != null && !levels.isEmpty() ? levels.iterator().next() : "";
LinkedHashMap> childMap = childMap(openMrsLocationData);
From 47ffabfeeeb9df0d17b2d3c5650f3cba776eaafe Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Tue, 26 May 2020 09:47:16 +0300
Subject: [PATCH 13/70] Update OAuth implementation - Refactor refresh token
and verify login logic - Add support for Spring OAuth Fallback if keycloak
not supported
---
opensrp-app/AndroidManifest.xml | 1 +
.../org/smartregister/SyncConfiguration.java | 3 +
.../account/AbstractAccountAuthenticator.java | 14 --
.../account/AccountAuthenticator.java | 24 +--
.../account/AccountConfiguration.java | 16 ++
.../smartregister/account/AccountHelper.java | 30 ++-
.../login/interactor/BaseLoginInteractor.java | 12 +-
.../login/task/RemoteLoginTask.java | 25 ++-
.../org/smartregister/service/HTTPAgent.java | 193 +++++++++---------
.../smartregister/service/UserService.java | 51 ++---
.../helper/SyncSettingsServiceHelper.java | 28 +--
.../view/activity/DrishtiApplication.java | 9 +-
.../view/activity/LoginActivity.java | 12 +-
.../smartregister/TestSyncConfiguration.java | 7 +
.../smartregister/service/HTTPAgentTest.java | 4 +-
.../service/UserServiceTest.java | 3 +-
.../view/activity/LoginActivityTest.java | 2 +-
.../LoginActivityWithRemoteLoginTest.java | 2 +-
.../fragment/BaseRegisterFragmentTest.java | 4 +
19 files changed, 234 insertions(+), 206 deletions(-)
delete mode 100644 opensrp-app/src/main/java/org/smartregister/account/AbstractAccountAuthenticator.java
diff --git a/opensrp-app/AndroidManifest.xml b/opensrp-app/AndroidManifest.xml
index 4428271db..168741189 100644
--- a/opensrp-app/AndroidManifest.xml
+++ b/opensrp-app/AndroidManifest.xml
@@ -16,6 +16,7 @@
+
diff --git a/opensrp-app/src/main/java/org/smartregister/SyncConfiguration.java b/opensrp-app/src/main/java/org/smartregister/SyncConfiguration.java
index 290404ad9..cbc33477e 100644
--- a/opensrp-app/src/main/java/org/smartregister/SyncConfiguration.java
+++ b/opensrp-app/src/main/java/org/smartregister/SyncConfiguration.java
@@ -1,6 +1,7 @@
package org.smartregister;
import org.smartregister.account.AccountHelper;
+import org.smartregister.view.activity.BaseLoginActivity;
import java.util.ArrayList;
import java.util.List;
@@ -181,4 +182,6 @@ public boolean runPlanEvaluationOnClientProcessing() {
public int getMaxAuthenticationRetries() {
return AccountHelper.MAX_AUTH_RETRIES;
}
+
+ public abstract Class extends BaseLoginActivity> getAuthenticationActivity();
}
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AbstractAccountAuthenticator.java b/opensrp-app/src/main/java/org/smartregister/account/AbstractAccountAuthenticator.java
deleted file mode 100644
index 5497ef7ba..000000000
--- a/opensrp-app/src/main/java/org/smartregister/account/AbstractAccountAuthenticator.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.smartregister.account;
-
-import android.content.Context;
-
-/**
- * Created by ndegwamartin on 11/05/2020.
- */
-public abstract class AbstractAccountAuthenticator extends android.accounts.AbstractAccountAuthenticator {
- public AbstractAccountAuthenticator(Context context) {
- super(context);
- }
-
- public abstract String getRefreshToken();
-}
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java b/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
index 229bd8449..0c2a75bdb 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
@@ -1,5 +1,6 @@
package org.smartregister.account;
+import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
@@ -9,8 +10,8 @@
import android.os.Bundle;
import android.text.TextUtils;
+import org.apache.http.HttpStatus;
import org.smartregister.CoreLibrary;
-import org.smartregister.view.activity.LoginActivity;
import timber.log.Timber;
@@ -30,7 +31,7 @@ public AccountAuthenticator(Context context) {
@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
- final Intent intent = new Intent(mContext, LoginActivity.class);
+ final Intent intent = new Intent(mContext, CoreLibrary.getInstance().getSyncConfiguration().getAuthenticationActivity());
intent.putExtra(AccountHelper.INTENT_KEY.ACCOUNT_TYPE, accountType);
intent.putExtra(AccountHelper.INTENT_KEY.AUTH_TYPE, authTokenType);
intent.putExtra(AccountHelper.INTENT_KEY.IS_NEW_ACCOUNT, true);
@@ -59,9 +60,14 @@ public Bundle getAuthToken(AccountAuthenticatorResponse response, Account accoun
Timber.d("Authenticate with saved credentials");
- AccountResponse accountResponse = CoreLibrary.getInstance().context().getHttpAgent().oauth2authenticate(account.name, password, AccountHelper.OAUTH.GRANT_TYPE.PASSWORD, CoreLibrary.getInstance().context().allSettings().get(AccountHelper.CONFIGURATION_CONSTANTS.TOKEN_ENDPOINT_URL));
- authToken = accountResponse.getAccessToken();
- refreshToken = accountResponse.getRefreshToken();
+ AccountResponse accountResponse = CoreLibrary.getInstance().context().getHttpAgent().oauth2authenticateRefreshToken(password);
+ if (accountResponse.getStatus() == HttpStatus.SC_OK) {
+ authToken = accountResponse.getAccessToken();
+ refreshToken = accountResponse.getRefreshToken();
+
+ accountManager.setPassword(account, refreshToken);
+ accountManager.setAuthToken(account, authTokenType, authToken);
+ }
} catch (Exception e) {
Timber.e(e);
@@ -74,11 +80,10 @@ public Bundle getAuthToken(AccountAuthenticatorResponse response, Account accoun
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
- result.putString(AccountHelper.KEY_REFRESH_TOKEN, refreshToken);
return result;
}
- final Intent intent = new Intent(mContext, LoginActivity.class);
+ final Intent intent = new Intent(mContext, CoreLibrary.getInstance().getSyncConfiguration().getAuthenticationActivity());
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
intent.putExtra(AccountHelper.INTENT_KEY.ACCOUNT_TYPE, account.type);
intent.putExtra(AccountHelper.INTENT_KEY.AUTH_TYPE, authTokenType);
@@ -113,9 +118,4 @@ public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
return null;
}
-
- @Override
- public String getRefreshToken() {
- return AccountHelper.getAccountManagerValue(AccountHelper.KEY_REFRESH_TOKEN, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType());
- }
}
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountConfiguration.java b/opensrp-app/src/main/java/org/smartregister/account/AccountConfiguration.java
index 72aeabfad..171d9a47b 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountConfiguration.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountConfiguration.java
@@ -36,4 +36,20 @@ public String getTokenEndpoint() {
public List getGrantTypesSupported() {
return grantTypesSupported;
}
+
+ public void setIssuerEndpoint(String issuerEndpoint) {
+ this.issuerEndpoint = issuerEndpoint;
+ }
+
+ public void setAuthorizationEndpoint(String authorizationEndpoint) {
+ this.authorizationEndpoint = authorizationEndpoint;
+ }
+
+ public void setTokenEndpoint(String tokenEndpoint) {
+ this.tokenEndpoint = tokenEndpoint;
+ }
+
+ public void setGrantTypesSupported(List grantTypesSupported) {
+ this.grantTypesSupported = grantTypesSupported;
+ }
}
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
index de97ca9c3..4a0b4a06b 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
@@ -5,6 +5,8 @@
import org.smartregister.CoreLibrary;
+import timber.log.Timber;
+
/**
* Created by ndegwamartin on 2020-04-27.
*/
@@ -64,7 +66,7 @@ public static Account getOauthAccountByType(String accountType) {
public static String getAccountManagerValue(String key, String accountType) {
Account account = AccountHelper.getOauthAccountByType(accountType);
if (account != null) {
- return CoreLibrary.getInstance().getAccountManager().getUserData(account, key);
+ return accountManager.getUserData(account, key);
}
return null;
}
@@ -72,11 +74,35 @@ public static String getAccountManagerValue(String key, String accountType) {
/**
* @param accountType unique name to identify our account type in the Account Manager
* @param authTokenType type of token requested from server e.g. PROVIDER, ADMIN
- * @return
+ * @return access token
*/
public static String getOAuthToken(String accountType, String authTokenType) {
Account account = getOauthAccountByType(accountType);
+ try {
+ return accountManager.blockingGetAuthToken(account, authTokenType, true);
+ } catch (Exception ex) {
+ Timber.e(ex, "EXCEPTION: %s", ex.toString());
+ return null;
+ }
+ }
+
+ /** This method invalidates the auth token so that the Authenticator can fetch a new one from server
+ * @param accountType unique name to identify our account type in the Account Manager
+ * @param authToken token to invalidate
+ */
+ public static void invalidateAuthToken(String accountType, String authToken) {
+ accountManager.invalidateAuthToken(accountType, authToken);
+ }
+
+
+ /**
+ * @param accountType unique name to identify our account type in the Account Manager
+ * @param authTokenType type of token requested from server e.g. PROVIDER, ADMIN
+ * @return access token cached
+ */
+ public static String getCachedOAuthToken(String accountType, String authTokenType) {
+ Account account = getOauthAccountByType(accountType);
return accountManager.peekAuthToken(account, authTokenType);
}
diff --git a/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java b/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java
index e59d97d3e..fbca7463e 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java
@@ -88,16 +88,16 @@ private void localLogin(WeakReference view, String userN
} else if (isAuthenticated && (!AllConstants.TIME_CHECK || TimeStatus.OK.equals(getUserService().validateStoredServerTimeZone()))) {
- navigateToHomePage(userName, password);
+ navigateToHomePage(userName);
} else {
loginWithLocalFlag(view, false, userName, password);
}
}
- private void navigateToHomePage(String userName, String password) {
+ private void navigateToHomePage(String userName) {
- getUserService().localLogin(userName, password);
+ getUserService().localLogin(userName);
getLoginView().goToHome(false);
CoreLibrary.getInstance().initP2pLibrary(userName);
@@ -136,7 +136,7 @@ public void onEvent(LoginResponse loginResponse) {
);
if (!AllConstants.TIME_CHECK || timeStatus.equals(TimeStatus.OK)) {
- remoteLoginWith(username, password, loginResponse);
+ remoteLoginWith(username, loginResponse);
} else {
if (timeStatus.equals(TimeStatus.TIMEZONE_MISMATCH)) {
@@ -207,8 +207,8 @@ private void tryRemoteLogin(final String userName, final String password, final
remoteLoginTask.execute();
}
- private void remoteLoginWith(String userName, String password, LoginResponse loginResponse) {
- getUserService().remoteLogin(userName, password, loginResponse.payload());
+ private void remoteLoginWith(String userName, LoginResponse loginResponse) {
+ getUserService().processLoginResponseDataForUser(userName, loginResponse.payload());
processServerSettings(loginResponse);
scheduleJobsPeriodically();
diff --git a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
index a23b1855f..6052d4b3e 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
@@ -24,6 +24,8 @@
import org.smartregister.util.Utils;
import org.smartregister.view.contract.BaseLoginContract;
+import java.util.Arrays;
+
import timber.log.Timber;
import static org.smartregister.domain.LoginResponse.CUSTOM_SERVER_RESPONSE;
@@ -58,11 +60,20 @@ protected void onPreExecute() {
protected LoginResponse doInBackground(Void... params) {
LoginResponse loginResponse;
- Bundle userData = null;
try {
AccountConfiguration accountConfiguration = CoreLibrary.getInstance().context().getHttpAgent().fetchOAuthConfiguration();
+ boolean isKeyclockConfigured = accountConfiguration != null;
+
+ if (!isKeyclockConfigured) {
+ accountConfiguration = new AccountConfiguration();
+ accountConfiguration.setGrantTypesSupported(Arrays.asList(AccountHelper.OAUTH.GRANT_TYPE.PASSWORD));
+ accountConfiguration.setTokenEndpoint(CoreLibrary.getInstance().context().configuration().dristhiBaseURL() + AccountHelper.OAUTH.TOKEN_ENDPOINT);
+ accountConfiguration.setAuthorizationEndpoint("");
+ accountConfiguration.setIssuerEndpoint("");
+ }
+
if (accountConfiguration != null) {
if (!accountConfiguration.getGrantTypesSupported().contains(AccountHelper.OAUTH.GRANT_TYPE.PASSWORD))
@@ -86,7 +97,11 @@ protected LoginResponse doInBackground(Void... params) {
if (loginResponse != null && loginResponse.equals(LoginResponse.SUCCESS)) {
- userData = getOpenSRPContext().userService().saveUserGroup(mUsername, mPassword, loginResponse.payload());
+ Bundle userData = getOpenSRPContext().userService().saveUserGroup(mUsername, mPassword, loginResponse.payload());
+
+ mAccountManager.addAccountExplicitly(account, response.getRefreshToken(), userData);
+ mAccountManager.setAuthToken(account, mLoginView.getAuthTokenType(), response.getAccessToken());
+ mAccountManager.setPassword(account, response.getRefreshToken());
if (getOpenSRPContext().userService().getGroupId(mUsername) != null && CoreLibrary.getInstance().getSyncConfiguration().isSyncSettings()) {
@@ -95,7 +110,7 @@ protected LoginResponse doInBackground(Void... params) {
SyncSettingsServiceHelper syncSettingsServiceHelper = new SyncSettingsServiceHelper(getOpenSRPContext().configuration().dristhiBaseURL(), getOpenSRPContext().getHttpAgent());
try {
- JSONArray settings = syncSettingsServiceHelper.pullSettingsFromServer(Utils.getFilterValue(loginResponse, CoreLibrary.getInstance().getSyncConfiguration().getSyncFilterParam()));
+ JSONArray settings = syncSettingsServiceHelper.pullSettingsFromServer(Utils.getFilterValue(loginResponse, CoreLibrary.getInstance().getSyncConfiguration().getSyncFilterParam()),response.getAccessToken());
JSONObject prefSettingsData = new JSONObject();
prefSettingsData.put(AllConstants.PREF_KEY.SETTINGS, settings);
@@ -108,10 +123,6 @@ protected LoginResponse doInBackground(Void... params) {
}
}
- mAccountManager.addAccountExplicitly(account, response.getAccessToken(), userData);
- mAccountManager.setAuthToken(account, mLoginView.getAuthTokenType(), response.getAccessToken());
- mAccountManager.setUserData(account, AccountHelper.KEY_REFRESH_TOKEN, response.getRefreshToken());
-
} else {
throw new AccountsException("Could not fetch OAuth Configuration");
}
diff --git a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
index eae885525..9c8a2bdb8 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
@@ -1,7 +1,5 @@
package org.smartregister.service;
-import android.accounts.Account;
-import android.accounts.AccountManager;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Base64;
@@ -110,7 +108,10 @@ public HTTPAgent(Context context, AllSharedPreferences
syncUtils = new SyncUtils(context.getApplicationContext());
}
- private HttpURLConnection initializeHttp(String requestURLPath) throws IOException {
+ /**
+ * This method initializes httpurlconnection
+ */
+ private HttpURLConnection initializeHttp(String requestURLPath, boolean setOauthToken) throws IOException {
URL url = new URL(requestURLPath);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
if (urlConnection instanceof HttpsURLConnection) {
@@ -119,73 +120,60 @@ private HttpURLConnection initializeHttp(String requestURLPath) throws IOExcepti
}
urlConnection.setConnectTimeout(getConnectTimeout());
urlConnection.setReadTimeout(getReadTimeout());
- AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
- if (AccountHelper.getOauthAccountByType(authenticatorXml.getAccountType()) != null)
- urlConnection.setRequestProperty("Authorization", new StringBuilder("Bearer ").append(AccountHelper.getOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).toString());
-
+ if (setOauthToken) {
+ AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
+ if (AccountHelper.getOauthAccountByType(authenticatorXml.getAccountType()) != null)
+ urlConnection.setRequestProperty("Authorization", new StringBuilder("Bearer ").append(AccountHelper.getOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).toString());
+ }
return urlConnection;
}
public Response fetch(String requestURLPath) {
try {
- HttpURLConnection urlConnection = httpURLConnectionTries(requestURLPath);
-
- return handleResponse(urlConnection);
-
- } catch (IOException ex) {
- Timber.e(ex, "EXCEPTION %s", ex.toString());
- return new Response<>(ResponseStatus.failure, null);
- }
- }
-
- @NonNull
- private HttpURLConnection httpURLConnectionTries(String requestURLPath) throws IOException {
-
- int authRetries = 0;
- HttpURLConnection urlConnection = null;
-
- while (authRetries < CoreLibrary.getInstance().getSyncConfiguration().getMaxAuthenticationRetries() + 1) {
+ HttpURLConnection urlConnection = initializeHttp(requestURLPath, true);
+ //If unauthorized, request new token
- authRetries++;
+ if (HttpStatus.SC_UNAUTHORIZED == urlConnection.getResponseCode()) {
- urlConnection = initializeHttp(requestURLPath);
+ AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
- if (urlConnection.getResponseCode() == HttpStatus.SC_UNAUTHORIZED) {
+ String authToken = AccountHelper.getCachedOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
+ if (authToken != null)
+ AccountHelper.invalidateAuthToken(authenticatorXml.getAccountType(), authToken);
- refreshAuthenticationToken(AccountHelper.getAccountManagerValue(AccountHelper.KEY_REFRESH_TOKEN, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType()));
+ urlConnection = initializeHttp(requestURLPath, true);
- } else {
- break;
}
+ return processResponse(urlConnection);
+
+ } catch (IOException ex) {
+ Timber.e(ex, "EXCEPTION %s", ex.toString());
+ return new Response<>(ResponseStatus.failure, null);
}
- return urlConnection;
}
public Response post(String postURLPath, String jsonPayload) {
HttpURLConnection urlConnection;
AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
try {
- urlConnection = initializeHttp(postURLPath);
- urlConnection.setDoOutput(true);
- urlConnection.setRequestMethod("POST");
- urlConnection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
- urlConnection.setRequestProperty("Content-Encoding", "gzip");
- urlConnection.setRequestProperty("Authorization", new StringBuilder("Bearer ").append(AccountHelper.getOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).toString());
+ urlConnection = generatePostRequest(postURLPath, jsonPayload);
+ //If unauthorized, request new token
- OutputStream os = urlConnection.getOutputStream();
- BufferedOutputStream writer = new BufferedOutputStream(os);
- writer.write(gzipCompression.compress(jsonPayload));
- writer.flush();
- writer.close();
- os.close();
+ if (HttpStatus.SC_UNAUTHORIZED == urlConnection.getResponseCode()) {
+
+ String authToken = AccountHelper.getCachedOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
+ if (authToken != null)
+ AccountHelper.invalidateAuthToken(authenticatorXml.getAccountType(), authToken);
- urlConnection.connect();
+ urlConnection = generatePostRequest(postURLPath, jsonPayload);
- return handleResponse(urlConnection);
+ }
+
+ return processResponse(urlConnection);
} catch (IOException ex) {
Timber.e(ex, "EXCEPTION: %s", ex.toString());
@@ -193,6 +181,28 @@ public Response post(String postURLPath, String jsonPayload) {
}
}
+ @NonNull
+ private HttpURLConnection generatePostRequest(String postURLPath, String jsonPayload) throws IOException {
+ HttpURLConnection urlConnection;
+ urlConnection = initializeHttp(postURLPath, true);
+
+ urlConnection.setDoOutput(true);
+ urlConnection.setRequestMethod("POST");
+ urlConnection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
+ urlConnection.setRequestProperty("Content-Encoding", "gzip");
+
+
+ OutputStream os = urlConnection.getOutputStream();
+ BufferedOutputStream writer = new BufferedOutputStream(os);
+ writer.write(gzipCompression.compress(jsonPayload));
+ writer.flush();
+ writer.close();
+ os.close();
+
+ urlConnection.connect();
+ return urlConnection;
+ }
+
public Response postWithJsonResponse(String postURLPath, String jsonPayload) {
logResponse(postURLPath, jsonPayload);
return post(postURLPath, jsonPayload);
@@ -208,7 +218,7 @@ public LoginResponse urlCanBeAccessWithGivenCredentials(String requestURL, Strin
String url = null;
try {
url = requestURL.replaceAll("\\s+", "");
- urlConnection = initializeHttp(url);
+ urlConnection = initializeHttp(url, false);
final String basicAuth = "Basic " + Base64.encodeToString((userName + ":" + password).getBytes(), Base64.NO_WRAP);
urlConnection.setRequestProperty("Authorization", basicAuth);
@@ -258,17 +268,29 @@ public LoginResponse urlCanBeAccessWithGivenCredentials(String requestURL, Strin
public DownloadStatus downloadFromUrl(String url, String filename) {
AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
- Response status = downloadFromURL(url, filename, new StringBuilder("Bearer ").append(AccountHelper.getOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).toString());
+ Response status = downloadFromURL(url, filename);
Timber.d("downloading file name : %s and url %s", filename, url);
return status.payload();
}
- public Response fetchWithCredentials(String requestURL) {
+ public Response fetchWithCredentials(String requestURL, String accessToken) {
try {
- HttpURLConnection urlConnection = httpURLConnectionTries(requestURL);
- return handleResponse(urlConnection);
+ HttpURLConnection urlConnection = initializeHttp(requestURL, false);
+ urlConnection.setRequestProperty("Authorization", new StringBuilder("Bearer ").append(accessToken).toString());
+
+
+ //If unauthorized, request new token
+ if (HttpStatus.SC_UNAUTHORIZED == urlConnection.getResponseCode()) {
+
+ AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
+ AccountHelper.invalidateAuthToken(authenticatorXml.getAccountType(), accessToken);
+
+ urlConnection = initializeHttp(requestURL, true);
+
+ }
+ return processResponse(urlConnection);
} catch (IOException ex) {
Timber.e(ex, "EXCEPTION %s", ex.toString());
@@ -277,7 +299,7 @@ public Response fetchWithCredentials(String requestURL) {
}
- private Response handleResponse(HttpURLConnection urlConnection) {
+ private Response processResponse(HttpURLConnection urlConnection) {
String responseString;
String totalRecords;
try {
@@ -285,11 +307,7 @@ private Response handleResponse(HttpURLConnection urlConnection) {
InputStream inputStream = null;
- if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
-
- syncUtils.logoutUser();
-
- } else if (statusCode >= HttpStatus.SC_BAD_REQUEST)
+ if (statusCode >= HttpStatus.SC_BAD_REQUEST)
inputStream = urlConnection.getErrorStream();
else
inputStream = urlConnection.getInputStream();
@@ -330,18 +348,16 @@ public String httpImagePost(String urlString, ProfileImage image) {
OutputStream outputStream;
PrintWriter writer;
String responseString = "";
- AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
HttpURLConnection httpUrlConnection = null;
try {
- httpUrlConnection = initializeHttp(urlString);
+ httpUrlConnection = initializeHttp(urlString, true);
httpUrlConnection.setUseCaches(false);
httpUrlConnection.setDoInput(true);
httpUrlConnection.setDoOutput(true);
httpUrlConnection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
- httpUrlConnection.setRequestProperty("Authorization", new StringBuilder("Bearer ").append(AccountHelper.getOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).toString());
outputStream = httpUrlConnection.getOutputStream();
@@ -532,7 +548,7 @@ public AccountResponse oauth2authenticate(String username, String password, Stri
BufferedOutputStream writer = null;
try {
- urlConnection = initializeHttp(tokenEndpointURL);
+ urlConnection = initializeHttp(tokenEndpointURL, false);
String clientId = CoreLibrary.getInstance().getSyncConfiguration().getOauthClientId();
String clientSecret = CoreLibrary.getInstance().getSyncConfiguration().getOauthClientSecret();
@@ -617,7 +633,8 @@ public LoginResponse fetchUserDetails(String requestURL, String oauthAccessToken
try {
url = requestURL.replaceAll("\\s+", "");
- urlConnection = httpURLConnectionTries(url);
+ urlConnection = initializeHttp(url, false);
+ urlConnection.setRequestProperty("Authorization", new StringBuilder("Bearer ").append(oauthAccessToken).toString());
int statusCode = urlConnection.getResponseCode();
@@ -666,10 +683,9 @@ public LoginResponse fetchUserDetails(String requestURL, String oauthAccessToken
/**
* @param downloadURL_ This is the url of the image
* @param fileName This is how the image should be name after it has been downloaded.
- * @param accessToken This is the access token used to authenticate when accessing the url endpoint.
* @return Response This returns whether the download succeeded or failed.
*/
- public Response downloadFromURL(String downloadURL_, String fileName, String accessToken) {
+ public Response downloadFromURL(String downloadURL_, String fileName) {
HttpURLConnection httpUrlConnection = null;
try {
@@ -689,19 +705,7 @@ public Response downloadFromURL(String downloadURL_, String file
String downloadURL = downloadURL_.replaceAll("\\s+", "");
- /* Open connection to URL */
- URL url = new URL(downloadURL);
-
- httpUrlConnection = (HttpURLConnection) url.openConnection();
-
- if (httpUrlConnection instanceof HttpsURLConnection) {
- OpensrpSSLHelper opensrpSSLHelper = new OpensrpSSLHelper(context, configuration);
- ((HttpsURLConnection) httpUrlConnection).setSSLSocketFactory(opensrpSSLHelper.getSSLSocketFactory());
- }
-
- httpUrlConnection.setRequestProperty("Authorization", accessToken);
-
- httpUrlConnection.connect();
+ httpUrlConnection = initializeHttp(downloadURL, true);
int status = httpUrlConnection.getResponseCode();
if (status == HttpURLConnection.HTTP_OK) {
@@ -749,6 +753,7 @@ public Response downloadFromURL(String downloadURL_, String file
}
public boolean verifyAuthorization() {
+
String baseUrl = configuration.dristhiBaseURL();
if (baseUrl.endsWith("/")) {
@@ -760,17 +765,23 @@ public boolean verifyAuthorization() {
HttpURLConnection urlConnection = null;
try {
- URL url = new URL(baseUrl);
- urlConnection = (HttpURLConnection) url.openConnection();
+
+ urlConnection = initializeHttp(baseUrl, true);
AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
- if (AccountHelper.getOauthAccountByType(authenticatorXml.getAccountType()) != null)
- urlConnection.setRequestProperty("Authorization", new StringBuilder("Bearer ").append(AccountHelper.getOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).toString());
+
int statusCode = urlConnection.getResponseCode();
- if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
+ if (HttpStatus.SC_UNAUTHORIZED == urlConnection.getResponseCode()) {
+
+ String authToken = AccountHelper.getCachedOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
+ if (authToken != null)
+ AccountHelper.invalidateAuthToken(authenticatorXml.getAccountType(), authToken);
+
+ urlConnection = initializeHttp(baseUrl, true);
+
Timber.i("User not authorized. User access was revoked, will log off user");
- return false;
+ return HttpStatus.SC_UNAUTHORIZED == urlConnection.getResponseCode() ? false : true;
} else if (statusCode != HttpStatus.SC_OK) {
Timber.w("Error occurred verifying authorization, User will not be logged off");
} else {
@@ -800,7 +811,7 @@ public AccountResponse oauth2authenticateRefreshToken(String refreshToken) {
try {
tokenEndpointURL = CoreLibrary.getInstance().context().allSharedPreferences().getPreferences().getString(AccountHelper.CONFIGURATION_CONSTANTS.TOKEN_ENDPOINT_URL, "");
- urlConnection = initializeHttp(tokenEndpointURL);
+ urlConnection = initializeHttp(tokenEndpointURL, false);
final String base64Auth = BaseEncoding.base64().encode(new String(clientId + ":" + clientSecret).getBytes());
@@ -872,22 +883,6 @@ public AccountResponse oauth2authenticateRefreshToken(String refreshToken) {
}
- private String refreshAuthenticationToken(String refreshToken) {
-
- AccountResponse response = CoreLibrary.getInstance().context().getHttpAgent().oauth2authenticateRefreshToken(refreshToken);
-
- AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
- AccountManager accountManager = CoreLibrary.getInstance().getAccountManager();
-
- Account account = AccountHelper.getOauthAccountByType(authenticatorXml.getAccountType());
-
- accountManager.setAuthToken(account, AccountHelper.TOKEN_TYPE.PROVIDER, response.getAccessToken());
- accountManager.setPassword(account, response.getAccessToken());
- accountManager.setUserData(account, AccountHelper.KEY_REFRESH_TOKEN, response.getRefreshToken());
-
- return response.getAccessToken();
- }
-
public AccountConfiguration fetchOAuthConfiguration() {
String baseUrl = configuration.dristhiBaseURL();
diff --git a/opensrp-app/src/main/java/org/smartregister/service/UserService.java b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
index 117aa9738..01b199547 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/UserService.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
@@ -285,21 +285,6 @@ public boolean isUserInPioneerGroup(String userName) {
return false;
}
- public LoginResponse oauth2Authenticate(String userName, String password) {
- String requestURL;
-
- requestURL = configuration.dristhiBaseURL() + OPENSRP_AUTH_USER_URL_PATH;
-
- LoginResponse loginResponse = httpAgent
- .urlCanBeAccessWithGivenCredentials(requestURL, userName, password);
-
- if (loginResponse != null && loginResponse.equals(LoginResponse.SUCCESS)) {
- saveUserGroup(userName, password, loginResponse.payload());
- }
-
- return loginResponse;
- }
-
public LoginResponse fetchUserDetails(String accessToken) {
String requestURL;
@@ -315,8 +300,7 @@ public LoginResponse isValidRemoteLogin(String userName, String password) {
requestURL = configuration.dristhiBaseURL() + OPENSRP_AUTH_USER_URL_PATH;
- LoginResponse loginResponse = httpAgent
- .urlCanBeAccessWithGivenCredentials(requestURL, userName, password);
+ LoginResponse loginResponse = httpAgent.urlCanBeAccessWithGivenCredentials(requestURL, userName, password);
if (loginResponse != null && loginResponse.equals(LoginResponse.SUCCESS)) {
saveUserGroup(userName, password, loginResponse.payload());
@@ -334,23 +318,20 @@ public Response getLocationInformation() {
return httpAgent.fetch(requestURL);
}
- private boolean loginWith(String userName, String password) {
+ private boolean loginWith(String userName) {
boolean loginSuccessful = true;
- if (usesGroupIdAsDBPassword(userName)) {
- String encryptedGroupId = allSharedPreferences.fetchEncryptedGroupId(userName);
- try {
- KeyStore.PrivateKeyEntry privateKeyEntry = getUserKeyPair(userName);
- if (privateKeyEntry != null) {
- String groupId = decryptString(privateKeyEntry, encryptedGroupId);
- setupContextForLogin(userName, groupId);
- }
- } catch (Exception e) {
- Timber.e(e);
- loginSuccessful = false;
+ String encryptedGroupId = allSharedPreferences.fetchEncryptedGroupId(userName);
+ try {
+ KeyStore.PrivateKeyEntry privateKeyEntry = getUserKeyPair(userName);
+ if (privateKeyEntry != null) {
+ String groupId = decryptString(privateKeyEntry, encryptedGroupId);
+ setupContextForLogin(userName, groupId);
}
- } else {
- setupContextForLogin(userName, password);
+ } catch (Exception e) {
+ Timber.e(e);
+ loginSuccessful = false;
}
+
String username = userName;
if (allSharedPreferences.fetchRegisteredANM().equalsIgnoreCase(userName))
username = allSharedPreferences.fetchRegisteredANM();
@@ -379,14 +360,14 @@ private boolean usesGroupIdAsDBPassword(String userName) {
return false;
}
- public void localLogin(String userName, String password) {
- loginWith(userName, password);
+ public void localLogin(String userName) {
+ loginWith(userName);
}
- public void remoteLogin(String userName, String password, LoginResponseData userInfo) {
+ public void processLoginResponseDataForUser(String userName, LoginResponseData userInfo) {
String username = userInfo.user != null && StringUtils.isNotBlank(userInfo.user.getUsername())
? userInfo.user.getUsername() : userName;
- boolean loginSuccessful = loginWith(username, password);
+ boolean loginSuccessful = loginWith(username);
saveAnmLocation(getUserLocation(userInfo));
saveAnmTeam(getUserTeam(userInfo));
saveUserInfo(getUserData(userInfo));
diff --git a/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java b/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java
index a3f5b51fd..0c52b8535 100644
--- a/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java
@@ -8,6 +8,8 @@
import org.smartregister.AllConstants;
import org.smartregister.CoreLibrary;
import org.smartregister.SyncFilter;
+import org.smartregister.account.AccountAuthenticatorXml;
+import org.smartregister.account.AccountHelper;
import org.smartregister.domain.Response;
import org.smartregister.domain.Setting;
import org.smartregister.domain.SyncStatus;
@@ -28,6 +30,8 @@ public class SyncSettingsServiceHelper {
private HTTPAgent httpAgent;
private String baseUrl;
+ private String username;
+ private String password;
private AllSharedPreferences sharedPreferences;
public SyncSettingsServiceHelper(String baseUrl, HTTPAgent httpAgent) {
@@ -65,7 +69,11 @@ public int processIntent() throws JSONException {
}
private JSONArray getSettings() throws JSONException {
- JSONArray settings = pullSettingsFromServer(getInstance().getSyncConfiguration().getSettingsSyncFilterValue());
+
+ AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
+ String authToken = AccountHelper.getCachedOAuthToken(sharedPreferences.fetchRegisteredANM(), authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
+
+ JSONArray settings = pullSettingsFromServer(getInstance().getSyncConfiguration().getSettingsSyncFilterValue(), authToken);
getGlobalSettings(settings);
getExtraSettings(settings);
return settings;
@@ -101,12 +109,8 @@ private void getGlobalSettings(JSONArray settings) throws JSONException {
globalSettings = pullGlobalSettingsFromServer();
}
-<<<<<<< HEAD
aggregateSettings(settings, globalSettings);
}
-=======
- Response resp = httpAgent.fetchWithCredentials(url);
->>>>>>> Migrate user service
private void aggregateSettings(JSONArray settings, JSONArray globalSettings) throws JSONException {
if (!JsonFormUtils.isBlankJsonArray(globalSettings)) {
@@ -150,9 +154,9 @@ private JSONObject pushSettingsToServer() throws JSONException {
* @return settings {@link JSONArray} -- a JSON array of all the settings
* @throws JSONException
*/
- public JSONArray pullSettingsFromServer(String syncFilterValue) throws JSONException {
+ public JSONArray pullSettingsFromServer(String syncFilterValue, String accessToken) throws JSONException {
String url = SettingsSyncIntentService.SETTINGS_URL + "?" + getSettingsSyncFilterParam().value() + "=" + syncFilterValue + "&" + AllConstants.SERVER_VERSION + "=" + sharedPreferences.fetchLastSettingsSyncTimeStamp();
- return pullSettings(url);
+ return pullSettings(url, accessToken);
}
@VisibleForTesting
@@ -186,7 +190,7 @@ private JSONObject createSettingsConfigurationPayload() throws JSONException {
return siteSettingsPayload;
}
- private JSONArray pullSettings(String directoryUrl) throws JSONException {
+ private JSONArray pullSettings(String directoryUrl, String accessToken) throws JSONException {
String endString = "/";
if (baseUrl.endsWith(endString)) {
baseUrl = baseUrl.substring(0, baseUrl.lastIndexOf(endString));
@@ -201,7 +205,7 @@ private JSONArray pullSettings(String directoryUrl) throws JSONException {
return null;
}
- Response resp = getResponse(completeUrl);
+ Response resp = getResponse(completeUrl, accessToken);
if (resp == null || resp.isFailure()) {
Timber.e(" %s not returned data ", completeUrl);
@@ -212,8 +216,8 @@ private JSONArray pullSettings(String directoryUrl) throws JSONException {
}
@VisibleForTesting
- protected Response getResponse(String completeUrl) {
- return httpAgent.fetchWithCredentials(completeUrl, getUsername(), getPassword());
+ protected Response getResponse(String completeUrl, String accessToken) {
+ return httpAgent.fetchWithCredentials(completeUrl, accessToken);
}
public String getUsername() {
@@ -231,6 +235,4 @@ public String getPassword() {
public void setPassword(String password) {
this.password = password;
}
-
}
-
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java b/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java
index d548dc714..2a6e31d0b 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java
@@ -109,13 +109,10 @@ protected void attachBaseContext(android.content.Context base) {
}
public Repository getRepository() {
- ArrayList drishtireposotorylist = CoreLibrary.getInstance().context()
- .sharedRepositories();
- DrishtiRepository[] drishtireposotoryarray = drishtireposotorylist
- .toArray(new DrishtiRepository[drishtireposotorylist.size()]);
+ ArrayList drishtiRepositoryList = CoreLibrary.getInstance().context().sharedRepositories();
+ DrishtiRepository[] drishtiRepositoryArray = drishtiRepositoryList.toArray(new DrishtiRepository[drishtiRepositoryList.size()]);
if (repository == null) {
- repository = new Repository(getInstance().getApplicationContext(), null,
- drishtireposotoryarray);
+ repository = new Repository(getInstance().getApplicationContext(), null, drishtiRepositoryArray);
}
return repository;
}
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java b/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java
index a18656057..26a389cb7 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java
@@ -160,7 +160,7 @@ private void localLogin(View view, String userName, String password) {
}
if (context.userService().isValidLocalLogin(userName, password)) {
- localLoginWith(userName, password);
+ localLoginWith(userName);
} else {
showErrorDialog(getString(R.string.login_failed_dialog_message));
view.setClickable(true);
@@ -184,7 +184,7 @@ private void remoteLogin(final View view, final String userName, final String pa
tryRemoteLogin(userName, password, new Listener() {
public void onEvent(LoginResponse loginResponse) {
if (loginResponse == SUCCESS) {
- remoteLoginWith(userName, password, loginResponse.payload());
+ remoteLoginWith(userName, loginResponse.payload());
} else {
if (loginResponse == null) {
showErrorDialog("Login failed. Unknown reason. Try Again");
@@ -254,14 +254,14 @@ private void hideKeyboard() {
inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), HIDE_NOT_ALWAYS);
}
- private void localLoginWith(String userName, String password) {
- context.userService().localLogin(userName, password);
+ private void localLoginWith(String userName) {
+ context.userService().localLogin(userName);
goToHome();
DrishtiSyncScheduler.startOnlyIfConnectedToNetwork(getApplicationContext());
}
- private void remoteLoginWith(String userName, String password, LoginResponseData userInfo) {
- context.userService().remoteLogin(userName, password, userInfo);
+ private void remoteLoginWith(String userName, LoginResponseData userInfo) {
+ context.userService().processLoginResponseDataForUser(userName, userInfo);
goToHome();
DrishtiSyncScheduler.startOnlyIfConnectedToNetwork(getApplicationContext());
}
diff --git a/opensrp-app/src/test/java/org/smartregister/TestSyncConfiguration.java b/opensrp-app/src/test/java/org/smartregister/TestSyncConfiguration.java
index 675cbc9ff..d018f9a55 100644
--- a/opensrp-app/src/test/java/org/smartregister/TestSyncConfiguration.java
+++ b/opensrp-app/src/test/java/org/smartregister/TestSyncConfiguration.java
@@ -1,5 +1,7 @@
package org.smartregister;
+import org.smartregister.view.activity.BaseLoginActivity;
+
import java.util.List;
/**
@@ -65,4 +67,9 @@ public String getOauthClientId() {
public String getOauthClientSecret() {
return "$om3cl13nt$3cret";
}
+
+ @Override
+ public Class extends BaseLoginActivity> getAuthenticationActivity() {
+ return BaseLoginActivity.class;
+ }
}
diff --git a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
index c54f82a8f..f97d7c222 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
@@ -155,14 +155,14 @@ public void testUrlCanBeAccessWithGivenCredentialsGivenEmptyResp() {
@Test
public void testfetchWithCredentialsFailsGivenWrongUrl() {
- Response resp = httpAgent.fetchWithCredentials("wrong.url");
+ Response resp = httpAgent.fetchWithCredentials("wrong.url","sample-test-token");
Assert.assertEquals(ResponseStatus.failure, resp.status());
}
@Test
public void testfetchWithCredentialsPassesGivenCorrectUrl() {
PowerMockito.mockStatic(Base64.class);
- Response resp = httpAgent.fetchWithCredentials("https://google.com");
+ Response resp = httpAgent.fetchWithCredentials("https://google.com","sample-test-token");
Assert.assertEquals(ResponseStatus.success, resp.status());
}
diff --git a/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java b/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java
index 315fdf46b..0830efa34 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java
@@ -209,10 +209,9 @@ public void logoutCurrentUser() {
when(allSharedPreferences.fetchRegisteredANM()).thenReturn("user X");
- userService.remoteLogin("user X", "password Y", userInfo);
+ userService.processLoginResponseDataForUser("user X", userInfo);
verify(allSettings).registerANM("user X");
- verify(session).setPassword("password Y");
}
@Test
diff --git a/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityTest.java b/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityTest.java
index 0c8edd581..8c20a2818 100644
--- a/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityTest.java
@@ -121,7 +121,7 @@ public void localLoginTest() {
Button login_button = activity.findViewById(R.id.login_loginButton);
login_button.performClick();
- Mockito.verify(userService, Mockito.atLeastOnce()).localLogin(anyString(), anyString());
+ Mockito.verify(userService, Mockito.atLeastOnce()).localLogin(anyString());
}
diff --git a/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityWithRemoteLoginTest.java b/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityWithRemoteLoginTest.java
index f06c432cb..754739d50 100644
--- a/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityWithRemoteLoginTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityWithRemoteLoginTest.java
@@ -136,7 +136,7 @@ public void remoteLoginTest() {
password.setText("password");
Button login_button = activity.findViewById(R.id.login_loginButton);
login_button.performClick();
- Mockito.verify(userService, Mockito.atLeastOnce()).remoteLogin(anyString(), anyString(), any(LoginResponseData.class));
+ Mockito.verify(userService, Mockito.atLeastOnce()).processLoginResponseDataForUser(anyString(), any(LoginResponseData.class));
destroyController();
diff --git a/opensrp-app/src/test/java/org/smartregister/view/fragment/BaseRegisterFragmentTest.java b/opensrp-app/src/test/java/org/smartregister/view/fragment/BaseRegisterFragmentTest.java
index 61eff77ad..2c2fac785 100644
--- a/opensrp-app/src/test/java/org/smartregister/view/fragment/BaseRegisterFragmentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/view/fragment/BaseRegisterFragmentTest.java
@@ -18,6 +18,7 @@
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
@@ -116,6 +117,9 @@ public void setUp() {
intent.putExtra(BaseRegisterFragment.TOOLBAR_TITLE, TEST_RANDOM_STRING);
activity = Robolectric.buildActivity(AppCompatActivity.class, intent).get();
+
+ AppCompatActivity activitySpy = Mockito.spy(activity);
+ Mockito.doReturn(activitySpy).when(baseRegisterFragment).getActivity();
}
@Test
From 428792a34c0d13d3d7b8c0867956edae0703298e Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Fri, 29 May 2020 04:42:56 +0300
Subject: [PATCH 14/70] Fix profile image loading Add add http request header
param enums
---
.../java/org/smartregister/AllConstants.java | 8 +++++++
.../account/AccountResponse.java | 4 ++++
.../smartregister/repository/AllSettings.java | 8 -------
.../org/smartregister/service/HTTPAgent.java | 23 ++++++++++---------
.../service/ImageUploadSyncService.java | 4 ++++
.../sync/CloudantSyncHandler.java | 8 +++----
.../util/OpenSRPImageLoader.java | 22 +++++++++---------
.../repository/AllSettingsTest.java | 8 -------
8 files changed, 43 insertions(+), 42 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/AllConstants.java b/opensrp-app/src/main/java/org/smartregister/AllConstants.java
index 038c8a088..e0f4339b1 100644
--- a/opensrp-app/src/main/java/org/smartregister/AllConstants.java
+++ b/opensrp-app/src/main/java/org/smartregister/AllConstants.java
@@ -498,4 +498,12 @@ public interface P2PDataTypes {
String FOREIGN_CLIENT = "ForeignClient";
String FOREIGN_EVENT = "ForeignEvent";
}
+ public static class HTTP_REQUEST_HEADERS {
+ public static String AUTHORIZATION = "Authorization";
+ }
+
+ public static class HTTP_REQUEST_AUTH_TOKEN_TYPE {
+ public static String BEARER = "Bearer";
+ public static String BASIC = "Basic";
+ }
}
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountResponse.java b/opensrp-app/src/main/java/org/smartregister/account/AccountResponse.java
index 9c7759cb9..1094df6c6 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountResponse.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountResponse.java
@@ -38,6 +38,10 @@ public int getStatus() {
return status;
}
+ public void setStatus(int status) {
+ this.status = status;
+ }
+
public AccountError getAccountError() {
return accountError;
}
diff --git a/opensrp-app/src/main/java/org/smartregister/repository/AllSettings.java b/opensrp-app/src/main/java/org/smartregister/repository/AllSettings.java
index b063c8819..df1c1e4ff 100644
--- a/opensrp-app/src/main/java/org/smartregister/repository/AllSettings.java
+++ b/opensrp-app/src/main/java/org/smartregister/repository/AllSettings.java
@@ -2,9 +2,7 @@
import org.smartregister.domain.Setting;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
public class AllSettings {
public static final String APPLIED_VILLAGE_FILTER_SETTING_KEY = "appliedVillageFilter";
@@ -71,12 +69,6 @@ public String fetchUserInformation() {
return settingsRepository.querySetting(USER_INFORMATION, "");
}
- public Map getAuthParams() {
- Map authParams = new HashMap();
- authParams.put("username", preferences.fetchRegisteredANM());
- return authParams;
- }
-
public void put(String key, String value) {
settingsRepository.updateSetting(key, value);
}
diff --git a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
index 9c8a2bdb8..d39360891 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
@@ -123,7 +123,7 @@ private HttpURLConnection initializeHttp(String requestURLPath, boolean setOauth
if (setOauthToken) {
AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
if (AccountHelper.getOauthAccountByType(authenticatorXml.getAccountType()) != null)
- urlConnection.setRequestProperty("Authorization", new StringBuilder("Bearer ").append(AccountHelper.getOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).toString());
+ urlConnection.setRequestProperty(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, new StringBuilder(AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BEARER + " ").append(AccountHelper.getOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).toString());
}
return urlConnection;
}
@@ -220,8 +220,8 @@ public LoginResponse urlCanBeAccessWithGivenCredentials(String requestURL, Strin
url = requestURL.replaceAll("\\s+", "");
urlConnection = initializeHttp(url, false);
- final String basicAuth = "Basic " + Base64.encodeToString((userName + ":" + password).getBytes(), Base64.NO_WRAP);
- urlConnection.setRequestProperty("Authorization", basicAuth);
+ final String basicAuth = AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BASIC + " " + Base64.encodeToString((userName + ":" + password).getBytes(), Base64.NO_WRAP);
+ urlConnection.setRequestProperty(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, basicAuth);
int statusCode = urlConnection.getResponseCode();
InputStream inputStream;
if (statusCode >= HttpStatus.SC_BAD_REQUEST)
@@ -267,7 +267,6 @@ public LoginResponse urlCanBeAccessWithGivenCredentials(String requestURL, Strin
public DownloadStatus downloadFromUrl(String url, String filename) {
- AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
Response status = downloadFromURL(url, filename);
Timber.d("downloading file name : %s and url %s", filename, url);
return status.payload();
@@ -278,7 +277,7 @@ public Response fetchWithCredentials(String requestURL, String accessTok
try {
HttpURLConnection urlConnection = initializeHttp(requestURL, false);
- urlConnection.setRequestProperty("Authorization", new StringBuilder("Bearer ").append(accessToken).toString());
+ urlConnection.setRequestProperty(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, new StringBuilder(AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BEARER + " ").append(accessToken).toString());
//If unauthorized, request new token
@@ -390,10 +389,10 @@ public String httpImagePost(String urlString, ProfileImage image) {
}
reader.close();
} else {
- Timber.d("SERVER RESPONSE %s Server returned non-OK status: %s :-", status, httpUrlConnection.getResponseMessage());
+ Timber.e("SERVER RESPONSE %s Server returned non-OK status: %s :-", status, httpUrlConnection.getResponseMessage());
BufferedReader reader = new BufferedReader(new InputStreamReader(httpUrlConnection.getErrorStream()));
while ((line = reader.readLine()) != null) {
- Timber.d("SERVER RESPONSE %s", line);
+ Timber.e("SERVER RESPONSE %s", line);
}
reader.close();
}
@@ -571,7 +570,7 @@ public AccountResponse oauth2authenticate(String username, String password, Stri
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("charset", "utf-8");
urlConnection.setRequestProperty("Content-Length", Integer.toString(postDataLength));
- urlConnection.setRequestProperty("Authorization", "Basic " + base64Auth);
+ urlConnection.setRequestProperty(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BASIC + " " + base64Auth);
urlConnection.setUseCaches(false);
outputStream = urlConnection.getOutputStream();
@@ -591,6 +590,7 @@ public AccountResponse oauth2authenticate(String username, String password, Stri
Timber.d("response String: %s using request url %s", responseString, url);
AccountResponse accountResponse = gson.fromJson(responseString, AccountResponse.class);
+ accountResponse.setStatus(statusCode);
return accountResponse;
} else {
@@ -634,7 +634,7 @@ public LoginResponse fetchUserDetails(String requestURL, String oauthAccessToken
url = requestURL.replaceAll("\\s+", "");
urlConnection = initializeHttp(url, false);
- urlConnection.setRequestProperty("Authorization", new StringBuilder("Bearer ").append(oauthAccessToken).toString());
+ urlConnection.setRequestProperty(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, new StringBuilder(AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BEARER + " ").append(oauthAccessToken).toString());
int statusCode = urlConnection.getResponseCode();
@@ -830,11 +830,11 @@ public AccountResponse oauth2authenticateRefreshToken(String refreshToken) {
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("charset", "utf-8");
urlConnection.setRequestProperty("Content-Length", Integer.toString(postDataLength));
- urlConnection.setRequestProperty("Authorization", "Basic " + base64Auth);
+ urlConnection.setRequestProperty(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BASIC + " " + base64Auth);
urlConnection.setUseCaches(false);
outputStream = urlConnection.getOutputStream();
- writer = new BufferedOutputStream(outputStream);//Bearer
+ writer = new BufferedOutputStream(outputStream);
writer.write(postData);
writer.flush();
@@ -849,6 +849,7 @@ public AccountResponse oauth2authenticateRefreshToken(String refreshToken) {
Timber.d("response String: %s using request url %s", responseString, tokenEndpointURL);
AccountResponse accountResponse = gson.fromJson(responseString, AccountResponse.class);
+ accountResponse.setStatus(statusCode);
return accountResponse;
} else {
diff --git a/opensrp-app/src/main/java/org/smartregister/service/ImageUploadSyncService.java b/opensrp-app/src/main/java/org/smartregister/service/ImageUploadSyncService.java
index 21d92aa1a..691146844 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/ImageUploadSyncService.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/ImageUploadSyncService.java
@@ -10,6 +10,8 @@
import java.util.List;
+import timber.log.Timber;
+
import static org.smartregister.util.Log.logError;
import static org.smartregister.util.Log.logInfo;
@@ -40,6 +42,8 @@ protected void onHandleIntent(Intent intent) {
+ AllConstants.PROFILE_IMAGES_UPLOAD_PATH, profileImages.get(i));
if (response.contains("success")) {
imageRepo.close(profileImages.get(i).getImageid());
+ } else {
+ Timber.e(new StringBuilder("Image Upload: could NOT upload image ID: ").append(profileImages.get(i).getImageid()).append(" PATH: ").append(profileImages.get(i).getFilepath()).toString());
}
}
} catch (Exception e) {
diff --git a/opensrp-app/src/main/java/org/smartregister/sync/CloudantSyncHandler.java b/opensrp-app/src/main/java/org/smartregister/sync/CloudantSyncHandler.java
index 099cf9c47..94a0047a5 100644
--- a/opensrp-app/src/main/java/org/smartregister/sync/CloudantSyncHandler.java
+++ b/opensrp-app/src/main/java/org/smartregister/sync/CloudantSyncHandler.java
@@ -358,8 +358,8 @@ public Boolean setReplicationFilter(JsonObject jsonObject, String designDocument
String authEncoded = getAuthorization();
if (authEncoded != null) {
- String basicAuth = "Basic " + authEncoded;
- conn.setRequestProperty("Authorization", basicAuth);
+ String basicAuth = AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BASIC + " " + authEncoded;
+ conn.setRequestProperty(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, basicAuth);
}
OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream());
@@ -392,8 +392,8 @@ public JsonObject getReplicationFiler(String designDocumentId) {
String authEncoded = getAuthorization();
if (authEncoded != null) {
- String basicAuth = "Basic " + authEncoded;
- get.setHeader("Authorization", basicAuth);
+ String basicAuth = AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BASIC + " " + authEncoded;
+ get.setHeader(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, basicAuth);
}
HttpResponse response = httpclient.execute(get);
diff --git a/opensrp-app/src/main/java/org/smartregister/util/OpenSRPImageLoader.java b/opensrp-app/src/main/java/org/smartregister/util/OpenSRPImageLoader.java
index 771d28dea..2ebede8e3 100644
--- a/opensrp-app/src/main/java/org/smartregister/util/OpenSRPImageLoader.java
+++ b/opensrp-app/src/main/java/org/smartregister/util/OpenSRPImageLoader.java
@@ -34,6 +34,7 @@
import org.smartregister.AllConstants;
import org.smartregister.CoreLibrary;
import org.smartregister.R;
+import org.smartregister.account.AccountHelper;
import org.smartregister.domain.ProfileImage;
import org.smartregister.repository.ImageRepository;
import org.smartregister.view.activity.DrishtiApplication;
@@ -159,8 +160,8 @@ private static RequestQueue newRequestQueue(Context context) {
public HttpResponse performRequest(Request> request, Map
headers) throws IOException, AuthFailureError {
- headers.putAll(
- CoreLibrary.getInstance().context().allSettings().getAuthParams());
+ String accessToken = AccountHelper.getOAuthToken(CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
+ headers.put(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, new StringBuilder(AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BEARER + " ").append(accessToken).toString());
return super.performRequest(request, headers);
}
@@ -172,11 +173,10 @@ public HttpResponse performRequest(Request> request, Map
HttpClientStack stack = new HttpClientStack(
AndroidHttpClient.newInstance(FileUtilities.getUserAgent(context))) {
@Override
- public HttpResponse performRequest(Request> request, Map
- headers) throws IOException, AuthFailureError {
+ public HttpResponse performRequest(Request> request, Map headers) throws IOException, AuthFailureError {
- headers.putAll(CoreLibrary.getInstance().context().allSettings().
- getAuthParams());
+ String accessToken = AccountHelper.getOAuthToken(CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
+ headers.put(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, new StringBuilder(AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BEARER + " ").append(accessToken).toString());
return super.performRequest(request, headers);
}
@@ -386,13 +386,13 @@ public static void saveStaticImageToDisk(String entityId, Bitmap image) {
}
} catch (FileNotFoundException e) {
- Timber.e( "Failed to save static image to disk");
+ Timber.e("Failed to save static image to disk");
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
- Timber.e( "Failed to close static images output stream after attempting"
+ Timber.e("Failed to close static images output stream after attempting"
+ " to write image");
}
}
@@ -515,7 +515,7 @@ public void getImageByClientId(String entityId, OpenSRPImageListener opensrpImag
}
} catch (Exception e) {
- Timber.e( e.getMessage(), e);
+ Timber.e(e.getMessage(), e);
}
}
@@ -537,7 +537,7 @@ public void get(final ProfileImage image, final OpenSRPImageListener opensrpImag
startAsyncTask(loadBitmap, filePathArray);
} catch (Exception e) {
- Timber.e( e.getMessage(), e);
+ Timber.e(e.getMessage(), e);
}
}
@@ -701,7 +701,7 @@ protected void onPostExecute(Bitmap result) {
} catch (Exception exc) {
- Timber.e( exc.getMessage(), exc);
+ Timber.e(exc.getMessage(), exc);
}
diff --git a/opensrp-app/src/test/java/org/smartregister/repository/AllSettingsTest.java b/opensrp-app/src/test/java/org/smartregister/repository/AllSettingsTest.java
index c3bca5f8a..9aab8c39d 100644
--- a/opensrp-app/src/test/java/org/smartregister/repository/AllSettingsTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/repository/AllSettingsTest.java
@@ -11,7 +11,6 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
@@ -117,13 +116,6 @@ public void assertSaveUserInformationCallsRepositoryUpdate() {
Mockito.verify(settingsRepository, Mockito.times(1)).updateSetting(Mockito.anyString(), Mockito.anyString());
}
- @Test
- public void assertGetAuthParamsReturnsUserNamePassword() {
- Mockito.when(allSharedPreferences.fetchRegisteredANM()).thenReturn("username");
- Map auth = allSettings.getAuthParams();
- Assert.assertEquals("username", auth.get("username"));
- }
-
@Test
public void testGetWithDefaultShouldReturnCorrectValue() {
SettingsRepository settingsRepository = spy(new SettingsRepository());
From ba23de2dd455fa9716bb1fa6445558f94f09c0f6 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Tue, 2 Jun 2020 08:56:17 +0300
Subject: [PATCH 15/70] Optimization, Refactor and unit testing - Optimize
upstream data processing for file uploads - Refactor for maintainability -
Unit testing
---
.../smartregister/account/AccountError.java | 9 +
.../account/AccountResponse.java | 4 +-
.../org/smartregister/service/HTTPAgent.java | 194 +++------
.../view/fragment/BaseRegisterFragment.java | 18 +-
.../smartregister/service/HTTPAgentTest.java | 378 +++++++++++++++++-
.../fragment/BaseRegisterFragmentTest.java | 28 +-
6 files changed, 481 insertions(+), 150 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountError.java b/opensrp-app/src/main/java/org/smartregister/account/AccountError.java
index 6b972cbfa..36057a684 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountError.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountError.java
@@ -1,5 +1,7 @@
package org.smartregister.account;
+import com.google.gson.annotations.SerializedName;
+
import java.io.Serializable;
/**
@@ -7,8 +9,11 @@
*/
public class AccountError implements Serializable {
+ @SerializedName("status_code")
private int statusCode;
private String error;
+ @SerializedName("error_description")
+ private String errorDescription;
public AccountError(int statusCode, String error) {
this.statusCode = statusCode;
@@ -23,4 +28,8 @@ public int getStatusCode() {
public String getError() {
return error;
}
+
+ public String getErrorDescription() {
+ return errorDescription;
+ }
}
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountResponse.java b/opensrp-app/src/main/java/org/smartregister/account/AccountResponse.java
index 1094df6c6..fd810d686 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountResponse.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountResponse.java
@@ -23,7 +23,7 @@ public class AccountResponse {
private Integer expiresIn;
@SerializedName("scope")
- private String Scope;
+ private String scope;
private int status;
@@ -63,7 +63,7 @@ public Integer getExpiresIn() {
}
public String getScope() {
- return Scope;
+ return scope;
}
public Integer getRefreshExpiresIn() {
diff --git a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
index d39360891..725240ae0 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
@@ -2,6 +2,7 @@
import android.content.Context;
import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
import android.util.Base64;
import android.util.Log;
@@ -31,7 +32,6 @@
import org.smartregister.domain.jsonmapping.LoginResponseData;
import org.smartregister.repository.AllSharedPreferences;
import org.smartregister.ssl.OpensrpSSLHelper;
-import org.smartregister.util.SyncUtils;
import org.smartregister.util.Utils;
import java.io.BufferedInputStream;
@@ -81,6 +81,9 @@
import static org.smartregister.util.HttpResponseUtil.getResponseBody;
public class HTTPAgent {
+
+ private static final int FILE_UPLOAD_CHUNK_SIZE_BYTES = 4096;
+
private Context context;
private AllSharedPreferences allSharedPreferences;
private DristhiConfiguration configuration;
@@ -96,7 +99,6 @@ public class HTTPAgent {
private static final String DETAILS_URL = "/user-details?anm-id=";
- private SyncUtils syncUtils;
public HTTPAgent(Context context, AllSharedPreferences
allSharedPreferences, DristhiConfiguration configuration) {
@@ -105,15 +107,15 @@ public HTTPAgent(Context context, AllSharedPreferences
this.configuration = configuration;
gson = new Gson();
gzipCompression = new GZIPCompression();
- syncUtils = new SyncUtils(context.getApplicationContext());
}
/**
* This method initializes httpurlconnection
*/
private HttpURLConnection initializeHttp(String requestURLPath, boolean setOauthToken) throws IOException {
- URL url = new URL(requestURLPath);
- HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
+
+ HttpURLConnection urlConnection = getHttpURLConnection(requestURLPath);
+
if (urlConnection instanceof HttpsURLConnection) {
OpensrpSSLHelper opensrpSSLHelper = new OpensrpSSLHelper(context, configuration);
((HttpsURLConnection) urlConnection).setSSLSocketFactory(opensrpSSLHelper.getSSLSocketFactory());
@@ -128,19 +130,21 @@ private HttpURLConnection initializeHttp(String requestURLPath, boolean setOauth
return urlConnection;
}
+ @VisibleForTesting
+ protected HttpURLConnection getHttpURLConnection(String requestURLPath) throws IOException {
+ URL url = new URL(requestURLPath);
+ return (HttpURLConnection) url.openConnection();
+ }
+
public Response fetch(String requestURLPath) {
try {
HttpURLConnection urlConnection = initializeHttp(requestURLPath, true);
- //If unauthorized, request new token
+ //If unauthorized invalidate cache of old token retry
if (HttpStatus.SC_UNAUTHORIZED == urlConnection.getResponseCode()) {
- AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
-
- String authToken = AccountHelper.getCachedOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
- if (authToken != null)
- AccountHelper.invalidateAuthToken(authenticatorXml.getAccountType(), authToken);
+ invalidateExpiredCachedAccessToken();
urlConnection = initializeHttp(requestURLPath, true);
@@ -154,20 +158,23 @@ public Response fetch(String requestURLPath) {
}
}
+ public void invalidateExpiredCachedAccessToken() {
+ AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
+ String authToken = AccountHelper.getCachedOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
+ if (authToken != null)
+ AccountHelper.invalidateAuthToken(authenticatorXml.getAccountType(), authToken);
+ }
+
public Response post(String postURLPath, String jsonPayload) {
HttpURLConnection urlConnection;
- AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
try {
urlConnection = generatePostRequest(postURLPath, jsonPayload);
- //If unauthorized, request new token
-
+ //If unauthorized invalidate cache of old token retry
if (HttpStatus.SC_UNAUTHORIZED == urlConnection.getResponseCode()) {
- String authToken = AccountHelper.getCachedOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
- if (authToken != null)
- AccountHelper.invalidateAuthToken(authenticatorXml.getAccountType(), authToken);
+ invalidateExpiredCachedAccessToken();
urlConnection = generatePostRequest(postURLPath, jsonPayload);
@@ -182,7 +189,8 @@ public Response post(String postURLPath, String jsonPayload) {
}
@NonNull
- private HttpURLConnection generatePostRequest(String postURLPath, String jsonPayload) throws IOException {
+ @VisibleForTesting
+ protected HttpURLConnection generatePostRequest(String postURLPath, String jsonPayload) throws IOException {
HttpURLConnection urlConnection;
urlConnection = initializeHttp(postURLPath, true);
@@ -191,10 +199,13 @@ private HttpURLConnection generatePostRequest(String postURLPath, String jsonPay
urlConnection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
urlConnection.setRequestProperty("Content-Encoding", "gzip");
+ byte[] content = gzipCompression.compress(jsonPayload);
+ urlConnection.setFixedLengthStreamingMode(content.length);
OutputStream os = urlConnection.getOutputStream();
BufferedOutputStream writer = new BufferedOutputStream(os);
- writer.write(gzipCompression.compress(jsonPayload));
+
+ writer.write(content);
writer.flush();
writer.close();
os.close();
@@ -279,8 +290,7 @@ public Response fetchWithCredentials(String requestURL, String accessTok
HttpURLConnection urlConnection = initializeHttp(requestURL, false);
urlConnection.setRequestProperty(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, new StringBuilder(AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BEARER + " ").append(accessToken).toString());
-
- //If unauthorized, request new token
+ //If unauthorized invalidate cache of old token retry
if (HttpStatus.SC_UNAUTHORIZED == urlConnection.getResponseCode()) {
AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
@@ -353,11 +363,12 @@ public String httpImagePost(String urlString, ProfileImage image) {
httpUrlConnection = initializeHttp(urlString, true);
+ httpUrlConnection.setRequestMethod("POST");
httpUrlConnection.setUseCaches(false);
httpUrlConnection.setDoInput(true);
httpUrlConnection.setDoOutput(true);
httpUrlConnection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
-
+ httpUrlConnection.setChunkedStreamingMode(FILE_UPLOAD_CHUNK_SIZE_BYTES);
outputStream = httpUrlConnection.getOutputStream();
writer = new PrintWriter(new OutputStreamWriter(outputStream, "UTF-8"), true);
@@ -424,7 +435,7 @@ private void attachImage(PrintWriter writer, ProfileImage image, OutputStream ou
writer.flush();
FileInputStream inputStream = new FileInputStream(uploadImageFile);
- byte[] buffer = new byte[4096];
+ byte[] buffer = new byte[FILE_UPLOAD_CHUNK_SIZE_BYTES];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
@@ -538,13 +549,14 @@ public void setReadTimeout(int readTimeout) {
this.readTimeout = readTimeout;
}
- public AccountResponse oauth2authenticate(String username, String password, String grantType, String tokenEndpointURL) {
+ public AccountResponse oauth2authenticateCore(StringBuilder requestParamBuilder, String grantType, String tokenEndpointURL) {
- AccountError accountError = null;
+
+ AccountError accountError;
HttpURLConnection urlConnection = null;
- String url = null;
OutputStream outputStream = null;
BufferedOutputStream writer = null;
+ InputStream inputStream;
try {
urlConnection = initializeHttp(tokenEndpointURL, false);
@@ -554,16 +566,14 @@ public AccountResponse oauth2authenticate(String username, String password, Stri
final String base64Auth = BaseEncoding.base64().encode(new String(clientId + ":" + clientSecret).getBytes());
- StringBuilder requestParamBuilder = new StringBuilder();
requestParamBuilder.append("&grant_type=").append(grantType);
- requestParamBuilder.append("&username=").append(username);
- requestParamBuilder.append("&password=").append(password);
requestParamBuilder.append("&client_id=").append(clientId);
requestParamBuilder.append("&client_secret=").append(clientSecret);
byte[] postData = requestParamBuilder.toString().getBytes(CharEncoding.UTF_8);
int postDataLength = postData.length;
+ urlConnection.setFixedLengthStreamingMode(postDataLength);
urlConnection.setDoOutput(true);
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setRequestMethod("POST");
@@ -579,7 +589,6 @@ public AccountResponse oauth2authenticate(String username, String password, Stri
writer.flush();
int statusCode = urlConnection.getResponseCode();
- InputStream inputStream;
if (statusCode >= HttpStatus.SC_BAD_REQUEST)
inputStream = urlConnection.getErrorStream();
else
@@ -587,7 +596,7 @@ public AccountResponse oauth2authenticate(String username, String password, Stri
String responseString = IOUtils.toString(inputStream);
if (statusCode == HttpStatus.SC_OK) {
- Timber.d("response String: %s using request url %s", responseString, url);
+ Timber.d("response String: %s using request url %s", responseString, tokenEndpointURL);
AccountResponse accountResponse = gson.fromJson(responseString, AccountResponse.class);
accountResponse.setStatus(statusCode);
@@ -600,17 +609,17 @@ public AccountResponse oauth2authenticate(String username, String password, Stri
}
} catch (MalformedURLException e) {
- Timber.e(e, "Failed to check credentials bad url %s", url);
+ Timber.e(e, "Failed to check credentials bad url %s", tokenEndpointURL);
accountError = new AccountError(0, MALFORMED_URL.name());
} catch (SocketTimeoutException e) {
- Timber.e(e, "SocketTimeoutException when authenticating %s", username);
+ Timber.e(e, "SocketTimeoutException when authenticating");
accountError = new AccountError(0, TIMEOUT.name());
- Timber.e(e, "Failed to check credentials of: %s using %s . Error: %s", username, url, e.toString());
+ Timber.e(e, "Failed to check credentials using %s . Error: %s", tokenEndpointURL, e.toString());
} catch (IOException e) {
- Timber.e(e, "Failed to check credentials of: %s using %s . Error: %s", username, url, e.toString());
+ Timber.e(e, "Failed to check credentials using %s . Error: %s", tokenEndpointURL, e.toString());
accountError = new AccountError(0, NO_INTERNET_CONNECTIVITY.name());
} finally {
@@ -626,6 +635,24 @@ public AccountResponse oauth2authenticate(String username, String password, Stri
}
+ public AccountResponse oauth2authenticate(String username, String password, String grantType, String tokenEndpointURL) {
+
+ StringBuilder requestParamBuilder = new StringBuilder();
+ requestParamBuilder.append("&username=").append(username);
+ requestParamBuilder.append("&password=").append(password);
+
+ return oauth2authenticateCore(requestParamBuilder, grantType, tokenEndpointURL);
+ }
+
+ public AccountResponse oauth2authenticateRefreshToken(String refreshToken) {
+
+ String tokenEndpointURL = allSharedPreferences.getPreferences().getString(AccountHelper.CONFIGURATION_CONSTANTS.TOKEN_ENDPOINT_URL, "");
+ StringBuilder requestParamBuilder = new StringBuilder();
+ requestParamBuilder.append("&refresh_token=").append(refreshToken);
+
+ return oauth2authenticateCore(requestParamBuilder, AccountHelper.OAUTH.GRANT_TYPE.REFRESH_TOKEN, tokenEndpointURL);
+ }
+
public LoginResponse fetchUserDetails(String requestURL, String oauthAccessToken) {
LoginResponse loginResponse = null;
String url = null;
@@ -768,15 +795,11 @@ public boolean verifyAuthorization() {
urlConnection = initializeHttp(baseUrl, true);
- AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
-
int statusCode = urlConnection.getResponseCode();
if (HttpStatus.SC_UNAUTHORIZED == urlConnection.getResponseCode()) {
- String authToken = AccountHelper.getCachedOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
- if (authToken != null)
- AccountHelper.invalidateAuthToken(authenticatorXml.getAccountType(), authToken);
+ invalidateExpiredCachedAccessToken();
urlConnection = initializeHttp(baseUrl, true);
@@ -797,93 +820,6 @@ public boolean verifyAuthorization() {
return true;
}
- public AccountResponse oauth2authenticateRefreshToken(String refreshToken) {
-
- AccountError accountError = null;
- HttpURLConnection urlConnection = null;
- OutputStream outputStream = null;
- BufferedOutputStream writer = null;
- InputStream inputStream = null;
-
- String clientId = CoreLibrary.getInstance().getSyncConfiguration().getOauthClientId();
- String clientSecret = CoreLibrary.getInstance().getSyncConfiguration().getOauthClientSecret();
- String tokenEndpointURL = null;
- try {
-
- tokenEndpointURL = CoreLibrary.getInstance().context().allSharedPreferences().getPreferences().getString(AccountHelper.CONFIGURATION_CONSTANTS.TOKEN_ENDPOINT_URL, "");
- urlConnection = initializeHttp(tokenEndpointURL, false);
-
- final String base64Auth = BaseEncoding.base64().encode(new String(clientId + ":" + clientSecret).getBytes());
-
- StringBuilder requestParamBuilder = new StringBuilder();
- requestParamBuilder.append("&grant_type=").append(AccountHelper.OAUTH.GRANT_TYPE.REFRESH_TOKEN);
- requestParamBuilder.append("&refresh_token=").append(refreshToken);
- requestParamBuilder.append("&client_id=").append(clientId);
- requestParamBuilder.append("&client_secret=").append(clientSecret);
-
- byte[] postData = requestParamBuilder.toString().getBytes(CharEncoding.UTF_8);
- int postDataLength = postData.length;
-
- urlConnection.setDoOutput(true);
- urlConnection.setInstanceFollowRedirects(false);
- urlConnection.setRequestMethod("POST");
- urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
- urlConnection.setRequestProperty("charset", "utf-8");
- urlConnection.setRequestProperty("Content-Length", Integer.toString(postDataLength));
- urlConnection.setRequestProperty(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BASIC + " " + base64Auth);
- urlConnection.setUseCaches(false);
-
- outputStream = urlConnection.getOutputStream();
- writer = new BufferedOutputStream(outputStream);
- writer.write(postData);
- writer.flush();
-
- int statusCode = urlConnection.getResponseCode();
- if (statusCode >= HttpStatus.SC_BAD_REQUEST)
- inputStream = urlConnection.getErrorStream();
- else
- inputStream = urlConnection.getInputStream();
- String responseString = IOUtils.toString(inputStream);
- if (statusCode == HttpStatus.SC_OK) {
-
- Timber.d("response String: %s using request url %s", responseString, tokenEndpointURL);
-
- AccountResponse accountResponse = gson.fromJson(responseString, AccountResponse.class);
- accountResponse.setStatus(statusCode);
- return accountResponse;
-
- } else {
-
- accountError = gson.fromJson(responseString, AccountError.class);
- return new AccountResponse(statusCode, accountError);
-
- }
- } catch (MalformedURLException e) {
- Timber.e(e, "Failed to check credentials bad url %s", tokenEndpointURL);
- accountError = new AccountError(0, MALFORMED_URL.name());
-
- } catch (SocketTimeoutException e) {
- Timber.e(e, "SocketTimeoutException when authenticating %s", refreshToken);
-
- accountError = new AccountError(0, TIMEOUT.name());
-
- Timber.e(e, "Failed to check credentials of: %s using %s . Error: %s", refreshToken, tokenEndpointURL, e.toString());
- } catch (IOException e) {
- Timber.e(e, "Failed to check credentials of: %s using %s . Error: %s", refreshToken, tokenEndpointURL, e.toString());
- accountError = new AccountError(0, NO_INTERNET_CONNECTIVITY.name());
-
- } finally {
- closeConnection(urlConnection);
- closeIOStream(writer);
- closeIOStream(outputStream);
-
- }
-
- //If we got here there was an issue with no server status code
- return new AccountResponse(0, accountError);
-
- }
-
public AccountConfiguration fetchOAuthConfiguration() {
String baseUrl = configuration.dristhiBaseURL();
@@ -898,8 +834,8 @@ public AccountConfiguration fetchOAuthConfiguration() {
InputStream inputStream = null;
try {
- URL url = new URL(baseUrl);
- urlConnection = (HttpURLConnection) url.openConnection();
+
+ urlConnection = getHttpURLConnection(baseUrl);
int statusCode = urlConnection.getResponseCode();
if (statusCode == HttpStatus.SC_OK) {
diff --git a/opensrp-app/src/main/java/org/smartregister/view/fragment/BaseRegisterFragment.java b/opensrp-app/src/main/java/org/smartregister/view/fragment/BaseRegisterFragment.java
index 08e268687..82f4fbafd 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/fragment/BaseRegisterFragment.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/fragment/BaseRegisterFragment.java
@@ -1,5 +1,6 @@
package org.smartregister.view.fragment;
+import android.content.Context;
import android.os.Bundle;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
@@ -393,23 +394,28 @@ public void onSyncComplete(FetchStatus fetchStatus) {
refreshSyncStatusViews(fetchStatus);
}
+ @VisibleForTesting
+ protected void showShortToast(Context context, String message) {
+ Utils.showShortToast(context, message);
+ }
+
@VisibleForTesting
protected void refreshSyncStatusViews(FetchStatus fetchStatus) {
if (isSyncing()) {
- Utils.showShortToast(getActivity(), getActivity().getString(R.string.syncing));
+ showShortToast(getActivity(), getActivity().getString(R.string.syncing));
Timber.i(getActivity().getString(R.string.syncing));
refreshSyncProgressSpinner();
} else {
if (fetchStatus != null) {
if (fetchStatus.equals(FetchStatus.fetchedFailed)) {
if (fetchStatus.displayValue().equals(ResponseErrorStatus.malformed_url.name())) {
- Utils.showShortToast(getActivity(), getActivity().getString(R.string.sync_failed_malformed_url));
+ showShortToast(getActivity(), getActivity().getString(R.string.sync_failed_malformed_url));
Timber.i(getActivity().getString(R.string.sync_failed_malformed_url));
} else if (fetchStatus.displayValue().equals(ResponseErrorStatus.timeout.name())) {
- Utils.showShortToast(getActivity(), getActivity().getString(R.string.sync_failed_timeout_error));
+ showShortToast(getActivity(), getActivity().getString(R.string.sync_failed_timeout_error));
Timber.i(getActivity().getString(R.string.sync_failed_timeout_error));
} else {
- Utils.showShortToast(getActivity(), getActivity().getString(R.string.sync_failed));
+ showShortToast(getActivity(), getActivity().getString(R.string.sync_failed));
Timber.i(getActivity().getString(R.string.sync_failed));
}
refreshSyncProgressSpinner();
@@ -417,11 +423,11 @@ protected void refreshSyncStatusViews(FetchStatus fetchStatus) {
setRefreshList(true);
renderView();
- Utils.showShortToast(getActivity(), getActivity().getString(R.string.sync_complete));
+ showShortToast(getActivity(), getActivity().getString(R.string.sync_complete));
Timber.i(getActivity().getString(R.string.sync_complete));
} else if (fetchStatus.equals(FetchStatus.noConnection)) {
- Utils.showShortToast(getActivity(), getActivity().getString(R.string.sync_failed_no_internet));
+ showShortToast(getActivity(), getActivity().getString(R.string.sync_failed_no_internet));
Timber.i(getActivity().getString(R.string.sync_failed_no_internet));
refreshSyncProgressSpinner();
} else {
diff --git a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
index f97d7c222..9782a9e75 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
@@ -2,8 +2,12 @@
import android.accounts.Account;
import android.accounts.AccountManager;
+import android.content.SharedPreferences;
import android.util.Base64;
+import com.google.common.io.BaseEncoding;
+
+import org.apache.commons.io.IOUtils;
import org.json.JSONObject;
import org.junit.Assert;
import org.junit.Before;
@@ -12,31 +16,43 @@
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
+import org.smartregister.AllConstants;
import org.smartregister.Context;
import org.smartregister.CoreLibrary;
import org.smartregister.DristhiConfiguration;
import org.smartregister.SyncConfiguration;
import org.smartregister.account.AccountAuthenticatorXml;
+import org.smartregister.account.AccountConfiguration;
import org.smartregister.account.AccountHelper;
+import org.smartregister.account.AccountResponse;
import org.smartregister.domain.LoginResponse;
import org.smartregister.domain.ProfileImage;
import org.smartregister.domain.Response;
import org.smartregister.domain.ResponseStatus;
-import org.smartregister.repository.AllSettings;
import org.smartregister.repository.AllSharedPreferences;
import java.io.File;
import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.SocketTimeoutException;
+import java.net.URL;
import java.util.HashMap;
+import java.util.List;
@RunWith(PowerMockRunner.class)
-@PrepareForTest({Base64.class, File.class, FileInputStream.class, Context.class, AccountHelper.class, CoreLibrary.class})
+@PrepareForTest({Base64.class, File.class, FileInputStream.class, Context.class, AccountHelper.class, CoreLibrary.class, IOUtils.class})
public class HTTPAgentTest {
@Mock
private android.content.Context context;
@@ -45,10 +61,10 @@ public class HTTPAgentTest {
private Context openSrpContext;
@Mock
- private AllSettings allSettings;
+ private AllSharedPreferences allSharedPreferences;
@Mock
- private AllSharedPreferences allSharedPreferences;
+ private SharedPreferences sharedPreferences;
@Mock
private DristhiConfiguration dristhiConfiguration;
@@ -71,10 +87,38 @@ public class HTTPAgentTest {
@Mock
private SyncConfiguration syncConfiguration;
+ @Mock
+ private HttpURLConnection httpURLConnection;
+
+ @Mock
+ private OutputStream outputStream;
+
+ @Mock
+ private InputStream inputStream;
+
+ @Mock
+ private InputStream errorStream;
+
@Rule
private TemporaryFolder folder = new TemporaryFolder();
private HTTPAgent httpAgent;
+ private static final String TEST_USERNAME = "demo";
+ private static final String TEST_PASSWORD = "password";
+ private static final String TEST_TOKEN_ENDPOINT = "https://my-server.com/oauth/token";
+ private static final String SECURE_RESOURCE_ENDPOINT = "https://my-server.com/my/secure/resource";
+ private static final String KEYClOAK_CONFIGURATION_ENDPOINT = "https://my-server.com/rest/config/keycloak";
+
+ private final String SAMPLE_TEST_TOKEN = "sample-test-token";
+ private final String SAMPLE_REFRESH_TOKEN = "sample-refresh-token";
+ private static final String TEST_CLIENT_ID = "my-client-id";
+ private static final String TEST_CLIENT_SECRET = "my-client-secret";
+ private static final String TOKEN_REQUEST_SERVER_RESPONSE = "{\r\naccess_token:\"1r9A8zi5E3r@Zz\",\r\ntoken_type: \"bearer\",\r\nrefresh_token: \"text_token\",\r\nexpires_in: 3600,\r\nrefresh_expires_in: 36000,\r\nscope: \"read write trust\"\r\n\r\n}";
+ private static final String TOKEN_BAD_REQUEST_SERVER_RESPONSE = "{status_code:400,\"error\":\"invalid_grant\",\"error_description\":\"Code not valid\"}";
+ private static final String TOKEN_INTERNAL_SERVER_RESPONSE = "{status_code:500,\"error\":\"internal server error\",\"error_description\":\"Oops, something went wrong\"}";
+ private static final String OAUTH_CONFIGURATION_SERVER_RESPONSE = "{\"issuer\":\"https://my-server.com/oauth/issuer\",\r\n\"authorization_endpoint\": \"https://my-server.com/oauth/auth\",\r\n\"token_endpoint\": \"https://my-server.com/oauth/token\",\r\n\"grant_types_supported\":[\"authorization code\",\"implicit\",\"password\"]\r\n}";
+ private static final String FETCH_DATA_REQUEST_SERVER_RESPONSE = "{status:{\"response_status\":\"success\"},payload: \"My secure resources from the server\"\r\n\r\n}";
+ private static final String SAMPLE_POST_REQUEST_PAYLOAD = "{\"payload\":\"My POST Payload\"}";
@Before
public void setUp() {
@@ -97,6 +141,8 @@ public void setUp() {
PowerMockito.when(AccountHelper.getOauthAccountByType(accountAuthenticatorXml.getAccountType())).thenReturn(account);
httpAgent = new HTTPAgent(context, allSharedPreferences, dristhiConfiguration);
+ httpAgent.setConnectTimeout(60000);
+ httpAgent.setReadTimeout(60000);
}
@Test
@@ -155,14 +201,14 @@ public void testUrlCanBeAccessWithGivenCredentialsGivenEmptyResp() {
@Test
public void testfetchWithCredentialsFailsGivenWrongUrl() {
- Response resp = httpAgent.fetchWithCredentials("wrong.url","sample-test-token");
+ Response resp = httpAgent.fetchWithCredentials("wrong.url", SAMPLE_TEST_TOKEN);
Assert.assertEquals(ResponseStatus.failure, resp.status());
}
@Test
public void testfetchWithCredentialsPassesGivenCorrectUrl() {
PowerMockito.mockStatic(Base64.class);
- Response resp = httpAgent.fetchWithCredentials("https://google.com","sample-test-token");
+ Response resp = httpAgent.fetchWithCredentials("https://google.com", SAMPLE_TEST_TOKEN);
Assert.assertEquals(ResponseStatus.success, resp.status());
}
@@ -194,4 +240,324 @@ public void testPostWithJsonResponse() {
Response resp = httpAgent.postWithJsonResponse("http://www.mocky.io/v2/5e54d9333100006300eb33a8", jObject.toString());
Assert.assertEquals(ResponseStatus.success, resp.status());
}
+
+
+ @Test
+ public void testOauth2authenticateCreatesUrlConnectionWithCorrectParameters() throws Exception {
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(TEST_TOKEN_ENDPOINT);
+ Mockito.doReturn(TEST_CLIENT_ID).when(syncConfiguration).getOauthClientId();
+ Mockito.doReturn(TEST_CLIENT_SECRET).when(syncConfiguration).getOauthClientSecret();
+
+ Mockito.doReturn(outputStream).when(httpURLConnection).getOutputStream();
+ Mockito.doReturn(inputStream).when(httpURLConnection).getInputStream();
+ Mockito.doReturn(HttpURLConnection.HTTP_OK).when(httpURLConnection).getResponseCode();
+
+ PowerMockito.mockStatic(IOUtils.class);
+ PowerMockito.when(IOUtils.toString(inputStream)).thenReturn(TOKEN_REQUEST_SERVER_RESPONSE);
+
+
+ AccountResponse accountResponse = httpAgentSpy.oauth2authenticate(TEST_USERNAME, TEST_PASSWORD, AccountHelper.OAUTH.GRANT_TYPE.PASSWORD, TEST_TOKEN_ENDPOINT);
+
+ Assert.assertNotNull(accountResponse);
+ Assert.assertEquals(200, accountResponse.getStatus());
+ Assert.assertEquals("1r9A8zi5E3r@Zz", accountResponse.getAccessToken());
+ Assert.assertEquals("bearer", accountResponse.getTokenType());
+ Assert.assertEquals("text_token", accountResponse.getRefreshToken());
+ Assert.assertEquals(Integer.valueOf("3600"), accountResponse.getExpiresIn());
+ Assert.assertEquals(Integer.valueOf("36000"), accountResponse.getRefreshExpiresIn());
+ Assert.assertEquals("read write trust", accountResponse.getScope());
+
+
+ Mockito.verify(httpURLConnection).setConnectTimeout(60000);
+ Mockito.verify(httpURLConnection).setReadTimeout(60000);
+
+ String requestParams = "&grant_type=" + AccountHelper.OAUTH.GRANT_TYPE.PASSWORD + "&username=" + TEST_USERNAME + "&password=" + TEST_PASSWORD + "&client_id=" + TEST_CLIENT_ID + "&client_secret=" + TEST_CLIENT_SECRET;
+
+ Mockito.verify(httpURLConnection).setFixedLengthStreamingMode(requestParams.getBytes().length);
+ Mockito.verify(httpURLConnection).setDoOutput(true);
+ Mockito.verify(httpURLConnection).setInstanceFollowRedirects(false);
+ Mockito.verify(httpURLConnection).setRequestMethod("POST");
+ Mockito.verify(httpURLConnection).setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+ Mockito.verify(httpURLConnection).setRequestProperty("charset", "utf-8");
+ Mockito.verify(httpURLConnection).setRequestProperty(ArgumentMatchers.eq("Content-Length"), ArgumentMatchers.anyString());
+ Mockito.verify(httpURLConnection).setUseCaches(false);
+ final String base64Auth = BaseEncoding.base64().encode(new String(TEST_CLIENT_ID + ":" + TEST_CLIENT_SECRET).getBytes());
+ Mockito.verify(httpURLConnection).setRequestProperty(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BASIC + " " + base64Auth);
+ Mockito.verify(httpURLConnection).setInstanceFollowRedirects(false);
+
+ }
+
+ @Test
+ public void testOauth2authenticateReturnsCorrectResponseForBadRequest() throws Exception {
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(TEST_TOKEN_ENDPOINT);
+ Mockito.doReturn(TEST_CLIENT_ID).when(syncConfiguration).getOauthClientId();
+ Mockito.doReturn(TEST_CLIENT_SECRET).when(syncConfiguration).getOauthClientSecret();
+
+ Mockito.doReturn(outputStream).when(httpURLConnection).getOutputStream();
+ Mockito.doReturn(inputStream).when(httpURLConnection).getInputStream();
+ Mockito.doReturn(errorStream).when(httpURLConnection).getErrorStream();
+ Mockito.doReturn(HttpURLConnection.HTTP_BAD_REQUEST).when(httpURLConnection).getResponseCode();
+
+ PowerMockito.mockStatic(IOUtils.class);
+ PowerMockito.when(IOUtils.toString(errorStream)).thenReturn(TOKEN_BAD_REQUEST_SERVER_RESPONSE);
+
+ AccountResponse accountResponse = httpAgentSpy.oauth2authenticate(TEST_USERNAME, TEST_PASSWORD, AccountHelper.OAUTH.GRANT_TYPE.PASSWORD, TEST_TOKEN_ENDPOINT);
+
+ Assert.assertNotNull(accountResponse);
+ Assert.assertEquals(HttpURLConnection.HTTP_BAD_REQUEST, accountResponse.getStatus());
+ Assert.assertNotNull(accountResponse.getAccountError());
+ Assert.assertEquals(HttpURLConnection.HTTP_BAD_REQUEST, accountResponse.getAccountError().getStatusCode());
+ Assert.assertEquals("Code not valid", accountResponse.getAccountError().getErrorDescription());
+ Assert.assertEquals("invalid_grant", accountResponse.getAccountError().getError());
+
+ }
+
+ @Test
+ public void testOauth2authenticateReturnsCorrectAccountErrorResponseForMalformedURL() throws Exception {
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(TEST_TOKEN_ENDPOINT);
+ Mockito.doReturn(TEST_CLIENT_ID).when(syncConfiguration).getOauthClientId();
+ Mockito.doReturn(TEST_CLIENT_SECRET).when(syncConfiguration).getOauthClientSecret();
+
+ Mockito.doReturn(outputStream).when(httpURLConnection).getOutputStream();
+ Mockito.doReturn(inputStream).when(httpURLConnection).getInputStream();
+
+ Mockito.doThrow(new MalformedURLException()).when(httpURLConnection).getResponseCode();
+
+ AccountResponse response = httpAgentSpy.oauth2authenticate(TEST_USERNAME, TEST_PASSWORD, AccountHelper.OAUTH.GRANT_TYPE.PASSWORD, TEST_TOKEN_ENDPOINT);
+ Assert.assertNotNull(response);
+ Assert.assertNotNull(response.getAccountError());
+ Assert.assertEquals(0, response.getAccountError().getStatusCode());
+ Assert.assertEquals(LoginResponse.MALFORMED_URL.name(), response.getAccountError().getError());
+
+ }
+
+ @Test
+ public void testOauth2authenticateReturnsCorrectAccountErrorResponseForSocketTimeout() throws Exception {
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(TEST_TOKEN_ENDPOINT);
+ Mockito.doReturn(TEST_CLIENT_ID).when(syncConfiguration).getOauthClientId();
+ Mockito.doReturn(TEST_CLIENT_SECRET).when(syncConfiguration).getOauthClientSecret();
+
+ Mockito.doReturn(outputStream).when(httpURLConnection).getOutputStream();
+ Mockito.doReturn(inputStream).when(httpURLConnection).getInputStream();
+
+ Mockito.doThrow(new SocketTimeoutException()).when(httpURLConnection).getResponseCode();
+
+ AccountResponse response = httpAgentSpy.oauth2authenticate(TEST_USERNAME, TEST_PASSWORD, AccountHelper.OAUTH.GRANT_TYPE.PASSWORD, TEST_TOKEN_ENDPOINT);
+ Assert.assertNotNull(response);
+ Assert.assertNotNull(response.getAccountError());
+ Assert.assertEquals(0, response.getAccountError().getStatusCode());
+ Assert.assertEquals(LoginResponse.TIMEOUT.name(), response.getAccountError().getError());
+
+ }
+
+ @Test
+ public void testOauth2authenticateReturnsCorrectAccountErrorResponseForIOException() throws Exception {
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(TEST_TOKEN_ENDPOINT);
+ Mockito.doReturn(TEST_CLIENT_ID).when(syncConfiguration).getOauthClientId();
+ Mockito.doReturn(TEST_CLIENT_SECRET).when(syncConfiguration).getOauthClientSecret();
+
+ Mockito.doReturn(outputStream).when(httpURLConnection).getOutputStream();
+ Mockito.doReturn(inputStream).when(httpURLConnection).getInputStream();
+
+ Mockito.doThrow(new IOException()).when(httpURLConnection).getResponseCode();
+
+ AccountResponse response = httpAgentSpy.oauth2authenticate(TEST_USERNAME, TEST_PASSWORD, AccountHelper.OAUTH.GRANT_TYPE.PASSWORD, TEST_TOKEN_ENDPOINT);
+ Assert.assertNotNull(response);
+ Assert.assertNotNull(response.getAccountError());
+ Assert.assertEquals(0, response.getAccountError().getStatusCode());
+ Assert.assertEquals(LoginResponse.NO_INTERNET_CONNECTIVITY.name(), response.getAccountError().getError());
+
+ }
+
+ @Test
+ public void testOauth2authenticateReturnsNonNullAccountErrorResponseForRandomException() throws Exception {
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(TEST_TOKEN_ENDPOINT);
+ Mockito.doReturn(TEST_CLIENT_ID).when(syncConfiguration).getOauthClientId();
+ Mockito.doReturn(TEST_CLIENT_SECRET).when(syncConfiguration).getOauthClientSecret();
+
+ Mockito.doReturn(errorStream).when(httpURLConnection).getErrorStream();
+ Mockito.doReturn(outputStream).when(httpURLConnection).getOutputStream();
+
+ Mockito.doReturn(HttpURLConnection.HTTP_INTERNAL_ERROR).when(httpURLConnection).getResponseCode();
+
+ PowerMockito.mockStatic(IOUtils.class);
+ PowerMockito.when(IOUtils.toString(errorStream)).thenReturn(TOKEN_INTERNAL_SERVER_RESPONSE);
+
+ AccountResponse response = httpAgentSpy.oauth2authenticate(TEST_USERNAME, TEST_PASSWORD, AccountHelper.OAUTH.GRANT_TYPE.PASSWORD, TEST_TOKEN_ENDPOINT);
+ Assert.assertNotNull(response);
+ Assert.assertNotNull(response.getAccountError());
+ Assert.assertEquals(HttpURLConnection.HTTP_INTERNAL_ERROR, response.getAccountError().getStatusCode());
+ Assert.assertNotNull(response.getAccountError().getError());
+ Assert.assertEquals("Oops, something went wrong", response.getAccountError().getErrorDescription());
+ Assert.assertEquals("internal server error", response.getAccountError().getError());
+
+ }
+
+ @Test
+ public void testFetchOAuthConfigurationProcessesConfigurationResponseCorrectly() throws Exception {
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn("https://my-server.com/").when(dristhiConfiguration).dristhiBaseURL();
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(KEYClOAK_CONFIGURATION_ENDPOINT);
+
+ Mockito.doReturn(inputStream).when(httpURLConnection).getInputStream();
+ Mockito.doReturn(HttpURLConnection.HTTP_OK).when(httpURLConnection).getResponseCode();
+
+ PowerMockito.mockStatic(IOUtils.class);
+ PowerMockito.when(IOUtils.toString(inputStream)).thenReturn(OAUTH_CONFIGURATION_SERVER_RESPONSE);
+
+ AccountConfiguration accountConfiguration = httpAgentSpy.fetchOAuthConfiguration();
+ Assert.assertNotNull(accountConfiguration);
+ Assert.assertEquals("https://my-server.com/oauth/auth", accountConfiguration.getAuthorizationEndpoint());
+ Assert.assertEquals("https://my-server.com/oauth/issuer", accountConfiguration.getIssuerEndpoint());
+ Assert.assertEquals(TEST_TOKEN_ENDPOINT, accountConfiguration.getTokenEndpoint());
+
+ List grantTypes = accountConfiguration.getGrantTypesSupported();
+ Assert.assertNotNull(grantTypes);
+ Assert.assertEquals("authorization code", grantTypes.get(0));
+ Assert.assertEquals("implicit", grantTypes.get(1));
+ Assert.assertEquals("password", grantTypes.get(2));
+ }
+
+ @Test
+ public void testFetchInvalidatesCacheIfUnauthorizedAndReturnsCorrectResponse() throws Exception {
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(SECURE_RESOURCE_ENDPOINT);
+ Mockito.doReturn(errorStream).when(httpURLConnection).getErrorStream();
+ Mockito.doReturn(HttpURLConnection.HTTP_UNAUTHORIZED).when(httpURLConnection).getResponseCode();
+
+ PowerMockito.mockStatic(IOUtils.class);
+ PowerMockito.when(IOUtils.toString(errorStream)).thenReturn(FETCH_DATA_REQUEST_SERVER_RESPONSE);
+
+ PowerMockito.mockStatic(AccountHelper.class);
+ PowerMockito.when(AccountHelper.getCachedOAuthToken(accountAuthenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).thenReturn(SAMPLE_TEST_TOKEN);
+
+ Response response = httpAgentSpy.fetch(SECURE_RESOURCE_ENDPOINT);
+ Assert.assertNotNull(response);
+ Assert.assertEquals(ResponseStatus.valueOf("success"), response.status());
+ Assert.assertEquals(FETCH_DATA_REQUEST_SERVER_RESPONSE, response.payload());
+
+ PowerMockito.verifyStatic(AccountHelper.class);
+ AccountHelper.invalidateAuthToken(accountAuthenticatorXml.getAccountType(), SAMPLE_TEST_TOKEN);
+
+ }
+
+ @Test
+ public void testPostInvokesInvalidateCacheIfUnauthorizedOnFirstAttempt() throws Exception {
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(SECURE_RESOURCE_ENDPOINT);
+ Mockito.doReturn(errorStream).when(httpURLConnection).getErrorStream();
+ Mockito.doReturn(HttpURLConnection.HTTP_UNAUTHORIZED).when(httpURLConnection).getResponseCode();
+
+ PowerMockito.mockStatic(IOUtils.class);
+ PowerMockito.when(IOUtils.toString(errorStream)).thenReturn(FETCH_DATA_REQUEST_SERVER_RESPONSE);
+
+ PowerMockito.mockStatic(AccountHelper.class);
+ PowerMockito.when(AccountHelper.getCachedOAuthToken(accountAuthenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).thenReturn(SAMPLE_TEST_TOKEN);
+
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).generatePostRequest(SECURE_RESOURCE_ENDPOINT, SAMPLE_POST_REQUEST_PAYLOAD);
+
+ httpAgentSpy.post(SECURE_RESOURCE_ENDPOINT, SAMPLE_POST_REQUEST_PAYLOAD);
+
+ Mockito.verify(httpAgentSpy).invalidateExpiredCachedAccessToken();
+
+ }
+
+ @Test
+ public void testFetchWithCredentialsInvokesInvalidateCacheIfUnauthorizedOnFirstAttempt() throws Exception {
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(SECURE_RESOURCE_ENDPOINT);
+ Mockito.doReturn(errorStream).when(httpURLConnection).getErrorStream();
+ Mockito.doReturn(HttpURLConnection.HTTP_UNAUTHORIZED).when(httpURLConnection).getResponseCode();
+
+ PowerMockito.mockStatic(IOUtils.class);
+ PowerMockito.when(IOUtils.toString(errorStream)).thenReturn(FETCH_DATA_REQUEST_SERVER_RESPONSE);
+
+ PowerMockito.mockStatic(AccountHelper.class);
+ PowerMockito.when(AccountHelper.getCachedOAuthToken(accountAuthenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).thenReturn(SAMPLE_TEST_TOKEN);
+
+ Response response = httpAgentSpy.fetchWithCredentials(SECURE_RESOURCE_ENDPOINT, SAMPLE_TEST_TOKEN);
+ Assert.assertNotNull(response);
+
+ PowerMockito.verifyStatic(AccountHelper.class);
+ AccountHelper.invalidateAuthToken(accountAuthenticatorXml.getAccountType(), SAMPLE_TEST_TOKEN);
+
+ }
+
+ @Test
+ public void testOauth2authenticateRefreshTokenInvokesOauth2authenticateCoreWithCorrectParams() throws Exception {
+
+ Mockito.doReturn(sharedPreferences).when(allSharedPreferences).getPreferences();
+ Mockito.doReturn(TEST_TOKEN_ENDPOINT).when(sharedPreferences).getString(AccountHelper.CONFIGURATION_CONSTANTS.TOKEN_ENDPOINT_URL, "");
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ AccountResponse accountResponse = Mockito.mock(AccountResponse.class);
+
+ Mockito.doReturn(accountResponse).when(httpAgentSpy).oauth2authenticateCore(ArgumentMatchers.any(StringBuilder.class), ArgumentMatchers.anyString(), ArgumentMatchers.anyString());
+
+ ArgumentCaptor requestParamStringBuilder = ArgumentCaptor.forClass(StringBuilder.class);
+ ArgumentCaptor grantType = ArgumentCaptor.forClass(String.class);
+ ArgumentCaptor tokenEndPoint = ArgumentCaptor.forClass(String.class);
+
+ httpAgentSpy.oauth2authenticateRefreshToken(SAMPLE_REFRESH_TOKEN);
+
+ Mockito.verify(httpAgentSpy).oauth2authenticateCore(requestParamStringBuilder.capture(), grantType.capture(), tokenEndPoint.capture());
+
+ String capturedRefreshTokenRequestValue = requestParamStringBuilder.getValue().toString();
+ String capturedGrantTypeValue = grantType.getValue();
+ String capturedTokenEndpointValue = tokenEndPoint.getValue();
+
+ Assert.assertEquals("&refresh_token=" + SAMPLE_REFRESH_TOKEN, capturedRefreshTokenRequestValue);
+
+ Assert.assertEquals(AccountHelper.OAUTH.GRANT_TYPE.REFRESH_TOKEN, capturedGrantTypeValue);
+ Assert.assertEquals(TEST_TOKEN_ENDPOINT, capturedTokenEndpointValue);
+
+
+ }
}
diff --git a/opensrp-app/src/test/java/org/smartregister/view/fragment/BaseRegisterFragmentTest.java b/opensrp-app/src/test/java/org/smartregister/view/fragment/BaseRegisterFragmentTest.java
index 2c2fac785..d4daea9bc 100644
--- a/opensrp-app/src/test/java/org/smartregister/view/fragment/BaseRegisterFragmentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/view/fragment/BaseRegisterFragmentTest.java
@@ -26,22 +26,27 @@
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import org.powermock.core.classloader.annotations.PrepareForTest;
import org.robolectric.Robolectric;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
import org.smartregister.AllConstants;
import org.smartregister.BaseUnitTest;
import org.smartregister.Context;
+import org.smartregister.CoreLibrary;
import org.smartregister.R;
import org.smartregister.cursoradapter.RecyclerViewPaginatedAdapter;
import org.smartregister.domain.FetchStatus;
import org.smartregister.domain.ResponseErrorStatus;
+import org.smartregister.util.AppProperties;
import org.smartregister.view.activity.SecuredNativeSmartRegisterActivity;
import org.smartregister.view.contract.BaseRegisterFragmentContract;
/**
* Created by ndegwamartin on 2020-04-28.
*/
+
+@PrepareForTest({CoreLibrary.class})
public class BaseRegisterFragmentTest extends BaseUnitTest {
private BaseRegisterFragment baseRegisterFragment;
@@ -102,6 +107,9 @@ public class BaseRegisterFragmentTest extends BaseUnitTest {
@Mock
private Resources resources;
+ @Mock
+ private AppProperties appProperties;
+
@Before
public void setUp() {
@@ -120,6 +128,13 @@ public void setUp() {
AppCompatActivity activitySpy = Mockito.spy(activity);
Mockito.doReturn(activitySpy).when(baseRegisterFragment).getActivity();
+
+ CoreLibrary.init(opensrpContext);
+ Mockito.doReturn(appProperties).when(opensrpContext).getAppProperties();
+
+ Mockito.doReturn(opensrpContext).when(baseRegisterFragment).context();
+
+ Mockito.doNothing().when(baseRegisterFragment).showShortToast(ArgumentMatchers.eq(activitySpy), ArgumentMatchers.anyString());
}
@Test
@@ -227,7 +242,6 @@ public void setSearchTermInitsCorrectValue() {
}
@Test
- @Ignore
public void assertOnQRCodeSucessfullyScannedInvokesFilterWithCorrectParams() {
String OPENSRP_ID = "8232-372-8L";
@@ -402,15 +416,15 @@ public void testRefreshSyncStatusViewsWithSyncingTrue() {
View parentLayout = LayoutInflater.from(RuntimeEnvironment.application.getApplicationContext()).inflate(R.layout.fragment_base_register, null, false);
Mockito.doReturn(parentLayout).when(layoutInflater).inflate(R.layout.fragment_base_register, container, false);
- ProgressBar syncProgressBar = parentLayout.findViewById(R.id.sync_progress_bar);
+ ProgressBar syncProgressBar = parentLayout.findViewById(R.id.sync_progress_bar);
ImageView syncButton = parentLayout.findViewById(R.id.sync_refresh);
ReflectionHelpers.setField(baseRegisterFragment, "syncProgressBar", syncProgressBar);
ReflectionHelpers.setField(baseRegisterFragment, "syncButton", syncButton);
baseRegisterFragment.refreshSyncStatusViews(FetchStatus.fetchStarted);
Mockito.verify(baseRegisterFragment).refreshSyncProgressSpinner();
- Assert.assertEquals(View.VISIBLE,syncProgressBar.getVisibility());
- Assert.assertEquals(View.GONE,syncButton.getVisibility());
+ Assert.assertEquals(View.VISIBLE, syncProgressBar.getVisibility());
+ Assert.assertEquals(View.GONE, syncButton.getVisibility());
}
@Test
@@ -485,7 +499,7 @@ public void testRefreshSyncStatusViewsWithSyncingFalseFetchStatusNoConnection()
View Layout = LayoutInflater.from(RuntimeEnvironment.application.getApplicationContext()).inflate(R.layout.fragment_base_register, null, false);
Mockito.doReturn(Layout).when(layoutInflater).inflate(R.layout.fragment_base_register, container, false);
- ProgressBar progressBar = Layout.findViewById(R.id.sync_progress_bar);
+ ProgressBar progressBar = Layout.findViewById(R.id.sync_progress_bar);
ImageView imageView = Layout.findViewById(R.id.sync_refresh);
ReflectionHelpers.setField(baseRegisterFragment, "syncProgressBar", progressBar);
@@ -493,8 +507,8 @@ public void testRefreshSyncStatusViewsWithSyncingFalseFetchStatusNoConnection()
baseRegisterFragment.refreshSyncStatusViews(FetchStatus.noConnection);
Mockito.verify(baseRegisterFragment).refreshSyncProgressSpinner();
- Assert.assertEquals(View.GONE,progressBar.getVisibility());
- Assert.assertEquals(View.VISIBLE,imageView.getVisibility());
+ Assert.assertEquals(View.GONE, progressBar.getVisibility());
+ Assert.assertEquals(View.VISIBLE, imageView.getVisibility());
}
@Test
From a7477ef0323ef65934cd9df5a54254b7ec1657c0 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Tue, 2 Jun 2020 10:37:28 +0300
Subject: [PATCH 16/70] Account Helper Unit test
---
.../account/AccountHelperTest.java | 93 +++++++++++++++++++
1 file changed, 93 insertions(+)
create mode 100644 opensrp-app/src/test/java/org/smartregister/account/AccountHelperTest.java
diff --git a/opensrp-app/src/test/java/org/smartregister/account/AccountHelperTest.java b/opensrp-app/src/test/java/org/smartregister/account/AccountHelperTest.java
new file mode 100644
index 000000000..307e058dd
--- /dev/null
+++ b/opensrp-app/src/test/java/org/smartregister/account/AccountHelperTest.java
@@ -0,0 +1,93 @@
+package org.smartregister.account;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.powermock.reflect.Whitebox;
+import org.smartregister.BaseUnitTest;
+
+import java.io.IOException;
+
+/**
+ * Created by ndegwamartin on 26/05/2020.
+ */
+
+public class AccountHelperTest extends BaseUnitTest {
+ private static final String CORE_ACCOUNT_NAME = "demo";
+ private static final String CORE_ACCOUNT_TYPE = "org.smartregister.core";
+ private static final String TEST_KEY = "testKey";
+ private static final String TEST_VALUE = "random-test-value";
+ private static final String AUTH_TOKEN_TYPE = "My Admin Token Type";
+ private static final String TEST_TOKEN_VALUE = "sample-token";
+
+ @Mock
+ private AccountManager accountManager;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ Account[] accounts = {new Account(CORE_ACCOUNT_NAME, CORE_ACCOUNT_TYPE)};
+ Mockito.doReturn(accounts).when(accountManager).getAccountsByType(CORE_ACCOUNT_TYPE);
+
+ Whitebox.setInternalState(AccountHelper.class, "accountManager", accountManager);
+ }
+
+ @Test
+ public void testGetOauthAccountByType() {
+
+ Account account = AccountHelper.getOauthAccountByType(CORE_ACCOUNT_TYPE);
+ Assert.assertNotNull(account);
+ Assert.assertEquals(CORE_ACCOUNT_NAME, account.name);
+ }
+
+ @Test
+ public void testGetAccountManagerValue() {
+
+ Whitebox.setInternalState(AccountHelper.class, "accountManager", accountManager);
+
+ Mockito.doReturn(TEST_VALUE).when(accountManager).getUserData(ArgumentMatchers.any(Account.class), ArgumentMatchers.eq(TEST_KEY));
+
+ String value = AccountHelper.getAccountManagerValue(TEST_KEY, CORE_ACCOUNT_TYPE);
+ Assert.assertNotNull(value);
+ Assert.assertEquals(TEST_VALUE, value);
+ }
+
+ @Test
+ public void testGetOAuthToken() throws AuthenticatorException, OperationCanceledException, IOException {
+
+ Mockito.doReturn(TEST_TOKEN_VALUE).when(accountManager).blockingGetAuthToken(new Account(CORE_ACCOUNT_NAME, CORE_ACCOUNT_TYPE), AUTH_TOKEN_TYPE, true);
+ String myToken = AccountHelper.getOAuthToken(CORE_ACCOUNT_TYPE, AUTH_TOKEN_TYPE);
+ Assert.assertNotNull(myToken);
+ Assert.assertEquals(TEST_TOKEN_VALUE, myToken);
+ }
+
+ @Test
+ public void testInvalidateAuthToken() {
+
+ AccountHelper.invalidateAuthToken(CORE_ACCOUNT_TYPE, TEST_TOKEN_VALUE);
+ Mockito.verify(accountManager).invalidateAuthToken(CORE_ACCOUNT_TYPE, TEST_TOKEN_VALUE);
+ }
+
+ @Test
+ public void testGetCachedOAuthToken() {
+
+ Account account = new Account(CORE_ACCOUNT_NAME, CORE_ACCOUNT_TYPE);
+ Mockito.doReturn(TEST_TOKEN_VALUE).when(accountManager).peekAuthToken(account, AUTH_TOKEN_TYPE);
+
+ String cachedAuthToken = AccountHelper.getCachedOAuthToken(CORE_ACCOUNT_TYPE, AUTH_TOKEN_TYPE);
+
+ Mockito.verify(accountManager).peekAuthToken(account, AUTH_TOKEN_TYPE);
+ Assert.assertEquals(TEST_TOKEN_VALUE, cachedAuthToken);
+ }
+
+}
From 9de9d9410c57ae743de96e3809ac93d603c45cfc Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Tue, 2 Jun 2020 14:06:55 +0300
Subject: [PATCH 17/70] HTTPAgent Unit tests - Add more unit test cases
---
.../org/smartregister/service/HTTPAgent.java | 12 +-
.../smartregister/service/HTTPAgentTest.java | 350 +++++++++++++++++-
.../util/LoginResponseTestData.java | 172 +++++++++
3 files changed, 528 insertions(+), 6 deletions(-)
create mode 100644 opensrp-app/src/test/java/org/smartregister/util/LoginResponseTestData.java
diff --git a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
index 725240ae0..70d5aad10 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
@@ -235,11 +235,13 @@ public LoginResponse urlCanBeAccessWithGivenCredentials(String requestURL, Strin
urlConnection.setRequestProperty(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, basicAuth);
int statusCode = urlConnection.getResponseCode();
InputStream inputStream;
+ String responseString = "";
if (statusCode >= HttpStatus.SC_BAD_REQUEST)
inputStream = urlConnection.getErrorStream();
else
inputStream = urlConnection.getInputStream();
- String responseString = IOUtils.toString(inputStream);
+ if (inputStream != null)
+ responseString = IOUtils.toString(inputStream);
if (statusCode == HttpStatus.SC_OK) {
Timber.d("response String: %s using request url %s", responseString, url);
@@ -666,11 +668,15 @@ public LoginResponse fetchUserDetails(String requestURL, String oauthAccessToken
int statusCode = urlConnection.getResponseCode();
InputStream inputStream;
+ String responseString = null;
if (statusCode >= HttpStatus.SC_BAD_REQUEST)
inputStream = urlConnection.getErrorStream();
else
inputStream = urlConnection.getInputStream();
- String responseString = IOUtils.toString(inputStream);
+
+ if (inputStream != null)
+ responseString = IOUtils.toString(inputStream);
+
if (statusCode == HttpStatus.SC_OK) {
Timber.d("response String: %s using request url %s", responseString, url);
@@ -804,7 +810,7 @@ public boolean verifyAuthorization() {
urlConnection = initializeHttp(baseUrl, true);
Timber.i("User not authorized. User access was revoked, will log off user");
- return HttpStatus.SC_UNAUTHORIZED == urlConnection.getResponseCode() ? false : true;
+ return false;
} else if (statusCode != HttpStatus.SC_OK) {
Timber.w("Error occurred verifying authorization, User will not be logged off");
} else {
diff --git a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
index 9782a9e75..c76839219 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
@@ -11,7 +11,6 @@
import org.json.JSONObject;
import org.junit.Assert;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -38,6 +37,7 @@
import org.smartregister.domain.Response;
import org.smartregister.domain.ResponseStatus;
import org.smartregister.repository.AllSharedPreferences;
+import org.smartregister.util.LoginResponseTestData;
import java.io.File;
import java.io.FileInputStream;
@@ -51,6 +51,8 @@
import java.util.HashMap;
import java.util.List;
+import javax.net.ssl.HttpsURLConnection;
+
@RunWith(PowerMockRunner.class)
@PrepareForTest({Base64.class, File.class, FileInputStream.class, Context.class, AccountHelper.class, CoreLibrary.class, IOUtils.class})
public class HTTPAgentTest {
@@ -90,6 +92,9 @@ public class HTTPAgentTest {
@Mock
private HttpURLConnection httpURLConnection;
+ @Mock
+ private HttpsURLConnection httpsURLConnection;
+
@Mock
private OutputStream outputStream;
@@ -105,9 +110,11 @@ public class HTTPAgentTest {
private HTTPAgent httpAgent;
private static final String TEST_USERNAME = "demo";
private static final String TEST_PASSWORD = "password";
+ public static final String TEST_BASE_URL = "https://my-server.com/";
private static final String TEST_TOKEN_ENDPOINT = "https://my-server.com/oauth/token";
private static final String SECURE_RESOURCE_ENDPOINT = "https://my-server.com/my/secure/resource";
private static final String KEYClOAK_CONFIGURATION_ENDPOINT = "https://my-server.com/rest/config/keycloak";
+ private static final String USER_DETAILS_ENDPOINT = "https://my-server.com/opensrp/security/authenticate";
private final String SAMPLE_TEST_TOKEN = "sample-test-token";
private final String SAMPLE_REFRESH_TOKEN = "sample-refresh-token";
@@ -192,7 +199,6 @@ public void testUrlCanBeAccessWithGivenCredentialsGivenWrongUrl() {
}
@Test
- @Ignore
public void testUrlCanBeAccessWithGivenCredentialsGivenEmptyResp() {
PowerMockito.mockStatic(Base64.class);
LoginResponse resp = httpAgent.urlCanBeAccessWithGivenCredentials("http://mockbin.org/bin/e42f7256-18b2-40b9-a20c-40fdc564d06f", "", "");
@@ -436,7 +442,7 @@ public void testFetchOAuthConfigurationProcessesConfigurationResponseCorrectly()
HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
- Mockito.doReturn("https://my-server.com/").when(dristhiConfiguration).dristhiBaseURL();
+ Mockito.doReturn(TEST_BASE_URL).when(dristhiConfiguration).dristhiBaseURL();
Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(KEYClOAK_CONFIGURATION_ENDPOINT);
Mockito.doReturn(inputStream).when(httpURLConnection).getInputStream();
@@ -560,4 +566,342 @@ public void testOauth2authenticateRefreshTokenInvokesOauth2authenticateCoreWithC
}
+
+ @Test
+ public void testFetchUserDetailsConstructsCorrectResponse() throws Exception {
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(httpsURLConnection).when(httpAgentSpy).getHttpURLConnection(USER_DETAILS_ENDPOINT);
+ Mockito.doReturn(inputStream).when(httpsURLConnection).getInputStream();
+ Mockito.doReturn(HttpURLConnection.HTTP_OK).when(httpsURLConnection).getResponseCode();
+
+ PowerMockito.mockStatic(IOUtils.class);
+ PowerMockito.when(IOUtils.toString(inputStream)).thenReturn(LoginResponseTestData.USER_DETAILS_REQUEST_SERVER_RESPONSE);
+
+ LoginResponse loginResponse = httpAgentSpy.fetchUserDetails(USER_DETAILS_ENDPOINT, SAMPLE_TEST_TOKEN);
+
+ Assert.assertNotNull(loginResponse);
+ Assert.assertNotNull(loginResponse.message());
+ Assert.assertEquals("Login successful.", loginResponse.message());
+ Assert.assertNotNull(loginResponse.payload());
+
+ Assert.assertNotNull(loginResponse.payload().user);
+ Assert.assertEquals("demo", loginResponse.payload().user.getUsername());
+ Assert.assertEquals("Demo User", loginResponse.payload().user.getPreferredName());
+ Assert.assertEquals("93c6526-6667-3333-a611112-f3b309999999", loginResponse.payload().user.getBaseEntityId());
+
+ Assert.assertNotNull(loginResponse.payload().time);
+ Assert.assertEquals("2020-06-02 08:21:40", loginResponse.payload().time.getTime());
+ Assert.assertEquals("Africa/Nairobi", loginResponse.payload().time.getTimeZone());
+
+ Assert.assertNotNull(loginResponse.payload().locations);
+ Assert.assertNotNull(loginResponse.payload().locations.getLocationsHierarchy());
+
+ Assert.assertNotNull(loginResponse.payload().jurisdictions);
+ Assert.assertEquals(1, loginResponse.payload().jurisdictions.size());
+ Assert.assertNotNull("Health Team Kasarani", loginResponse.payload().jurisdictions.get(0));
+
+ Assert.assertNotNull(loginResponse.payload().team);
+ Assert.assertEquals("93c6526-6667-3333-a611112-f3b309999999", loginResponse.payload().team.identifier);
+ Assert.assertEquals("93c6526-6667-3333-a611112-f3b309999999", loginResponse.payload().team.uuid);
+
+ Assert.assertEquals("SUCCESS", loginResponse.name());
+
+ ArgumentCaptor headerKey = ArgumentCaptor.forClass(String.class);
+ ArgumentCaptor headerValue = ArgumentCaptor.forClass(String.class);
+
+ Mockito.verify(httpsURLConnection).setRequestProperty(headerKey.capture(), headerValue.capture());
+ String capturedKey = headerKey.getValue();
+ String capturedValue = headerValue.getValue();
+
+ Assert.assertEquals(capturedKey, AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION);
+ Assert.assertEquals(capturedValue, AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BEARER + " " + SAMPLE_TEST_TOKEN);
+
+ }
+
+ @Test
+ public void testFetchUserDetailsConstructsCorrectResponseForUnauthorizedRequests() throws Exception {
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(httpsURLConnection).when(httpAgentSpy).getHttpURLConnection(USER_DETAILS_ENDPOINT);
+ Mockito.doReturn(HttpURLConnection.HTTP_UNAUTHORIZED).when(httpsURLConnection).getResponseCode();
+
+ LoginResponse loginResponse = httpAgentSpy.fetchUserDetails(USER_DETAILS_ENDPOINT, SAMPLE_TEST_TOKEN);
+
+ Assert.assertNotNull(loginResponse);
+ Assert.assertNotNull(loginResponse.message());
+ Assert.assertEquals("Please check the credentials", loginResponse.message());
+ Assert.assertNull(loginResponse.payload());
+
+ Assert.assertEquals("UNAUTHORIZED", loginResponse.name());
+
+ }
+
+ @Test
+ public void testFetchUserDetailsConstructsCorrectResponseForRandomServerError() throws Exception {
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(httpsURLConnection).when(httpAgentSpy).getHttpURLConnection(USER_DETAILS_ENDPOINT);
+
+ Mockito.doReturn(errorStream).when(httpsURLConnection).getInputStream();
+ Mockito.doReturn(HttpURLConnection.HTTP_INTERNAL_ERROR).when(httpsURLConnection).getResponseCode();
+
+ PowerMockito.mockStatic(IOUtils.class);
+ PowerMockito.when(IOUtils.toString(errorStream)).thenReturn("message Oops, something went wrong
");
+
+ LoginResponse loginResponse = httpAgentSpy.fetchUserDetails(USER_DETAILS_ENDPOINT, SAMPLE_TEST_TOKEN);
+
+ Assert.assertNotNull(loginResponse);
+ Assert.assertNotNull(loginResponse.message());
+ Assert.assertEquals("Dristhi login failed. Try later", loginResponse.message());
+ Assert.assertNull(loginResponse.payload());
+
+ Assert.assertEquals("UNKNOWN_RESPONSE", loginResponse.name());
+
+ }
+
+
+ @Test
+ public void testFetchUserDetailsConstructsCorrectResponseForMalformedURLRequests() throws Exception {
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(httpsURLConnection).when(httpAgentSpy).getHttpURLConnection(USER_DETAILS_ENDPOINT);
+ Mockito.doThrow(new MalformedURLException()).when(httpsURLConnection).getResponseCode();
+
+ LoginResponse loginResponse = httpAgentSpy.fetchUserDetails(USER_DETAILS_ENDPOINT, SAMPLE_TEST_TOKEN);
+
+ Assert.assertNotNull(loginResponse);
+ Assert.assertNotNull(loginResponse.message());
+ Assert.assertEquals("Incorrect url", loginResponse.message());
+ Assert.assertNull(loginResponse.payload());
+
+ Assert.assertEquals("MALFORMED_URL", loginResponse.name());
+
+ }
+
+ @Test
+ public void testFetchUserDetailsConstructsCorrectResponseForConnectionTimedOutRequests() throws Exception {
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(httpsURLConnection).when(httpAgentSpy).getHttpURLConnection(USER_DETAILS_ENDPOINT);
+ Mockito.doThrow(new SocketTimeoutException()).when(httpsURLConnection).getResponseCode();
+
+ LoginResponse loginResponse = httpAgentSpy.fetchUserDetails(USER_DETAILS_ENDPOINT, SAMPLE_TEST_TOKEN);
+
+ Assert.assertNotNull(loginResponse);
+ Assert.assertNotNull(loginResponse.message());
+ Assert.assertEquals("The server could not be reached. Try again", loginResponse.message());
+ Assert.assertNull(loginResponse.payload());
+
+ Assert.assertEquals("TIMEOUT", loginResponse.name());
+
+ }
+
+
+ @Test
+ public void testFetchUserDetailsConstructsCorrectResponseForRequestsWithNetworkConnectivity() throws Exception {
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(httpsURLConnection).when(httpAgentSpy).getHttpURLConnection(USER_DETAILS_ENDPOINT);
+ Mockito.doThrow(new IOException()).when(httpsURLConnection).getResponseCode();
+
+ LoginResponse loginResponse = httpAgentSpy.fetchUserDetails(USER_DETAILS_ENDPOINT, SAMPLE_TEST_TOKEN);
+
+ Assert.assertNotNull(loginResponse);
+ Assert.assertNotNull(loginResponse.message());
+ Assert.assertEquals("No internet connection. Please ensure data connectivity", loginResponse.message());
+ Assert.assertNull(loginResponse.payload());
+
+ Assert.assertEquals("NO_INTERNET_CONNECTIVITY", loginResponse.name());
+
+ }
+
+
+ @Test
+ public void testVerifyAuthorizationReturnsTrueForAuthorizedResponse() throws Exception {
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(TEST_BASE_URL).when(dristhiConfiguration).dristhiBaseURL();
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(KEYClOAK_CONFIGURATION_ENDPOINT);
+
+ Mockito.doReturn(HttpURLConnection.HTTP_OK).when(httpURLConnection).getResponseCode();
+
+ boolean isVerified = httpAgentSpy.verifyAuthorization();
+ Assert.assertTrue(isVerified);
+
+ }
+
+ @Test
+ public void testVerifyAuthorizationReturnsFalseForUnauthorizedResponse() throws Exception {
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(TEST_BASE_URL).when(dristhiConfiguration).dristhiBaseURL();
+ Mockito.doReturn(TEST_USERNAME).when(allSharedPreferences).fetchRegisteredANM();
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection("https://my-server.com/user-details?anm-id=" + TEST_USERNAME);
+
+ Mockito.doReturn(HttpURLConnection.HTTP_UNAUTHORIZED).when(httpURLConnection).getResponseCode();
+
+ boolean isVerified = httpAgentSpy.verifyAuthorization();
+ Assert.assertFalse(isVerified);
+
+ }
+
+ @Test
+ public void testUrlCanBeAccessWithGivenCredentialsReturnsUnauthorizedResponse() throws Exception {
+
+ PowerMockito.mockStatic(Base64.class);
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(TEST_BASE_URL).when(dristhiConfiguration).dristhiBaseURL();
+ Mockito.doReturn(TEST_USERNAME).when(allSharedPreferences).fetchRegisteredANM();
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(USER_DETAILS_ENDPOINT);
+
+ Mockito.doReturn(HttpURLConnection.HTTP_UNAUTHORIZED).when(httpURLConnection).getResponseCode();
+
+ LoginResponse response = httpAgentSpy.urlCanBeAccessWithGivenCredentials(USER_DETAILS_ENDPOINT, TEST_USERNAME, TEST_PASSWORD);
+ Assert.assertNotNull(response);
+ Assert.assertNotNull(response.message());
+ Assert.assertNull(response.payload());
+ Assert.assertEquals("Please check the credentials", response.message());
+
+ }
+
+ @Test
+ public void testUrlCanBeAccessWithGivenCredentialsReturnsErrorResponseForMalformedURL() throws Exception {
+
+ PowerMockito.mockStatic(Base64.class);
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(TEST_BASE_URL).when(dristhiConfiguration).dristhiBaseURL();
+ Mockito.doReturn(TEST_USERNAME).when(allSharedPreferences).fetchRegisteredANM();
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(USER_DETAILS_ENDPOINT);
+
+ Mockito.doThrow(new MalformedURLException()).when(httpURLConnection).getResponseCode();
+
+ LoginResponse response = httpAgentSpy.urlCanBeAccessWithGivenCredentials(USER_DETAILS_ENDPOINT, TEST_USERNAME, TEST_PASSWORD);
+ Assert.assertNotNull(response);
+ Assert.assertNull(response.payload());
+ Assert.assertNotNull(response.message());
+ Assert.assertEquals(LoginResponse.MALFORMED_URL.name(), response.name());
+
+ }
+
+ @Test
+ public void testUrlCanBeAccessWithGivenCredentialsReturnsCorrectErrorResponseForSocketTimeout() throws Exception {
+
+ PowerMockito.mockStatic(Base64.class);
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(TEST_BASE_URL).when(dristhiConfiguration).dristhiBaseURL();
+ Mockito.doReturn(TEST_USERNAME).when(allSharedPreferences).fetchRegisteredANM();
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(USER_DETAILS_ENDPOINT);
+
+ Mockito.doThrow(new MalformedURLException()).when(httpURLConnection).getResponseCode();
+
+
+ LoginResponse response = httpAgentSpy.urlCanBeAccessWithGivenCredentials(USER_DETAILS_ENDPOINT, TEST_USERNAME, TEST_PASSWORD);
+ Assert.assertNotNull(response);
+ Assert.assertNull(response.payload());
+ Assert.assertNotNull(response.message());
+ Assert.assertEquals(LoginResponse.MALFORMED_URL.name(), response.name());
+
+ }
+
+ @Test
+ public void testUrlCanBeAccessWithGivenCredentialsReturnsCorrectErrorResponseErrorResponseForIOException() throws Exception {
+
+ PowerMockito.mockStatic(Base64.class);
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(TEST_BASE_URL).when(dristhiConfiguration).dristhiBaseURL();
+ Mockito.doReturn(TEST_USERNAME).when(allSharedPreferences).fetchRegisteredANM();
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(USER_DETAILS_ENDPOINT);
+
+ Mockito.doThrow(new IOException()).when(httpURLConnection).getResponseCode();
+
+
+ LoginResponse response = httpAgentSpy.urlCanBeAccessWithGivenCredentials(USER_DETAILS_ENDPOINT, TEST_USERNAME, TEST_PASSWORD);
+ Assert.assertNotNull(response);
+ Assert.assertNull(response.payload());
+ Assert.assertNotNull(response.message());
+ Assert.assertEquals(LoginResponse.NO_INTERNET_CONNECTIVITY.name(), response.name());
+
+ }
+
+ @Test
+ public void testUrlCanBeAccessWithGivenCredentialsReturnsCorrectResponseForRandomServerError() throws Exception {
+
+ PowerMockito.mockStatic(Base64.class);
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(httpsURLConnection).when(httpAgentSpy).getHttpURLConnection(USER_DETAILS_ENDPOINT);
+
+ Mockito.doReturn(errorStream).when(httpsURLConnection).getErrorStream();
+ Mockito.doReturn(HttpURLConnection.HTTP_INTERNAL_ERROR).when(httpsURLConnection).getResponseCode();
+
+ PowerMockito.mockStatic(IOUtils.class);
+ PowerMockito.when(IOUtils.toString(errorStream)).thenReturn("message Oops, something went wrong
");
+
+ LoginResponse loginResponse = httpAgentSpy.urlCanBeAccessWithGivenCredentials(USER_DETAILS_ENDPOINT, TEST_USERNAME, TEST_PASSWORD);
+
+ Assert.assertNotNull(loginResponse);
+ Assert.assertNotNull(loginResponse.message());
+ Assert.assertEquals("Oops, something went wrong", loginResponse.message());
+ Assert.assertNull(loginResponse.payload());
+
+ Assert.assertEquals("CUSTOM_SERVER_RESPONSE", loginResponse.name());
+
+ }
}
diff --git a/opensrp-app/src/test/java/org/smartregister/util/LoginResponseTestData.java b/opensrp-app/src/test/java/org/smartregister/util/LoginResponseTestData.java
new file mode 100644
index 000000000..8e9930607
--- /dev/null
+++ b/opensrp-app/src/test/java/org/smartregister/util/LoginResponseTestData.java
@@ -0,0 +1,172 @@
+package org.smartregister.util;
+
+/**
+ * Created by ndegwamartin on 02/06/2020.
+ */
+public final class LoginResponseTestData {
+
+ public static final String USER_DETAILS_REQUEST_SERVER_RESPONSE = "{\n" +
+ " \"locations\":{\n" +
+ " \"locationsHierarchy\":{\n" +
+ " \"map\":{\n" +
+ " \"583j4bve4-9a36-459a-99fc-bc0356d99999\":{\n" +
+ " \"id\":\"583j4bve4-9a36-459a-99fc-bc0356d99999\",\n" +
+ " \"label\":\"Kenya\",\n" +
+ " \"node\":{\n" +
+ " \"locationId\":\"583j4bve4-9a36-459a-99fc-bc0356d99999\",\n" +
+ " \"name\":\"Kenya\",\n" +
+ " \"tags\":[\n" +
+ " \"Country\"\n" +
+ " ],\n" +
+ " \"voided\":false\n" +
+ " },\n" +
+ " \"children\":{\n" +
+ " \"3x3tsh58-e6db-4dsa-8be1-e860ec299999\":{\n" +
+ " \"id\":\"3x3tsh58-e6db-4dsa-8be1-e860ec299999\",\n" +
+ " \"label\":\"Nairobi\",\n" +
+ " \"node\":{\n" +
+ " \"locationId\":\"3x3tsh58-e6db-4dsa-8be1-e860ec299999\",\n" +
+ " \"name\":\"Nairobi\",\n" +
+ " \"parentLocation\":{\n" +
+ " \"locationId\":\"583j4bve4-9a36-459a-99fc-bc0356d99999\",\n" +
+ " \"voided\":false\n" +
+ " },\n" +
+ " \"tags\":[\n" +
+ " \"Province\"\n" +
+ " ],\n" +
+ " \"voided\":false\n" +
+ " },\n" +
+ " \"children\":{\n" +
+ " \"93437b43-485d-44df-8eaf-434449579999\":{\n" +
+ " \"id\":\"93437b43-485d-44df-8eaf-434449579999\",\n" +
+ " \"label\":\"Kasarani\",\n" +
+ " \"node\":{\n" +
+ " \"locationId\":\"93437b43-485d-44df-8eaf-434449579999\",\n" +
+ " \"name\":\"Kasarani\",\n" +
+ " \"parentLocation\":{\n" +
+ " \"locationId\":\"3x3tsh58-e6db-4dsa-8be1-e860ec299999\",\n" +
+ " \"voided\":false\n" +
+ " },\n" +
+ " \"tags\":[\n" +
+ " \"District\"\n" +
+ " ],\n" +
+ " \"voided\":false\n" +
+ " },\n" +
+ " \"children\":{\n" +
+ " \"9ue62k3a-88f3-886f-b990-bb529b399999\":{\n" +
+ " \"id\":\"9ue62k3a-88f3-886f-b990-bb529b399999\",\n" +
+ " \"label\":\"Roysambu\",\n" +
+ " \"node\":{\n" +
+ " \"locationId\":\"9ue62k3a-88f3-886f-b990-bb529b399999\",\n" +
+ " \"name\":\"Roysambu\",\n" +
+ " \"parentLocation\":{\n" +
+ " \"locationId\":\"93437b43-485d-44df-8eaf-434449579999\",\n" +
+ " \"voided\":false\n" +
+ " },\n" +
+ " \"tags\":[\n" +
+ " \"Ward\"\n" +
+ " ],\n" +
+ " \"voided\":false\n" +
+ " },\n" +
+ " \"children\":{\n" +
+ " \"kdjafls993-33d1-4322-9342-062j8cf99999\":{\n" +
+ " \"id\":\"kdjafls993-33d1-4322-9342-062j8cf99999\",\n" +
+ " \"label\":\"Thika Road Health Center\",\n" +
+ " \"node\":{\n" +
+ " \"locationId\":\"kdjafls993-33d1-4322-9342-062j8cf99999\",\n" +
+ " \"name\":\"Thika Road Health Center\",\n" +
+ " \"parentLocation\":{\n" +
+ " \"locationId\":\"9ue62k3a-88f3-886f-b990-bb529b399999\",\n" +
+ " \"voided\":false\n" +
+ " },\n" +
+ " \"tags\":[\n" +
+ " \"Facility\"\n" +
+ " ],\n" +
+ " \"voided\":false\n" +
+ " },\n" +
+ " \"parent\":\"9ue62k3a-88f3-886f-b990-bb529b399999\"\n" +
+ " }\n" +
+ " },\n" +
+ " \"parent\":\"93437b43-485d-44df-8eaf-434449579999\"\n" +
+ " }\n" +
+ " },\n" +
+ " \"parent\":\"3x3tsh58-e6db-4dsa-8be1-e860ec299999\"\n" +
+ " }\n" +
+ " },\n" +
+ " \"parent\":\"583j4bve4-9a36-459a-99fc-bc0356d99999\"\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " },\n" +
+ " \"parentChildren\":{\n" +
+ " \"9ue62k3a-88f3-886f-b990-bb529b399999\":[\n" +
+ " \"kdjafls993-33d1-4322-9342-062j8cf99999\"\n" +
+ " ],\n" +
+ " \"93437b43-485d-44df-8eaf-434449579999\":[\n" +
+ " \"9ue62k3a-88f3-886f-b990-bb529b399999\"\n" +
+ " ],\n" +
+ " \"583j4bve4-9a36-459a-99fc-bc0356d99999\":[\n" +
+ " \"3x3tsh58-e6db-4dsa-8be1-e860ec299999\"\n" +
+ " ],\n" +
+ " \"3x3tsh58-e6db-4dsa-8be1-e860ec299999\":[\n" +
+ " \"93437b43-485d-44df-8eaf-434449579999\"\n" +
+ " ]\n" +
+ " }\n" +
+ " }\n" +
+ " },\n" +
+ " \"team\":{\n" +
+ " \"identifier\":\"93c6526-6667-3333-a611112-f3b309999999\",\n" +
+ " \"locations\":[\n" +
+ " {\n" +
+ " \"display\":\"Health Team Kasarani\",\n" +
+ " \"name\":\"Health Team Kasarani\",\n" +
+ " \"uuid\":\"kdjafls993-33d1-4322-9342-062j8cf99999\"\n" +
+ " }\n" +
+ " ],\n" +
+ " \"team\":{\n" +
+ " \"teamName\":\"Health Team Kasarani\",\n" +
+ " \"organizationIds\":[\n" +
+ " 1.0\n" +
+ " ],\n" +
+ " \"display\":\"Health Team Kasarani\",\n" +
+ " \"location\":{\n" +
+ " \"display\":\"Health Team Kasarani\",\n" +
+ " \"name\":\"Health Team Kasarani\",\n" +
+ " \"uuid\":\"kdjafls993-33d1-4322-9342-062j8cf99999\"\n" +
+ " },\n" +
+ " \"uuid\":\"abf1be43-32da-4848-9b50-630fb89ec0ef\"\n" +
+ " },\n" +
+ " \"uuid\":\"93c6526-6667-3333-a611112-f3b309999999\"\n" +
+ " },\n" +
+ " \"time\":{\n" +
+ " \"time\":\"2020-06-02 08:21:40\",\n" +
+ " \"timeZone\":\"Africa/Nairobi\"\n" +
+ " },\n" +
+ " \"user\":{\n" +
+ " \"username\":\"demo\",\n" +
+ " \"roles\":[\n" +
+ " \"ROLE_OPENMRS\",\n" +
+ " \"ROLE_ALL_EVENTS\",\n" +
+ " \"ROLE_PLANS_FOR_USER\",\n" +
+ " \"ROLE_offline_access\",\n" +
+ " \"ROLE_uma_authorization\"\n" +
+ " ],\n" +
+ " \"permissions\":[\n" +
+ " \"ROLE_OPENMRS\",\n" +
+ " \"ROLE_ALL_EVENTS\",\n" +
+ " \"ROLE_PLANS_FOR_USER\",\n" +
+ " \"ROLE_offline_access\",\n" +
+ " \"ROLE_uma_authorization\"\n" +
+ " ],\n" +
+ " \"preferredName\":\"Demo User\",\n" +
+ " \"baseEntityId\":\"93c6526-6667-3333-a611112-f3b309999999\",\n" +
+ " \"attributes\":{\n" +
+ "\n" +
+ " },\n" +
+ " \"voided\":false\n" +
+ " },\n" +
+ " \"jurisdictions\":[\n" +
+ " \"Health Team Kasarani\"\n" +
+ " ]\n" +
+ "}";
+}
From f91d166537991efd386cc9d5a64272f181f2e25f Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Tue, 2 Jun 2020 19:08:56 +0300
Subject: [PATCH 18/70] CI Configuration updates - Add codacy configuration
file - Rename test case
---
.codacy.yml | 6 ++++++
.../test/java/org/smartregister/service/HTTPAgentTest.java | 2 +-
2 files changed, 7 insertions(+), 1 deletion(-)
create mode 100644 .codacy.yml
diff --git a/.codacy.yml b/.codacy.yml
new file mode 100644
index 000000000..367d4457b
--- /dev/null
+++ b/.codacy.yml
@@ -0,0 +1,6 @@
+engines:
+ duplication:
+ exclude_paths:
+ - '**/test/**'
+ - '**/androidTest/**'
+
diff --git a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
index c76839219..c433d2044 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
@@ -852,7 +852,7 @@ public void testUrlCanBeAccessWithGivenCredentialsReturnsCorrectErrorResponseFor
}
@Test
- public void testUrlCanBeAccessWithGivenCredentialsReturnsCorrectErrorResponseErrorResponseForIOException() throws Exception {
+ public void testUrlCanBeAccessWithGivenCredentialsReturnsCorrectErrorResponseForIOException() throws Exception {
PowerMockito.mockStatic(Base64.class);
From 63a7ca66f80253b5fd8157489b551f45f466f1d9 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Wed, 3 Jun 2020 12:21:46 +0300
Subject: [PATCH 19/70] Fixed/Implment code review change requests on HTTP
Agent test - Renamed test case - Refactored test case logic
---
.../org/smartregister/service/HTTPAgentTest.java | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
index c433d2044..8f0ae1e23 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
@@ -655,7 +655,7 @@ public void testFetchUserDetailsConstructsCorrectResponseForRandomServerError()
Mockito.doReturn(httpsURLConnection).when(httpAgentSpy).getHttpURLConnection(USER_DETAILS_ENDPOINT);
- Mockito.doReturn(errorStream).when(httpsURLConnection).getInputStream();
+ Mockito.doReturn(errorStream).when(httpsURLConnection).getErrorStream();
Mockito.doReturn(HttpURLConnection.HTTP_INTERNAL_ERROR).when(httpsURLConnection).getResponseCode();
PowerMockito.mockStatic(IOUtils.class);
@@ -665,10 +665,10 @@ public void testFetchUserDetailsConstructsCorrectResponseForRandomServerError()
Assert.assertNotNull(loginResponse);
Assert.assertNotNull(loginResponse.message());
- Assert.assertEquals("Dristhi login failed. Try later", loginResponse.message());
+ Assert.assertEquals("Oops, something went wrong", loginResponse.message());
Assert.assertNull(loginResponse.payload());
- Assert.assertEquals("UNKNOWN_RESPONSE", loginResponse.name());
+ Assert.assertEquals("CUSTOM_SERVER_RESPONSE", loginResponse.name());
}
@@ -719,7 +719,7 @@ public void testFetchUserDetailsConstructsCorrectResponseForConnectionTimedOutRe
@Test
- public void testFetchUserDetailsConstructsCorrectResponseForRequestsWithNetworkConnectivity() throws Exception {
+ public void testFetchUserDetailsConstructsCorrectResponseForRequestsWithoutNetworkConnectivity() throws Exception {
URL url = PowerMockito.mock(URL.class);
Assert.assertNotNull(url);
@@ -840,14 +840,14 @@ public void testUrlCanBeAccessWithGivenCredentialsReturnsCorrectErrorResponseFor
Mockito.doReturn(TEST_USERNAME).when(allSharedPreferences).fetchRegisteredANM();
Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(USER_DETAILS_ENDPOINT);
- Mockito.doThrow(new MalformedURLException()).when(httpURLConnection).getResponseCode();
+ Mockito.doThrow(new SocketTimeoutException()).when(httpURLConnection).getResponseCode();
LoginResponse response = httpAgentSpy.urlCanBeAccessWithGivenCredentials(USER_DETAILS_ENDPOINT, TEST_USERNAME, TEST_PASSWORD);
Assert.assertNotNull(response);
Assert.assertNull(response.payload());
Assert.assertNotNull(response.message());
- Assert.assertEquals(LoginResponse.MALFORMED_URL.name(), response.name());
+ Assert.assertEquals(LoginResponse.TIMEOUT.name(), response.name());
}
From fa29df3ea93cae8f064df26d146cf3c13e6a8dc0 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Wed, 3 Jun 2020 15:39:00 +0300
Subject: [PATCH 20/70] Remove unused imports
---
.../main/java/org/smartregister/sample/SampleLoginActivity.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/sample/src/main/java/org/smartregister/sample/SampleLoginActivity.java b/sample/src/main/java/org/smartregister/sample/SampleLoginActivity.java
index 89f4de02c..8fde0248b 100644
--- a/sample/src/main/java/org/smartregister/sample/SampleLoginActivity.java
+++ b/sample/src/main/java/org/smartregister/sample/SampleLoginActivity.java
@@ -2,7 +2,6 @@
import android.content.Intent;
import android.os.Bundle;
-import android.widget.TextView;
import org.smartregister.sample.presenter.LoginPresenter;
import org.smartregister.view.activity.BaseLoginActivity;
From a9e592cc63b1aa1489dd445e11e9b8d457849ccb Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Wed, 3 Jun 2020 15:41:52 +0300
Subject: [PATCH 21/70] Refactor to remove duplicate code : DRY
---
.../org/smartregister/util/OpenSRPImageLoader.java | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/util/OpenSRPImageLoader.java b/opensrp-app/src/main/java/org/smartregister/util/OpenSRPImageLoader.java
index 2ebede8e3..4a56c87e9 100644
--- a/opensrp-app/src/main/java/org/smartregister/util/OpenSRPImageLoader.java
+++ b/opensrp-app/src/main/java/org/smartregister/util/OpenSRPImageLoader.java
@@ -160,8 +160,7 @@ private static RequestQueue newRequestQueue(Context context) {
public HttpResponse performRequest(Request> request, Map
headers) throws IOException, AuthFailureError {
- String accessToken = AccountHelper.getOAuthToken(CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
- headers.put(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, new StringBuilder(AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BEARER + " ").append(accessToken).toString());
+ addBearerTokenAuthorizationHeader(headers);
return super.performRequest(request, headers);
}
@@ -175,8 +174,7 @@ public HttpResponse performRequest(Request> request, Map
@Override
public HttpResponse performRequest(Request> request, Map headers) throws IOException, AuthFailureError {
- String accessToken = AccountHelper.getOAuthToken(CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
- headers.put(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, new StringBuilder(AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BEARER + " ").append(accessToken).toString());
+ addBearerTokenAuthorizationHeader(headers);
return super.performRequest(request, headers);
}
@@ -187,6 +185,11 @@ public HttpResponse performRequest(Request> request, Map heade
return requestQueue;
}
+ private static void addBearerTokenAuthorizationHeader(Map headers) {
+ String accessToken = AccountHelper.getOAuthToken(CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
+ headers.put(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, new StringBuilder(AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BEARER + " ").append(accessToken).toString());
+ }
+
/**
* Sets a {@link Bitmap} to an {@link ImageView} using a fade-in animation. If there is a
* {@link Drawable} already set on the ImageView then use that as the image to fade from.
From 599b4c297881030dad9f38a6f29c13bcc95f8bc9 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Fri, 5 Jun 2020 20:19:19 +0300
Subject: [PATCH 22/70] Add unit tests - Add unit tests for
HTTPAgent.downloadFormUrl
---
.../org/smartregister/service/HTTPAgent.java | 59 +++++++---
.../smartregister/service/HTTPAgentTest.java | 109 ++++++++++++++++++
2 files changed, 153 insertions(+), 15 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
index 70d5aad10..f69121a4a 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
@@ -4,7 +4,6 @@
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.util.Base64;
-import android.util.Log;
import com.google.common.io.BaseEncoding;
import com.google.gson.Gson;
@@ -40,6 +39,7 @@
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -426,7 +426,7 @@ public String httpImagePost(String urlString, ProfileImage image) {
}
private void attachImage(PrintWriter writer, ProfileImage image, OutputStream outputStream) throws IOException {
- File uploadImageFile = new File(image.getFilepath());
+ File uploadImageFile = getDownloadFolder(image.getFilepath());
String fileName = uploadImageFile.getName();
writer.append("--" + boundary).append(crlf);
@@ -722,18 +722,18 @@ public Response downloadFromURL(String downloadURL_, String file
HttpURLConnection httpUrlConnection = null;
try {
- File dir = new File(FormPathService.sdcardPathDownload);
+ File dir = getSDCardDownloadPath();
if (!dir.exists()) {
dir.mkdirs();
}
- File file = new File(dir, fileName);
+ File file = getFile(fileName, dir);
long startTime = System.currentTimeMillis();
- Log.d("DownloadFormService", "download begin");
- Log.d("DownloadFormService", "download url: " + downloadURL_);
- Log.d("DownloadFormService", "download file name: " + fileName);
+ Timber.d("DownloadFormService %s", "download begin");
+ Timber.d("DownloadFormService %s %s", "download url: ", downloadURL_);
+ Timber.d("DownloadFormService %s %s", "download file name: ", fileName);
String downloadURL = downloadURL_.replaceAll("\\s+", "");
@@ -744,14 +744,14 @@ public Response downloadFromURL(String downloadURL_, String file
if (status == HttpURLConnection.HTTP_OK) {
InputStream inputStream = httpUrlConnection.getInputStream();
- BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
+ BufferedInputStream bufferedInputStream = getBufferedInputStream(inputStream);
long fileLength = bufferedInputStream.available();
if (fileLength == 0) {
return new Response(ResponseStatus.success,
DownloadStatus.nothingDownloaded);
}
- Log.d("DownloadFormService", "file length : " + fileLength);
+ Timber.d("DownloadFormService %s %d", "file length : ", fileLength);
ByteArrayBuffer baf = new ByteArrayBuffer(9999);
int current = 0;
@@ -760,22 +760,22 @@ public Response downloadFromURL(String downloadURL_, String file
}
/* Convert the bytes to String */
- FileOutputStream fos = new FileOutputStream(file);
+ FileOutputStream fos = getFileOutputStream(file);
fos.write(baf.toByteArray());
fos.flush();
fos.close();
- Log.d("DownloadFormService",
- "download finished in " + ((System.currentTimeMillis() - startTime) / 1000)
- + " sec");
+ Timber.d("DownloadFormService %s %d %s",
+ "download finished in ", ((System.currentTimeMillis() - startTime) / 1000)
+ , " sec");
} else {
- Log.d("RESPONSE", "Server returned non-OK status: " + status);
+ Timber.d("RESPONSE %s %s ", "Server returned non-OK status: ", status);
return new Response(ResponseStatus.failure, DownloadStatus.failedDownloaded);
}
} catch (IOException e) {
- Log.d("DownloadFormService", "download error : " + e);
+ Timber.d(e, "DownloadFormService");
return new Response(ResponseStatus.success, DownloadStatus.failedDownloaded);
} finally {
@@ -785,6 +785,35 @@ public Response downloadFromURL(String downloadURL_, String file
return new Response(ResponseStatus.success, DownloadStatus.downloaded);
}
+ @VisibleForTesting
+ @NonNull
+ protected FileOutputStream getFileOutputStream(File file) throws FileNotFoundException {
+ return new FileOutputStream(file);
+ }
+
+ @VisibleForTesting
+ @NonNull
+ protected BufferedInputStream getBufferedInputStream(InputStream inputStream) {
+ return new BufferedInputStream(inputStream);
+ }
+
+ @VisibleForTesting
+ @NonNull
+ protected File getFile(String fileName, File dir) {
+ return new File(dir, fileName);
+ }
+
+ @VisibleForTesting
+ protected File getSDCardDownloadPath() {
+ return getDownloadFolder(FormPathService.sdcardPathDownload);
+ }
+
+ @NonNull
+ @VisibleForTesting
+ protected File getDownloadFolder(String sdcardPathDownload) {
+ return new File(sdcardPathDownload);
+ }
+
public boolean verifyAuthorization() {
String baseUrl = configuration.dristhiBaseURL();
diff --git a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
index 8f0ae1e23..be37a2701 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
@@ -32,6 +32,7 @@
import org.smartregister.account.AccountConfiguration;
import org.smartregister.account.AccountHelper;
import org.smartregister.account.AccountResponse;
+import org.smartregister.domain.DownloadStatus;
import org.smartregister.domain.LoginResponse;
import org.smartregister.domain.ProfileImage;
import org.smartregister.domain.Response;
@@ -39,8 +40,10 @@
import org.smartregister.repository.AllSharedPreferences;
import org.smartregister.util.LoginResponseTestData;
+import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -104,6 +107,18 @@ public class HTTPAgentTest {
@Mock
private InputStream errorStream;
+ @Mock
+ private File dirFile;
+
+ @Mock
+ private File file;
+
+ @Mock
+ private BufferedInputStream bufferedInputStream;
+
+ @Mock
+ private FileOutputStream fileOutputStream;
+
@Rule
private TemporaryFolder folder = new TemporaryFolder();
@@ -115,6 +130,7 @@ public class HTTPAgentTest {
private static final String SECURE_RESOURCE_ENDPOINT = "https://my-server.com/my/secure/resource";
private static final String KEYClOAK_CONFIGURATION_ENDPOINT = "https://my-server.com/rest/config/keycloak";
private static final String USER_DETAILS_ENDPOINT = "https://my-server.com/opensrp/security/authenticate";
+ private static final String TEST_IMAGE_DOWNLOAD_ENDPOINT = "https://my-server.com/opensrp/multimedia/myimage.jpg";
private final String SAMPLE_TEST_TOKEN = "sample-test-token";
private final String SAMPLE_REFRESH_TOKEN = "sample-refresh-token";
@@ -126,6 +142,7 @@ public class HTTPAgentTest {
private static final String OAUTH_CONFIGURATION_SERVER_RESPONSE = "{\"issuer\":\"https://my-server.com/oauth/issuer\",\r\n\"authorization_endpoint\": \"https://my-server.com/oauth/auth\",\r\n\"token_endpoint\": \"https://my-server.com/oauth/token\",\r\n\"grant_types_supported\":[\"authorization code\",\"implicit\",\"password\"]\r\n}";
private static final String FETCH_DATA_REQUEST_SERVER_RESPONSE = "{status:{\"response_status\":\"success\"},payload: \"My secure resources from the server\"\r\n\r\n}";
private static final String SAMPLE_POST_REQUEST_PAYLOAD = "{\"payload\":\"My POST Payload\"}";
+ private static final String TEST_FILE_NAME = "Profile";
@Before
public void setUp() {
@@ -904,4 +921,96 @@ public void testUrlCanBeAccessWithGivenCredentialsReturnsCorrectResponseForRando
Assert.assertEquals("CUSTOM_SERVER_RESPONSE", loginResponse.name());
}
+
+ @Test
+ public void testDownloadFromUrl() throws Exception {
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(dirFile).when(httpAgentSpy).getSDCardDownloadPath();
+ Mockito.doReturn(file).when(httpAgentSpy).getFile(TEST_FILE_NAME, dirFile);
+ Mockito.doReturn(false).when(dirFile).exists();
+ Mockito.doReturn(true).when(dirFile).mkdirs();
+
+ Mockito.doReturn(httpsURLConnection).when(httpAgentSpy).getHttpURLConnection(TEST_IMAGE_DOWNLOAD_ENDPOINT);
+ Mockito.doReturn(HttpURLConnection.HTTP_OK).when(httpsURLConnection).getResponseCode();
+ Mockito.doReturn(inputStream).when(httpsURLConnection).getInputStream();
+ Mockito.doReturn(bufferedInputStream).when(httpAgentSpy).getBufferedInputStream(inputStream);
+ Mockito.doReturn(1985).when(bufferedInputStream).available();
+ Mockito.doReturn(-1).when(bufferedInputStream).read();
+
+ Mockito.doReturn(fileOutputStream).when(httpAgentSpy).getFileOutputStream(file);
+
+ DownloadStatus downloadStatus = httpAgentSpy.downloadFromUrl(TEST_IMAGE_DOWNLOAD_ENDPOINT, TEST_FILE_NAME);
+ Assert.assertNotNull(downloadStatus);
+ Assert.assertEquals("Download successful", downloadStatus.displayValue());
+
+ Mockito.verify(fileOutputStream).write(ArgumentMatchers.any(byte[].class));
+ Mockito.verify(fileOutputStream).flush();
+ Mockito.verify(fileOutputStream).close();
+
+ }
+
+
+ @Test
+ public void testDownloadFromUrlReturnsCorrectResponseIfNothingDownloaded() throws Exception {
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(dirFile).when(httpAgentSpy).getSDCardDownloadPath();
+ Mockito.doReturn(file).when(httpAgentSpy).getFile(TEST_FILE_NAME, dirFile);
+ Mockito.doReturn(false).when(dirFile).exists();
+ Mockito.doReturn(true).when(dirFile).mkdirs();
+
+ Mockito.doReturn(httpsURLConnection).when(httpAgentSpy).getHttpURLConnection(TEST_IMAGE_DOWNLOAD_ENDPOINT);
+ Mockito.doReturn(HttpURLConnection.HTTP_OK).when(httpsURLConnection).getResponseCode();
+
+ Mockito.doReturn(inputStream).when(httpsURLConnection).getInputStream();
+
+ DownloadStatus downloadStatus = httpAgentSpy.downloadFromUrl(TEST_IMAGE_DOWNLOAD_ENDPOINT, TEST_FILE_NAME);
+ Assert.assertNotNull(downloadStatus);
+ Assert.assertEquals("Nothing downloaded.", downloadStatus.displayValue());
+
+
+ }
+
+ @Test
+ public void testDownloadFromUrlReturnsCorrectResponseIfIOExceptionThrown() throws Exception {
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(dirFile).when(httpAgentSpy).getSDCardDownloadPath();
+ Mockito.doReturn(file).when(httpAgentSpy).getFile(TEST_FILE_NAME, dirFile);
+ Mockito.doReturn(false).when(dirFile).exists();
+ Mockito.doReturn(true).when(dirFile).mkdirs();
+
+ Mockito.doReturn(httpsURLConnection).when(httpAgentSpy).getHttpURLConnection(TEST_IMAGE_DOWNLOAD_ENDPOINT);
+ Mockito.doThrow(new IOException()).when(httpsURLConnection).getResponseCode();
+
+ DownloadStatus downloadStatus = httpAgentSpy.downloadFromUrl(TEST_IMAGE_DOWNLOAD_ENDPOINT, TEST_FILE_NAME);
+ Assert.assertNotNull(downloadStatus);
+ Assert.assertEquals("Download failed.", downloadStatus.displayValue());
+
+
+ }
+
+ @Test
+ public void testDownloadFromUrlReturnsCorrectResponseIfConnectionStatusIsNOT200() throws Exception {
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(dirFile).when(httpAgentSpy).getSDCardDownloadPath();
+ Mockito.doReturn(file).when(httpAgentSpy).getFile(TEST_FILE_NAME, dirFile);
+ Mockito.doReturn(true).when(dirFile).exists();
+
+ Mockito.doReturn(httpsURLConnection).when(httpAgentSpy).getHttpURLConnection(TEST_IMAGE_DOWNLOAD_ENDPOINT);
+ Mockito.doReturn(HttpURLConnection.HTTP_NOT_FOUND).when(httpsURLConnection).getResponseCode();
+
+ DownloadStatus downloadStatus = httpAgentSpy.downloadFromUrl(TEST_IMAGE_DOWNLOAD_ENDPOINT, TEST_FILE_NAME);
+ Assert.assertNotNull(downloadStatus);
+ Assert.assertEquals("Download failed.", downloadStatus.displayValue());
+
+
+ }
+
}
From c9c4a0df78c261f17a269aa253bb48cb43e18d95 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Mon, 8 Jun 2020 22:37:29 +0300
Subject: [PATCH 23/70] Refactor client core : Remove password type String -
Remove type String for password processing to mitigates against the heap dump
attack vector exposed by Java String type - Fix unit tests
Signed-off-by: Martin Ndegwa
---
.../smartregister/util/FakeUserService.java | 14 +--
.../login/interactor/BaseLoginInteractor.java | 22 ++--
.../login/model/BaseLoginModel.java | 6 +-
.../login/presenter/BaseLoginPresenter.java | 4 +-
.../login/task/RemoteLoginTask.java | 4 +-
.../repository/AllSharedPreferences.java | 14 ---
.../smartregister/repository/Repository.java | 6 +-
.../security/SecurityHelper.java | 111 ++++++++++++++++++
.../org/smartregister/service/HTTPAgent.java | 22 ++--
.../smartregister/service/UserService.java | 103 ++++++++--------
.../java/org/smartregister/util/Session.java | 8 +-
.../view/activity/BaseLoginActivity.java | 5 +-
.../view/activity/DrishtiApplication.java | 6 +-
.../view/activity/LoginActivity.java | 21 ++--
.../view/contract/BaseLoginContract.java | 6 +-
.../org/smartregister/TestApplication.java | 4 +-
.../presenter/BaseLoginPresenterTest.java | 3 +-
.../repository/AllSharedPreferencesTest.java | 12 --
.../repository/mock/RepositoryMock.java | 2 +-
.../smartregister/service/HTTPAgentTest.java | 12 +-
.../service/UserServiceTest.java | 22 ++--
.../view/activity/LoginActivityTest.java | 4 +-
.../LoginActivityWithRemoteLoginTest.java | 6 +-
23 files changed, 259 insertions(+), 158 deletions(-)
create mode 100644 opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java
diff --git a/opensrp-app/src/androidTest/java/org/smartregister/util/FakeUserService.java b/opensrp-app/src/androidTest/java/org/smartregister/util/FakeUserService.java
index 21f03d6cd..7a229ea35 100644
--- a/opensrp-app/src/androidTest/java/org/smartregister/util/FakeUserService.java
+++ b/opensrp-app/src/androidTest/java/org/smartregister/util/FakeUserService.java
@@ -22,29 +22,29 @@ public FakeUserService() {
}
@Override
- public boolean isValidLocalLogin(String userName, String password) {
+ public boolean isValidLocalLogin(String userName, char[] password) {
assertExpectedCredentials(userName, password);
actualCalls.add("local");
return shouldSucceedLocalLogin;
}
@Override
- public LoginResponse isValidRemoteLogin(String userName, String password) {
+ public LoginResponse isValidRemoteLogin(String userName, char[] password) {
assertExpectedCredentials(userName, password);
actualCalls.add("remote");
return shouldSucceedRemoteLogin;
}
@Override
- public void localLogin(String userName, String password) {
- super.setupContextForLogin(userName, password);
+ public void localLogin(String userName, char[] password) {
+ super.setupContextForLogin(password);
actualCalls.add("login");
assertExpectedCredentials(userName, password);
}
- public void remoteLogin(String userName, String password, String anmLocation) {
- super.setupContextForLogin(userName, password);
+ public void remoteLogin(String userName, char[] password, String anmLocation) {
+ super.setupContextForLogin(password);
actualCalls.add("login");
assertExpectedCredentials(userName, password);
}
@@ -65,7 +65,7 @@ public void logoutSession() {
super.logoutSession();
}
- private void assertExpectedCredentials(String userName, String password) {
+ private void assertExpectedCredentials(String userName, char[] password) {
if (!expectedUserName.equals(userName)) {
throw new RuntimeException("Expected user: " + expectedUserName + ". Actual: " + userName);
}
diff --git a/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java b/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java
index fbca7463e..4c64006eb 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java
@@ -61,12 +61,12 @@ public void onDestroy(boolean isChangingConfiguration) {
}
@Override
- public void login(WeakReference view, String userName, String password) {
+ public void login(WeakReference view, String userName, char[] password) {
loginWithLocalFlag(view, !getSharedPreferences().fetchForceRemoteLogin()
&& userName.equalsIgnoreCase(getSharedPreferences().fetchRegisteredANM()), userName, password);
}
- public void loginWithLocalFlag(WeakReference view, boolean localLogin, String userName, String password) {
+ public void loginWithLocalFlag(WeakReference view, boolean localLogin, String userName, char[] password) {
getLoginView().hideKeyboard();
getLoginView().enableLoginButton(false);
@@ -79,7 +79,7 @@ public void loginWithLocalFlag(WeakReference view, boole
Timber.i("Login result finished " + DateTime.now().toString());
}
- private void localLogin(WeakReference view, String userName, String password) {
+ private void localLogin(WeakReference view, String userName, char[] password) {
getLoginView().enableLoginButton(true);
boolean isAuthenticated = getUserService().isUserInValidGroup(userName, password);
if (!isAuthenticated) {
@@ -88,16 +88,16 @@ private void localLogin(WeakReference view, String userN
} else if (isAuthenticated && (!AllConstants.TIME_CHECK || TimeStatus.OK.equals(getUserService().validateStoredServerTimeZone()))) {
- navigateToHomePage(userName);
+ navigateToHomePage(userName, password);
} else {
loginWithLocalFlag(view, false, userName, password);
}
}
- private void navigateToHomePage(String userName) {
+ private void navigateToHomePage(String userName, char[] password) {
- getUserService().localLogin(userName);
+ getUserService().localLogin(userName, password);
getLoginView().goToHome(false);
CoreLibrary.getInstance().initP2pLibrary(userName);
@@ -116,7 +116,7 @@ public void run() {
}).start();
}
- private void remoteLogin(final String userName, final String password, final AccountAuthenticatorXml accountAuthenticatorXml) {
+ private void remoteLogin(final String userName, final char[] password, final AccountAuthenticatorXml accountAuthenticatorXml) {
try {
if (getSharedPreferences().fetchBaseURL("").isEmpty() && StringUtils.isNotBlank(this.getApplicationContext().getString(R.string.opensrp_url))) {
@@ -136,7 +136,7 @@ public void onEvent(LoginResponse loginResponse) {
);
if (!AllConstants.TIME_CHECK || timeStatus.equals(TimeStatus.OK)) {
- remoteLoginWith(username, loginResponse);
+ remoteLoginWith(username, password, loginResponse);
} else {
if (timeStatus.equals(TimeStatus.TIMEZONE_MISMATCH)) {
@@ -199,7 +199,7 @@ public void onComplete() {
}
}
- private void tryRemoteLogin(final String userName, final String password, final AccountAuthenticatorXml accountAuthenticatorXml, final Listener afterLogincheck) {
+ private void tryRemoteLogin(final String userName, final char[] password, final AccountAuthenticatorXml accountAuthenticatorXml, final Listener afterLogincheck) {
if (remoteLoginTask != null && !remoteLoginTask.isCancelled()) {
remoteLoginTask.cancel(true);
}
@@ -207,8 +207,8 @@ private void tryRemoteLogin(final String userName, final String password, final
remoteLoginTask.execute();
}
- private void remoteLoginWith(String userName, LoginResponse loginResponse) {
- getUserService().processLoginResponseDataForUser(userName, loginResponse.payload());
+ private void remoteLoginWith(String userName, char[] password, LoginResponse loginResponse) {
+ getUserService().processLoginResponseDataForUser(userName, password, loginResponse.payload());
processServerSettings(loginResponse);
scheduleJobsPeriodically();
diff --git a/opensrp-app/src/main/java/org/smartregister/login/model/BaseLoginModel.java b/opensrp-app/src/main/java/org/smartregister/login/model/BaseLoginModel.java
index 8047a5237..fc9f6a345 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/model/BaseLoginModel.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/model/BaseLoginModel.java
@@ -12,13 +12,13 @@ public class BaseLoginModel implements BaseLoginContract.Model {
@Override
public org.smartregister.Context getOpenSRPContext() {
- return CoreLibrary.getInstance().context();
+ return CoreLibrary.getInstance().context();
}
@Override
- public boolean isPasswordValid(String password) {
- return !TextUtils.isEmpty(password) && password.length() > 1;
+ public boolean isPasswordValid(char[] password) {
+ return password != null && password.length > 1;
}
@Override
diff --git a/opensrp-app/src/main/java/org/smartregister/login/presenter/BaseLoginPresenter.java b/opensrp-app/src/main/java/org/smartregister/login/presenter/BaseLoginPresenter.java
index b1b4d246d..9dd86e6e9 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/presenter/BaseLoginPresenter.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/presenter/BaseLoginPresenter.java
@@ -42,7 +42,7 @@ public void onDestroy(boolean isChangingConfiguration) {
}
@Override
- public void attemptLogin(String username, String password) {
+ public void attemptLogin(String username, char[] password) {
if (!mLoginView.get().isAppVersionAllowed()) {
getLoginView().showErrorDialog(getLoginView()
.getActivityContext().getResources().getString(R.string.outdated_app));
@@ -69,7 +69,7 @@ public void attemptLogin(String username, String password) {
}
if (!cancel) {
- mLoginInteractor.login(mLoginView, username.trim(), password.trim());
+ mLoginInteractor.login(mLoginView, username.trim(), password);
}
}
diff --git a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
index 6052d4b3e..280a181e9 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
@@ -37,12 +37,12 @@ public class RemoteLoginTask extends AsyncTask {
private BaseLoginContract.View mLoginView;
private final String mUsername;
- private final String mPassword;
+ private final char[] mPassword;
private final AccountAuthenticatorXml mAccountAuthenticatorXml;
private final Listener afterLoginCheck;
- public RemoteLoginTask(BaseLoginContract.View loginView, String username, String password, AccountAuthenticatorXml accountAuthenticatorXml, Listener afterLoginCheck) {
+ public RemoteLoginTask(BaseLoginContract.View loginView, String username, char[] password, AccountAuthenticatorXml accountAuthenticatorXml, Listener afterLoginCheck) {
mLoginView = loginView;
mUsername = username;
mPassword = password;
diff --git a/opensrp-app/src/main/java/org/smartregister/repository/AllSharedPreferences.java b/opensrp-app/src/main/java/org/smartregister/repository/AllSharedPreferences.java
index 8647aee7c..8c374f56c 100644
--- a/opensrp-app/src/main/java/org/smartregister/repository/AllSharedPreferences.java
+++ b/opensrp-app/src/main/java/org/smartregister/repository/AllSharedPreferences.java
@@ -15,7 +15,6 @@
import static org.smartregister.AllConstants.DEFAULT_TEAM_ID_PREFIX;
import static org.smartregister.AllConstants.DEFAULT_TEAM_PREFIX;
import static org.smartregister.AllConstants.DRISHTI_BASE_URL;
-import static org.smartregister.AllConstants.ENCRYPTED_GROUP_ID_PREFIX;
import static org.smartregister.AllConstants.FORCE_REMOTE_LOGIN;
import static org.smartregister.AllConstants.IS_SYNC_INITIAL_KEY;
import static org.smartregister.AllConstants.IS_SYNC_IN_PROGRESS_PREFERENCE_KEY;
@@ -144,19 +143,6 @@ public void saveCurrentLocality(String currentLocality) {
preferences.edit().putString(CURRENT_LOCALITY, currentLocality).commit();
}
- public String fetchEncryptedGroupId(String username) {
- if (username != null) {
- return preferences.getString(ENCRYPTED_GROUP_ID_PREFIX + username, null);
- }
- return null;
- }
-
- public void saveEncryptedGroupId(String username, String groupId) {
- if (username != null) {
- preferences.edit().putString(ENCRYPTED_GROUP_ID_PREFIX + username, groupId).commit();
- }
- }
-
public String fetchLanguagePreference() {
return preferences.getString(LANGUAGE_PREFERENCE_KEY, DEFAULT_LOCALE).trim();
}
diff --git a/opensrp-app/src/main/java/org/smartregister/repository/Repository.java b/opensrp-app/src/main/java/org/smartregister/repository/Repository.java
index f1b5c1b6a..81c4d32b4 100755
--- a/opensrp-app/src/main/java/org/smartregister/repository/Repository.java
+++ b/opensrp-app/src/main/java/org/smartregister/repository/Repository.java
@@ -156,7 +156,7 @@ public SQLiteDatabase getWritableDatabase() {
return getWritableDatabase(password());
}
- private boolean isDatabaseWritable(String password) {
+ private boolean isDatabaseWritable(char[] password) {
SQLiteDatabase database = SQLiteDatabase
.openDatabase(databasePath.getPath(), password, null,
SQLiteDatabase.OPEN_READONLY, hook);
@@ -164,7 +164,7 @@ private boolean isDatabaseWritable(String password) {
return true;
}
- public boolean canUseThisPassword(String password) {
+ public boolean canUseThisPassword(char[] password) {
try {
return isDatabaseWritable(password);
} catch (SQLiteException e) {
@@ -190,7 +190,7 @@ public boolean canUseThisPassword(String password) {
}
}
- private String password() {
+ private char[] password() {
return DrishtiApplication.getInstance().getPassword();
}
diff --git a/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java b/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java
new file mode 100644
index 000000000..91b4f3d7a
--- /dev/null
+++ b/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java
@@ -0,0 +1,111 @@
+package org.smartregister.security;
+
+import android.text.Editable;
+
+import org.apache.commons.codec.CharEncoding;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.util.Arrays;
+
+/**
+ * Created by ndegwamartin on 04/06/2020.
+ */
+public class SecurityHelper {
+
+ private static Charset charset = Charset.forName(CharEncoding.UTF_8);
+
+ /**
+ * This method ensures that sensitive info can be collected for the edit text in a safer way
+ */
+ public static char[] readValue(Editable editable) {
+
+ char[] chars = new char[editable.length()];
+ editable.getChars(0, editable.length(), chars, 0);
+
+ editable.clear();
+
+ return chars;
+ }
+
+ /**
+ * This method allows us to overwrite byte array data
+ *
+ * @param array character array
+ */
+ public static void clearArray(byte[] array) {
+
+ Arrays.fill(array, (byte) 0);
+ }
+
+ /**
+ * This method allows us to overwrite byte array data thus removing original values from memory
+ *
+ * @param array character array
+ */
+ public static void clearArray(char[] array) {
+ Arrays.fill(array, '*');
+ }
+
+ /**
+ * This method converts characters in the string buffer to byte array without creating a String object
+ *
+ * @param stringBuffer
+ * @return an array of bytes , a conversion from the string buffer
+ */
+
+ public static byte[] toBytes(StringBuffer stringBuffer) throws CharacterCodingException {
+
+ CharsetEncoder encoder = charset.newEncoder();
+
+ CharBuffer buffer = CharBuffer.wrap(stringBuffer);
+
+ ByteBuffer bytesBuffer = encoder.encode(buffer);
+
+ byte[] bytes = bytesBuffer.array();
+
+ clearArray(bytesBuffer.array());
+
+ clearStringBuffer(stringBuffer);
+
+ return bytes;
+ }
+
+ private static void clearStringBuffer(StringBuffer stringBuffer) {
+ stringBuffer.setLength(0);
+ stringBuffer.append("*");
+ }
+
+ /**
+ * This method converts characters in the string buffer to byte array without creating a String object
+ *
+ * @param chars array
+ * @return an array of bytes , a conversion from the chars array
+ */
+ public static byte[] toBytes(char[] chars) {
+ CharBuffer charBuffer = CharBuffer.wrap(chars);
+
+ ByteBuffer byteBuffer = charset.encode(charBuffer);
+
+ byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
+
+ clearArray(byteBuffer.array());
+
+ return bytes;
+
+ }
+
+ public static char[] toChars(byte[] bytes) {
+
+ char[] convertedChar = new char[bytes.length];
+ for (int i = 0; i < bytes.length; i++) {
+ convertedChar[i] = (char) bytes[i];
+ }
+
+ return convertedChar;
+
+ }
+}
diff --git a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
index f69121a4a..9a9e962bd 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
@@ -30,6 +30,7 @@
import org.smartregister.domain.ResponseStatus;
import org.smartregister.domain.jsonmapping.LoginResponseData;
import org.smartregister.repository.AllSharedPreferences;
+import org.smartregister.security.SecurityHelper;
import org.smartregister.ssl.OpensrpSSLHelper;
import org.smartregister.util.Utils;
@@ -223,7 +224,7 @@ private void logResponse(String postURLPath, String jsonPayload) {
Timber.d("postURLPath: %s and jsonPayLoad: %s", postURLPath, jsonPayload);
}
- public LoginResponse urlCanBeAccessWithGivenCredentials(String requestURL, String userName, String password) {
+ public LoginResponse urlCanBeAccessWithGivenCredentials(String requestURL, String userName, char[] password) {
LoginResponse loginResponse = null;
HttpURLConnection urlConnection = null;
String url = null;
@@ -231,7 +232,8 @@ public LoginResponse urlCanBeAccessWithGivenCredentials(String requestURL, Strin
url = requestURL.replaceAll("\\s+", "");
urlConnection = initializeHttp(url, false);
- final String basicAuth = AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BASIC + " " + Base64.encodeToString((userName + ":" + password).getBytes(), Base64.NO_WRAP);
+ byte[] credentials = SecurityHelper.toBytes(new StringBuffer(userName).append(':').append(password));//(new StringBuffer(userName).append(':').append(password)).toString().getBytes();
+ final String basicAuth = AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BASIC + " " + Base64.encodeToString(credentials, Base64.NO_WRAP);
urlConnection.setRequestProperty(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, basicAuth);
int statusCode = urlConnection.getResponseCode();
InputStream inputStream;
@@ -551,7 +553,7 @@ public void setReadTimeout(int readTimeout) {
this.readTimeout = readTimeout;
}
- public AccountResponse oauth2authenticateCore(StringBuilder requestParamBuilder, String grantType, String tokenEndpointURL) {
+ public AccountResponse oauth2authenticateCore(StringBuffer requestParamBuffer, String grantType, String tokenEndpointURL) {
AccountError accountError;
@@ -568,11 +570,11 @@ public AccountResponse oauth2authenticateCore(StringBuilder requestParamBuilder,
final String base64Auth = BaseEncoding.base64().encode(new String(clientId + ":" + clientSecret).getBytes());
- requestParamBuilder.append("&grant_type=").append(grantType);
- requestParamBuilder.append("&client_id=").append(clientId);
- requestParamBuilder.append("&client_secret=").append(clientSecret);
+ requestParamBuffer.append("&grant_type=").append(grantType);
+ requestParamBuffer.append("&client_id=").append(clientId);
+ requestParamBuffer.append("&client_secret=").append(clientSecret);
- byte[] postData = requestParamBuilder.toString().getBytes(CharEncoding.UTF_8);
+ byte[] postData = requestParamBuffer.toString().getBytes(CharEncoding.UTF_8);
int postDataLength = postData.length;
urlConnection.setFixedLengthStreamingMode(postDataLength);
@@ -637,9 +639,9 @@ public AccountResponse oauth2authenticateCore(StringBuilder requestParamBuilder,
}
- public AccountResponse oauth2authenticate(String username, String password, String grantType, String tokenEndpointURL) {
+ public AccountResponse oauth2authenticate(String username, char[] password, String grantType, String tokenEndpointURL) {
- StringBuilder requestParamBuilder = new StringBuilder();
+ StringBuffer requestParamBuilder = new StringBuffer();
requestParamBuilder.append("&username=").append(username);
requestParamBuilder.append("&password=").append(password);
@@ -649,7 +651,7 @@ public AccountResponse oauth2authenticate(String username, String password, Stri
public AccountResponse oauth2authenticateRefreshToken(String refreshToken) {
String tokenEndpointURL = allSharedPreferences.getPreferences().getString(AccountHelper.CONFIGURATION_CONSTANTS.TOKEN_ENDPOINT_URL, "");
- StringBuilder requestParamBuilder = new StringBuilder();
+ StringBuffer requestParamBuilder = new StringBuffer();
requestParamBuilder.append("&refresh_token=").append(refreshToken);
return oauth2authenticateCore(requestParamBuilder, AccountHelper.OAUTH.GRANT_TYPE.REFRESH_TOKEN, tokenEndpointURL);
diff --git a/opensrp-app/src/main/java/org/smartregister/service/UserService.java b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
index 01b199547..e105de17f 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/UserService.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
@@ -24,6 +24,7 @@
import org.smartregister.domain.jsonmapping.util.TeamMember;
import org.smartregister.repository.AllSettings;
import org.smartregister.repository.AllSharedPreferences;
+import org.smartregister.security.SecurityHelper;
import org.smartregister.sync.SaveANMLocationTask;
import org.smartregister.sync.SaveANMTeamTask;
import org.smartregister.sync.SaveUserInfoTask;
@@ -71,7 +72,6 @@ public class UserService {
private static final String KEYSTORE = "AndroidKeyStore";
private static final String CIPHER = "RSA/ECB/PKCS1Padding";
private static final String CIPHER_PROVIDER = "AndroidOpenSSL";
- private static final String CIPHER_TEXT_CHARACTER_CODE = "UTF-8";
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
@@ -207,19 +207,19 @@ public TimeStatus validateDeviceTime(LoginResponseData userInfo, long serverTime
return TimeStatus.ERROR;
}
- public boolean isValidLocalLogin(String userName, String password) {
+ public boolean isValidLocalLogin(String userName, char[] password) {
return allSharedPreferences.fetchRegisteredANM().equals(userName) && DrishtiApplication.getInstance().getRepository()
.canUseThisPassword(password) && !allSharedPreferences.fetchForceRemoteLogin();
}
- public boolean isUserInValidGroup(final String userName, final String password) {
+ public boolean isUserInValidGroup(final String userName, final char[] password) {
// Check if everything OK for local login
if (keyStore != null && userName != null && password != null && !allSharedPreferences.fetchForceRemoteLogin()) {
String username = userName.equalsIgnoreCase(allSharedPreferences.fetchRegisteredANM()) ? allSharedPreferences.fetchRegisteredANM() : userName;
try {
KeyStore.PrivateKeyEntry privateKeyEntry = getUserKeyPair(username);
if (privateKeyEntry != null) {
- String groupId = getGroupId(username, privateKeyEntry);
+ char[] groupId = getGroupId(username, privateKeyEntry);
if (groupId != null) {
return isValidGroupId(groupId);
}
@@ -232,11 +232,11 @@ public boolean isUserInValidGroup(final String userName, final String password)
return false;
}
- private boolean isValidGroupId(String groupId) {
+ private boolean isValidGroupId(char[] groupId) {
return DrishtiApplication.getInstance().getRepository().canUseThisPassword(groupId);
}
- public String getGroupId(String userName) {
+ public char[] getGroupId(String userName) {
if (keyStore != null && userName != null) {
try {
KeyStore.PrivateKeyEntry privateKeyEntry = getUserKeyPair(userName);
@@ -248,12 +248,14 @@ public String getGroupId(String userName) {
return null;
}
- public String getGroupId(String userName, KeyStore.PrivateKeyEntry privateKeyEntry) {
+ public char[] getGroupId(String userName, KeyStore.PrivateKeyEntry privateKeyEntry) {
if (privateKeyEntry != null) {
- String encryptedGroupId = allSharedPreferences.fetchEncryptedGroupId(userName);
+
+ String encryptedGroupId = AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType());
+
if (encryptedGroupId != null) {
try {
- return decryptString(privateKeyEntry, encryptedGroupId);
+ return SecurityHelper.toChars(decryptString(privateKeyEntry, encryptedGroupId));
} catch (Exception e) {
Timber.e(e);
}
@@ -274,8 +276,8 @@ public boolean isUserInPioneerGroup(String userName) {
if (userName.equals(pioneerUser)) {
return true;
} else {
- String userGroupId = getGroupId(userName);
- String pioneerGroupId = getGroupId(pioneerUser);
+ char[] userGroupId = getGroupId(userName);
+ char[] pioneerGroupId = getGroupId(pioneerUser);
if (userGroupId != null && userGroupId.equals(pioneerGroupId)) {
return isValidGroupId(userGroupId);
@@ -295,7 +297,7 @@ public LoginResponse fetchUserDetails(String accessToken) {
return loginResponse;
}
- public LoginResponse isValidRemoteLogin(String userName, String password) {
+ public LoginResponse isValidRemoteLogin(String userName, char[] password) {
String requestURL;
requestURL = configuration.dristhiBaseURL() + OPENSRP_AUTH_USER_URL_PATH;
@@ -318,20 +320,27 @@ public Response getLocationInformation() {
return httpAgent.fetch(requestURL);
}
- private boolean loginWith(String userName) {
+ private boolean loginWith(String userName, char[] password) {
boolean loginSuccessful = true;
- String encryptedGroupId = allSharedPreferences.fetchEncryptedGroupId(userName);
- try {
- KeyStore.PrivateKeyEntry privateKeyEntry = getUserKeyPair(userName);
- if (privateKeyEntry != null) {
- String groupId = decryptString(privateKeyEntry, encryptedGroupId);
- setupContextForLogin(userName, groupId);
+
+ if (usesGroupIdAsDBPassword(userName)) {
+
+ String encryptedGroupId = AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType());
+
+ try {
+ KeyStore.PrivateKeyEntry privateKeyEntry = getUserKeyPair(userName);
+ if (privateKeyEntry != null) {
+ byte[] groupId = Base64.decode(encryptedGroupId, Base64.DEFAULT);
+ setupContextForLogin(SecurityHelper.toChars(decryptString(privateKeyEntry, encryptedGroupId)));
+ SecurityHelper.clearArray(groupId);
+ }
+ } catch (Exception e) {
+ Timber.e(e);
+ loginSuccessful = false;
}
- } catch (Exception e) {
- Timber.e(e);
- loginSuccessful = false;
+ } else {
+ setupContextForLogin(password);
}
-
String username = userName;
if (allSharedPreferences.fetchRegisteredANM().equalsIgnoreCase(userName))
username = allSharedPreferences.fetchRegisteredANM();
@@ -350,7 +359,7 @@ private boolean loginWith(String userName) {
private boolean usesGroupIdAsDBPassword(String userName) {
try {
if (keyStore != null && keyStore.containsAlias(userName)) {
- if (allSharedPreferences.fetchEncryptedGroupId(userName) != null) {
+ if (AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType()) != null) {
return true;
}
}
@@ -360,14 +369,14 @@ private boolean usesGroupIdAsDBPassword(String userName) {
return false;
}
- public void localLogin(String userName) {
- loginWith(userName);
+ public void localLogin(String userName, char[] password) {
+ loginWith(userName, password);
}
- public void processLoginResponseDataForUser(String userName, LoginResponseData userInfo) {
+ public void processLoginResponseDataForUser(String userName, char[] password, LoginResponseData userInfo) {
String username = userInfo.user != null && StringUtils.isNotBlank(userInfo.user.getUsername())
? userInfo.user.getUsername() : userName;
- boolean loginSuccessful = loginWith(username);
+ boolean loginSuccessful = loginWith(username, password);
saveAnmLocation(getUserLocation(userInfo));
saveAnmTeam(getUserTeam(userInfo));
saveUserInfo(getUserData(userInfo));
@@ -545,7 +554,7 @@ public void saveUserInfo(User user) {
* @param userInfo The user's info from the
* endpoint (should contain the {team}.{team}.{uuid} key)
*/
- public Bundle saveUserGroup(String userName, String password, LoginResponseData userInfo) {
+ public Bundle saveUserGroup(String userName, char[] password, LoginResponseData userInfo) {
Bundle bundle = new Bundle();
String username = userInfo.user != null && StringUtils.isNotBlank(userInfo.user.getUsername()) ? userInfo.user.getUsername() : userName;
@@ -553,6 +562,9 @@ public Bundle saveUserGroup(String userName, String password, LoginResponseData
if (keyStore != null && username != null) {
+
+ byte[] groupId = null;
+
try {
KeyStore.PrivateKeyEntry privateKeyEntry = createUserKeyPair(username);
@@ -560,21 +572,19 @@ public Bundle saveUserGroup(String userName, String password, LoginResponseData
return null;
}
- String groupId = null;
-
SyncConfiguration syncConfiguration = CoreLibrary.getInstance().getSyncConfiguration();
if (syncConfiguration.getEncryptionParam() != null) {
SyncFilter syncFilter = syncConfiguration.getEncryptionParam();
if (SyncFilter.TEAM.equals(syncFilter) || SyncFilter.TEAM_ID.equals(syncFilter)) {
- groupId = getUserDefaultTeamId(userInfo);
+ groupId = SecurityHelper.toBytes(getUserDefaultTeamId(userInfo).toCharArray());
} else if (SyncFilter.LOCATION.equals(syncFilter) || SyncFilter.LOCATION_ID.equals(syncFilter)) {
- groupId = getUserLocationId(userInfo);
+ groupId = SecurityHelper.toBytes(getUserLocationId(userInfo).toCharArray());
} else if (SyncFilter.PROVIDER.equals(syncFilter)) {
- groupId = username + "-" + password;
+ groupId = SecurityHelper.toBytes(new StringBuffer(username).append('-').append(password));
}
}
- if (StringUtils.isBlank(groupId)) {
+ if (groupId == null || groupId.length < 1) {
return null;
}
@@ -582,8 +592,6 @@ public Bundle saveUserGroup(String userName, String password, LoginResponseData
// Then save the encrypted group
String encryptedGroupId = encryptString(privateKeyEntry, groupId);
- allSharedPreferences.saveEncryptedGroupId(username, encryptedGroupId);
-
bundle.putString(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, encryptedGroupId);
// Finally, save the pioneer user
@@ -593,6 +601,10 @@ public Bundle saveUserGroup(String userName, String password, LoginResponseData
}
} catch (Exception e) {
Timber.e(e);
+ } finally {
+
+ SecurityHelper.clearArray(password);
+ SecurityHelper.clearArray(groupId);
}
}
@@ -619,10 +631,10 @@ public boolean hasSessionExpired() {
return session().hasExpired();
}
- protected void setupContextForLogin(String userName, String password) {
+ protected void setupContextForLogin(char[] password) {
session().start(session().lengthInMilliseconds());
configuration.getDrishtiApplication().setPassword(password);
- session().setPassword(password);
+ session().setPassword(SecurityHelper.toBytes(password));
}
protected Session session() {
@@ -687,12 +699,11 @@ private KeyStore.PrivateKeyEntry createUserKeyPair(final String username) throws
*
* @param privateKeyEntry Keypair to use to decrypt the string
* @param cipherText String to be decrypted
- * @return Plain text derived from the cipher text
+ * @return char array of text derived from the cipher text
* @throws Exception
*/
@VisibleForTesting
- protected String decryptString(KeyStore.PrivateKeyEntry privateKeyEntry, String cipherText)
- throws Exception {
+ protected byte[] decryptString(KeyStore.PrivateKeyEntry privateKeyEntry, String cipherText)throws Exception {
Cipher output;
if (Build.VERSION.SDK_INT >= 23) {
@@ -718,18 +729,18 @@ protected String decryptString(KeyStore.PrivateKeyEntry privateKeyEntry, String
bytes[i] = values.get(i);
}
- return new String(bytes, 0, bytes.length, CIPHER_TEXT_CHARACTER_CODE);
+ return bytes;
}
/**
* Encrypts a string using the provided keypair
*
* @param privateKeyEntry The keypair to use to encrypt the text
- * @param plainText The plain text to encrypt (should be at most 256bytes)
+ * @param plainTextBytes The plain text to encrypt (should be at most 256bytes)
* @return Cipher text corresponding to the plain text
* @throws Exception
*/
- private String encryptString(KeyStore.PrivateKeyEntry privateKeyEntry, String plainText)
+ private String encryptString(KeyStore.PrivateKeyEntry privateKeyEntry, byte[] plainTextBytes)
throws Exception {
RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey();
@@ -743,7 +754,7 @@ private String encryptString(KeyStore.PrivateKeyEntry privateKeyEntry, String pl
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, input);
- cipherOutputStream.write(plainText.getBytes(CIPHER_TEXT_CHARACTER_CODE));
+ cipherOutputStream.write(plainTextBytes);
cipherOutputStream.close();
byte[] vals = outputStream.toByteArray();
diff --git a/opensrp-app/src/main/java/org/smartregister/util/Session.java b/opensrp-app/src/main/java/org/smartregister/util/Session.java
index 0766aac52..b315dc52f 100644
--- a/opensrp-app/src/main/java/org/smartregister/util/Session.java
+++ b/opensrp-app/src/main/java/org/smartregister/util/Session.java
@@ -1,6 +1,7 @@
package org.smartregister.util;
import org.smartregister.AllConstants;
+import org.smartregister.security.SecurityHelper;
import java.util.Date;
@@ -9,7 +10,7 @@
import static org.joda.time.DateTimeConstants.SECONDS_PER_MINUTE;
public class Session {
- private String password;
+ private byte[] password;
private String repositoryName = AllConstants.DATABASE_NAME;
private long sessionExpiryTime = 0;
@@ -17,7 +18,7 @@ public long lengthInMilliseconds() {
return 24 * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MILLIS_PER_SECOND;
}
- public String password() {
+ public byte[] password() {
return password;
}
@@ -25,7 +26,7 @@ public String repositoryName() {
return repositoryName;
}
- public Session setPassword(String password) {
+ public Session setPassword(byte[] password) {
this.password = password;
return this;
}
@@ -51,6 +52,7 @@ public boolean hasExpired() {
}
public void expire() {
+ SecurityHelper.clearArray(this.password);
setSessionExpiryTimeTo(new Date().getTime() - 1);
}
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java b/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java
index cb45af0dc..b21445e79 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java
@@ -28,6 +28,7 @@
import org.joda.time.DateTime;
import org.smartregister.R;
import org.smartregister.account.AccountHelper;
+import org.smartregister.security.SecurityHelper;
import org.smartregister.util.SyncUtils;
import org.smartregister.util.Utils;
import org.smartregister.view.contract.BaseLoginContract;
@@ -207,7 +208,7 @@ public void enableLoginButton(boolean isClickable) {
public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
if (actionId == R.integer.login || actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE) {
String username = userNameEditText.getText().toString();
- String password = passwordEditText.getText().toString();
+ char[] password = SecurityHelper.readValue(passwordEditText.getText());
mLoginPresenter.attemptLogin(username, password);
return true;
}
@@ -218,7 +219,7 @@ public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent
public void onClick(View v) {
if (v.getId() == R.id.login_login_btn) {
String username = userNameEditText.getText().toString();
- String password = passwordEditText.getText().toString();
+ char[] password = SecurityHelper.readValue(passwordEditText.getText());
mLoginPresenter.attemptLogin(username, password);
}
}
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java b/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java
index 2a6e31d0b..51a969643 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java
@@ -38,7 +38,7 @@ public abstract class DrishtiApplication extends Application {
protected Locale locale = null;
protected Context context;
protected Repository repository;
- private String password;
+ private char[] password;
private String username;
public static synchronized X getInstance() {
@@ -117,7 +117,7 @@ public Repository getRepository() {
return repository;
}
- public String getPassword() {
+ public char[] getPassword() {
if (password == null) {
String username = context.userService().getAllSharedPreferences().fetchRegisteredANM();
@@ -127,7 +127,7 @@ public String getPassword() {
return password;
}
- public void setPassword(String password) {
+ public void setPassword(char[] password) {
this.password = password;
}
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java b/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java
index 26a389cb7..ddee8f121 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java
@@ -25,6 +25,7 @@
import org.smartregister.domain.LoginResponse;
import org.smartregister.domain.jsonmapping.LoginResponseData;
import org.smartregister.event.Listener;
+import org.smartregister.security.SecurityHelper;
import org.smartregister.sync.DrishtiSyncScheduler;
import org.smartregister.util.LangUtils;
import org.smartregister.util.SyncUtils;
@@ -117,7 +118,7 @@ public void login(final View view) {
view.setClickable(false);
final String userName = userNameEditText.getText().toString();
- final String password = passwordEditText.getText().toString();
+ final char[] password = SecurityHelper.readValue(passwordEditText.getText());
if (context.userService().hasARegisteredUser()) {
localLogin(view, userName, password);
@@ -152,7 +153,7 @@ private void initializeProgressDialog() {
progressDialog.setMessage(getString(R.string.loggin_in_dialog_message));
}
- private void localLogin(View view, String userName, String password) {
+ private void localLogin(View view, String userName, char[] password) {
try {
if (!syncUtils.isAppVersionAllowed()) {
showErrorDialog(getString(R.string.outdated_app));
@@ -160,7 +161,7 @@ private void localLogin(View view, String userName, String password) {
}
if (context.userService().isValidLocalLogin(userName, password)) {
- localLoginWith(userName);
+ localLoginWith(userName, password);
} else {
showErrorDialog(getString(R.string.login_failed_dialog_message));
view.setClickable(true);
@@ -171,7 +172,7 @@ private void localLogin(View view, String userName, String password) {
}
- private void remoteLogin(final View view, final String userName, final String password) {
+ private void remoteLogin(final View view, final String userName, final char[] password) {
try {
if (!syncUtils.isAppVersionAllowed()) {
@@ -184,7 +185,7 @@ private void remoteLogin(final View view, final String userName, final String pa
tryRemoteLogin(userName, password, new Listener() {
public void onEvent(LoginResponse loginResponse) {
if (loginResponse == SUCCESS) {
- remoteLoginWith(userName, loginResponse.payload());
+ remoteLoginWith(userName, password, loginResponse.payload());
} else {
if (loginResponse == null) {
showErrorDialog("Login failed. Unknown reason. Try Again");
@@ -216,7 +217,7 @@ public void onClick(DialogInterface dialogInterface, int i) {
dialog.show();
}
- private void tryRemoteLogin(final String userName, final String password, final
+ private void tryRemoteLogin(final String userName, final char[] password, final
Listener afterLoginCheck) {
LockingBackgroundTask task = new LockingBackgroundTask(new ProgressIndicator() {
@Override
@@ -254,14 +255,14 @@ private void hideKeyboard() {
inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), HIDE_NOT_ALWAYS);
}
- private void localLoginWith(String userName) {
- context.userService().localLogin(userName);
+ private void localLoginWith(String userName, char[] password) {
+ context.userService().localLogin(userName, password);
goToHome();
DrishtiSyncScheduler.startOnlyIfConnectedToNetwork(getApplicationContext());
}
- private void remoteLoginWith(String userName, LoginResponseData userInfo) {
- context.userService().processLoginResponseDataForUser(userName, userInfo);
+ private void remoteLoginWith(String userName, char[] password, LoginResponseData userInfo) {
+ context.userService().processLoginResponseDataForUser(userName, password, userInfo);
goToHome();
DrishtiSyncScheduler.startOnlyIfConnectedToNetwork(getApplicationContext());
}
diff --git a/opensrp-app/src/main/java/org/smartregister/view/contract/BaseLoginContract.java b/opensrp-app/src/main/java/org/smartregister/view/contract/BaseLoginContract.java
index a020d8e52..8569a8aa3 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/contract/BaseLoginContract.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/contract/BaseLoginContract.java
@@ -10,7 +10,7 @@
public interface BaseLoginContract {
interface Presenter {
- void attemptLogin(String username, String password);
+ void attemptLogin(String username, char[] password);
View getLoginView();
@@ -69,14 +69,14 @@ interface Interactor {
void onDestroy(boolean isChangingConfiguration);
- void login(WeakReference view, String userName, String password);
+ void login(WeakReference view, String userName, char[] password);
}
interface Model {
boolean isEmptyUsername(String username);
- boolean isPasswordValid(String password);
+ boolean isPasswordValid(char[] password);
org.smartregister.Context getOpenSRPContext();
diff --git a/opensrp-app/src/test/java/org/smartregister/TestApplication.java b/opensrp-app/src/test/java/org/smartregister/TestApplication.java
index c94c83841..c2b4c43e2 100644
--- a/opensrp-app/src/test/java/org/smartregister/TestApplication.java
+++ b/opensrp-app/src/test/java/org/smartregister/TestApplication.java
@@ -41,7 +41,7 @@ public void setContext(Context context) {
}
@Override
- public String getPassword() {
- return "";
+ public char[] getPassword() {
+ return new char[]{};
}
}
diff --git a/opensrp-app/src/test/java/org/smartregister/presenter/BaseLoginPresenterTest.java b/opensrp-app/src/test/java/org/smartregister/presenter/BaseLoginPresenterTest.java
index fbab93cb1..8f5b27116 100644
--- a/opensrp-app/src/test/java/org/smartregister/presenter/BaseLoginPresenterTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/presenter/BaseLoginPresenterTest.java
@@ -1,7 +1,6 @@
package org.smartregister.presenter;
import android.app.Activity;
-import android.content.Context;
import android.content.res.Resources;
import org.junit.Before;
@@ -52,7 +51,7 @@ public void testAttemptLoginShouldFailForUnauthorizedApp() {
doReturn(resources).when(context).getResources();
doReturn("string").when(resources).getString(anyInt());
- presenter.attemptLogin("", "");
+ presenter.attemptLogin("", "".toCharArray());
verify(view).showErrorDialog(any());
}
}
diff --git a/opensrp-app/src/test/java/org/smartregister/repository/AllSharedPreferencesTest.java b/opensrp-app/src/test/java/org/smartregister/repository/AllSharedPreferencesTest.java
index 0aeed77cf..1807a988f 100644
--- a/opensrp-app/src/test/java/org/smartregister/repository/AllSharedPreferencesTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/repository/AllSharedPreferencesTest.java
@@ -101,18 +101,6 @@ public void assertsaveCurrentLocality() {
Mockito.verify(preferences, Mockito.times(1)).edit();
}
- @Test
- public void assertfetchEncryptedGroupId() {
- Assert.assertNull(allSharedPreferences.fetchEncryptedGroupId(null));
- Assert.assertEquals(allSharedPreferences.fetchEncryptedGroupId("uname"), str);
- }
-
- @Test
- public void assertsaveEncryptedGroupId() {
- allSharedPreferences.saveEncryptedGroupId("uname", "Id");
- Mockito.verify(preferences, Mockito.times(1)).edit();
- }
-
@Test
public void assertsaveLanguagePreference() {
allSharedPreferences.saveLanguagePreference("EN");
diff --git a/opensrp-app/src/test/java/org/smartregister/repository/mock/RepositoryMock.java b/opensrp-app/src/test/java/org/smartregister/repository/mock/RepositoryMock.java
index 8430b9d4c..0afbd399f 100644
--- a/opensrp-app/src/test/java/org/smartregister/repository/mock/RepositoryMock.java
+++ b/opensrp-app/src/test/java/org/smartregister/repository/mock/RepositoryMock.java
@@ -50,7 +50,7 @@ public SQLiteDatabase getWritableDatabase() {
}
@Override
- public boolean canUseThisPassword(String password) {
+ public boolean canUseThisPassword(char[] password) {
return super.canUseThisPassword(password);
}
diff --git a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
index be37a2701..1f93c6ea6 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
@@ -124,7 +124,7 @@ public class HTTPAgentTest {
private HTTPAgent httpAgent;
private static final String TEST_USERNAME = "demo";
- private static final String TEST_PASSWORD = "password";
+ private static final char[] TEST_PASSWORD = "password".toCharArray();
public static final String TEST_BASE_URL = "https://my-server.com/";
private static final String TEST_TOKEN_ENDPOINT = "https://my-server.com/oauth/token";
private static final String SECURE_RESOURCE_ENDPOINT = "https://my-server.com/my/secure/resource";
@@ -204,21 +204,21 @@ public void testPostPassesGivenCorrectUrl() {
@Test
public void testUrlCanBeAccessWithGivenCredentials() {
PowerMockito.mockStatic(Base64.class);
- LoginResponse resp = httpAgent.urlCanBeAccessWithGivenCredentials("http://www.mocky.io/v2/5e54de89310000d559eb33d9", "", "");
+ LoginResponse resp = httpAgent.urlCanBeAccessWithGivenCredentials("http://www.mocky.io/v2/5e54de89310000d559eb33d9", "", "".toCharArray());
Assert.assertEquals(LoginResponse.SUCCESS.message(), resp.message());
}
@Test
public void testUrlCanBeAccessWithGivenCredentialsGivenWrongUrl() {
PowerMockito.mockStatic(Base64.class);
- LoginResponse resp = httpAgent.urlCanBeAccessWithGivenCredentials("wrong.url", "", "");
+ LoginResponse resp = httpAgent.urlCanBeAccessWithGivenCredentials("wrong.url", "", "".toCharArray());
Assert.assertEquals(LoginResponse.MALFORMED_URL.message(), resp.message());
}
@Test
public void testUrlCanBeAccessWithGivenCredentialsGivenEmptyResp() {
PowerMockito.mockStatic(Base64.class);
- LoginResponse resp = httpAgent.urlCanBeAccessWithGivenCredentials("http://mockbin.org/bin/e42f7256-18b2-40b9-a20c-40fdc564d06f", "", "");
+ LoginResponse resp = httpAgent.urlCanBeAccessWithGivenCredentials("http://mockbin.org/bin/e42f7256-18b2-40b9-a20c-40fdc564d06f", "", "".toCharArray());
Assert.assertEquals(LoginResponse.SUCCESS_WITH_EMPTY_RESPONSE.message(), resp.message());
}
@@ -562,9 +562,9 @@ public void testOauth2authenticateRefreshTokenInvokesOauth2authenticateCoreWithC
AccountResponse accountResponse = Mockito.mock(AccountResponse.class);
- Mockito.doReturn(accountResponse).when(httpAgentSpy).oauth2authenticateCore(ArgumentMatchers.any(StringBuilder.class), ArgumentMatchers.anyString(), ArgumentMatchers.anyString());
+ Mockito.doReturn(accountResponse).when(httpAgentSpy).oauth2authenticateCore(ArgumentMatchers.any(StringBuffer.class), ArgumentMatchers.anyString(), ArgumentMatchers.anyString());
- ArgumentCaptor requestParamStringBuilder = ArgumentCaptor.forClass(StringBuilder.class);
+ ArgumentCaptor requestParamStringBuilder = ArgumentCaptor.forClass(StringBuffer.class);
ArgumentCaptor grantType = ArgumentCaptor.forClass(String.class);
ArgumentCaptor tokenEndPoint = ArgumentCaptor.forClass(String.class);
diff --git a/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java b/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java
index 0830efa34..4f6cf82aa 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java
@@ -120,13 +120,13 @@ public void shouldUseHttpAgentToDoRemoteLoginCheck() {
when(httpAgent.urlCanBeAccessWithGivenCredentials(
httpAuthenticateUrl,
user,
- password)).thenReturn(loginResponse);
+ password.toCharArray())).thenReturn(loginResponse);
when(allSharedPreferences.fetchRegisteredANM()).thenReturn("user");
- userService.isValidRemoteLogin(user, password);
+ userService.isValidRemoteLogin(user, password.toCharArray());
- verify(httpAgent).urlCanBeAccessWithGivenCredentials(httpAuthenticateUrl, user, password);
+ verify(httpAgent).urlCanBeAccessWithGivenCredentials(httpAuthenticateUrl, user, password.toCharArray());
}
@Test
@@ -163,19 +163,19 @@ public void shouldSaveANMLocation() {
public void shouldConsiderALocalLoginValid() {
// When Username Matches Registered User And Password Matches The One In DB
when(allSharedPreferences.fetchRegisteredANM()).thenReturn("ANM X");
- when(repository.canUseThisPassword("password Z")).thenReturn(true);
+ when(repository.canUseThisPassword("password Z".toCharArray())).thenReturn(true);
- assertTrue(userService.isValidLocalLogin("ANM X", "password Z"));
+ assertTrue(userService.isValidLocalLogin("ANM X", "password Z".toCharArray()));
verify(allSharedPreferences).fetchRegisteredANM();
- verify(repository).canUseThisPassword("password Z");
+ verify(repository).canUseThisPassword("password Z".toCharArray());
}
@Test
public void shouldConsiderALocalLoginInvalidWhenRegisteredUserDoesNotMatch() {
when(allSharedPreferences.fetchRegisteredANM()).thenReturn("ANM X");
- assertFalse(userService.isValidLocalLogin("SOME OTHER ANM", "password"));
+ assertFalse(userService.isValidLocalLogin("SOME OTHER ANM", "password".toCharArray()));
verify(allSharedPreferences).fetchRegisteredANM();
verifyZeroInteractions(repository);
@@ -184,12 +184,12 @@ public void shouldConsiderALocalLoginInvalidWhenRegisteredUserDoesNotMatch() {
@Test
public void shouldConsiderALocalLoginInvalidWhenRegisteredUserMatchesButNotThePassword() {
when(allSharedPreferences.fetchRegisteredANM()).thenReturn("ANM X");
- when(repository.canUseThisPassword("password Z")).thenReturn(false);
+ when(repository.canUseThisPassword("password Z".toCharArray())).thenReturn(false);
- assertFalse(userService.isValidLocalLogin("ANM X", "password Z"));
+ assertFalse(userService.isValidLocalLogin("ANM X", "password Z".toCharArray()));
verify(allSharedPreferences).fetchRegisteredANM();
- verify(repository).canUseThisPassword("password Z");
+ verify(repository).canUseThisPassword("password Z".toCharArray());
}
@Test
@@ -209,7 +209,7 @@ public void logoutCurrentUser() {
when(allSharedPreferences.fetchRegisteredANM()).thenReturn("user X");
- userService.processLoginResponseDataForUser("user X", userInfo);
+ userService.processLoginResponseDataForUser("user X", "password Y".toCharArray(), userInfo);
verify(allSettings).registerANM("user X");
}
diff --git a/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityTest.java b/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityTest.java
index 8c20a2818..624f3de9d 100644
--- a/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityTest.java
@@ -109,7 +109,7 @@ public void assertActivityNotNull() {
@Test
public void localLoginTest() {
Mockito.doReturn(true).when(userService).hasARegisteredUser();
- Mockito.doReturn(true).when(userService).isValidLocalLogin(anyString(), anyString());
+ Mockito.doReturn(true).when(userService).isValidLocalLogin(anyString(), any(char[].class));
Mockito.doReturn(allSharedPreferences).when(context_).allSharedPreferences();
EditText username = activity.findViewById(R.id.login_userNameText);
@@ -121,7 +121,7 @@ public void localLoginTest() {
Button login_button = activity.findViewById(R.id.login_loginButton);
login_button.performClick();
- Mockito.verify(userService, Mockito.atLeastOnce()).localLogin(anyString());
+ Mockito.verify(userService, Mockito.atLeastOnce()).localLogin(anyString(), any(char[].class));
}
diff --git a/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityWithRemoteLoginTest.java b/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityWithRemoteLoginTest.java
index 754739d50..3dd2d2359 100644
--- a/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityWithRemoteLoginTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityWithRemoteLoginTest.java
@@ -126,8 +126,8 @@ private void destroyController() {
@Test
public void remoteLoginTest() {
when(userService.hasARegisteredUser()).thenReturn(false);
- when(userService.isValidLocalLogin(anyString(), anyString())).thenReturn(true);
- when(userService.isValidRemoteLogin(anyString(), anyString())).thenReturn(LoginResponse.SUCCESS.withPayload(new LoginResponseData()));
+ when(userService.isValidLocalLogin(anyString(), any(char[].class))).thenReturn(true);
+ when(userService.isValidRemoteLogin(anyString(),any(char[].class))).thenReturn(LoginResponse.SUCCESS.withPayload(new LoginResponseData()));
when(context_.allSharedPreferences()).thenReturn(allSharedPreferences);
when(allSharedPreferences.fetchBaseURL(anyString())).thenReturn("base url");
EditText username = activity.findViewById(R.id.login_userNameText);
@@ -136,7 +136,7 @@ public void remoteLoginTest() {
password.setText("password");
Button login_button = activity.findViewById(R.id.login_loginButton);
login_button.performClick();
- Mockito.verify(userService, Mockito.atLeastOnce()).processLoginResponseDataForUser(anyString(), any(LoginResponseData.class));
+ Mockito.verify(userService, Mockito.atLeastOnce()).processLoginResponseDataForUser(anyString(),any(char[].class), any(LoginResponseData.class));
destroyController();
From b09d8613fc02fec023ee63634ac5b78e43501e71 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Tue, 9 Jun 2020 10:46:42 +0300
Subject: [PATCH 24/70] Fix unit test
---
.../src/main/java/org/smartregister/service/HTTPAgent.java | 6 +++---
.../test/java/org/smartregister/service/HTTPAgentTest.java | 7 ++++---
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
index 9a9e962bd..de60b0f37 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
@@ -8,8 +8,8 @@
import com.google.common.io.BaseEncoding;
import com.google.gson.Gson;
+import org.apache.commons.codec.CharEncoding;
import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.CharEncoding;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.util.ByteArrayBuffer;
@@ -232,7 +232,7 @@ public LoginResponse urlCanBeAccessWithGivenCredentials(String requestURL, Strin
url = requestURL.replaceAll("\\s+", "");
urlConnection = initializeHttp(url, false);
- byte[] credentials = SecurityHelper.toBytes(new StringBuffer(userName).append(':').append(password));//(new StringBuffer(userName).append(':').append(password)).toString().getBytes();
+ byte[] credentials = SecurityHelper.toBytes(new StringBuffer(userName).append(':').append(password));
final String basicAuth = AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BASIC + " " + Base64.encodeToString(credentials, Base64.NO_WRAP);
urlConnection.setRequestProperty(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, basicAuth);
int statusCode = urlConnection.getResponseCode();
@@ -568,7 +568,7 @@ public AccountResponse oauth2authenticateCore(StringBuffer requestParamBuffer, S
String clientId = CoreLibrary.getInstance().getSyncConfiguration().getOauthClientId();
String clientSecret = CoreLibrary.getInstance().getSyncConfiguration().getOauthClientSecret();
- final String base64Auth = BaseEncoding.base64().encode(new String(clientId + ":" + clientSecret).getBytes());
+ final String base64Auth = BaseEncoding.base64().encode(new String(clientId + ":" + clientSecret).getBytes(CharEncoding.UTF_8));
requestParamBuffer.append("&grant_type=").append(grantType);
requestParamBuffer.append("&client_id=").append(clientId);
diff --git a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
index 1f93c6ea6..905b71524 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
@@ -7,6 +7,7 @@
import com.google.common.io.BaseEncoding;
+import org.apache.commons.codec.CharEncoding;
import org.apache.commons.io.IOUtils;
import org.json.JSONObject;
import org.junit.Assert;
@@ -300,9 +301,9 @@ public void testOauth2authenticateCreatesUrlConnectionWithCorrectParameters() th
Mockito.verify(httpURLConnection).setConnectTimeout(60000);
Mockito.verify(httpURLConnection).setReadTimeout(60000);
- String requestParams = "&grant_type=" + AccountHelper.OAUTH.GRANT_TYPE.PASSWORD + "&username=" + TEST_USERNAME + "&password=" + TEST_PASSWORD + "&client_id=" + TEST_CLIENT_ID + "&client_secret=" + TEST_CLIENT_SECRET;
+ String requestParams = "&grant_type=" + AccountHelper.OAUTH.GRANT_TYPE.PASSWORD + "&username=" + TEST_USERNAME + "&password=" + String.valueOf(TEST_PASSWORD) + "&client_id=" + TEST_CLIENT_ID + "&client_secret=" + TEST_CLIENT_SECRET;
- Mockito.verify(httpURLConnection).setFixedLengthStreamingMode(requestParams.getBytes().length);
+ Mockito.verify(httpURLConnection).setFixedLengthStreamingMode(requestParams.getBytes(CharEncoding.UTF_8).length);
Mockito.verify(httpURLConnection).setDoOutput(true);
Mockito.verify(httpURLConnection).setInstanceFollowRedirects(false);
Mockito.verify(httpURLConnection).setRequestMethod("POST");
@@ -310,7 +311,7 @@ public void testOauth2authenticateCreatesUrlConnectionWithCorrectParameters() th
Mockito.verify(httpURLConnection).setRequestProperty("charset", "utf-8");
Mockito.verify(httpURLConnection).setRequestProperty(ArgumentMatchers.eq("Content-Length"), ArgumentMatchers.anyString());
Mockito.verify(httpURLConnection).setUseCaches(false);
- final String base64Auth = BaseEncoding.base64().encode(new String(TEST_CLIENT_ID + ":" + TEST_CLIENT_SECRET).getBytes());
+ final String base64Auth = BaseEncoding.base64().encode(new String(TEST_CLIENT_ID + ":" + TEST_CLIENT_SECRET).getBytes(CharEncoding.UTF_8));
Mockito.verify(httpURLConnection).setRequestProperty(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BASIC + " " + base64Auth);
Mockito.verify(httpURLConnection).setInstanceFollowRedirects(false);
From 658aef8e2b4a61f2563a329e6252347327abc472 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Tue, 9 Jun 2020 14:00:00 +0300
Subject: [PATCH 25/70] HTTP Agent unit tests - Testing Tuesday - Add unit test
for http image post/ upload
---
.../org/smartregister/service/HTTPAgent.java | 21 ++++-
.../smartregister/service/HTTPAgentTest.java | 85 +++++++++++++++++++
2 files changed, 102 insertions(+), 4 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
index de60b0f37..4c5c6a7b2 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
@@ -48,6 +48,7 @@
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
@@ -83,7 +84,7 @@
public class HTTPAgent {
- private static final int FILE_UPLOAD_CHUNK_SIZE_BYTES = 4096;
+ public static final int FILE_UPLOAD_CHUNK_SIZE_BYTES = 4096;
private Context context;
private AllSharedPreferences allSharedPreferences;
@@ -375,7 +376,7 @@ public String httpImagePost(String urlString, ProfileImage image) {
httpUrlConnection.setChunkedStreamingMode(FILE_UPLOAD_CHUNK_SIZE_BYTES);
outputStream = httpUrlConnection.getOutputStream();
- writer = new PrintWriter(new OutputStreamWriter(outputStream, "UTF-8"), true);
+ writer = getPrintWriter(outputStream);
// attach image
attachImage(writer, image, outputStream);
@@ -427,6 +428,12 @@ public String httpImagePost(String urlString, ProfileImage image) {
return responseString;
}
+ @VisibleForTesting
+ @NonNull
+ protected PrintWriter getPrintWriter(OutputStream outputStream) throws UnsupportedEncodingException {
+ return new PrintWriter(new OutputStreamWriter(outputStream, CharEncoding.UTF_8), true);
+ }
+
private void attachImage(PrintWriter writer, ProfileImage image, OutputStream outputStream) throws IOException {
File uploadImageFile = getDownloadFolder(image.getFilepath());
String fileName = uploadImageFile.getName();
@@ -438,7 +445,7 @@ private void attachImage(PrintWriter writer, ProfileImage image, OutputStream ou
writer.append(crlf);
writer.flush();
- FileInputStream inputStream = new FileInputStream(uploadImageFile);
+ FileInputStream inputStream = getFileInputStream(uploadImageFile);
byte[] buffer = new byte[FILE_UPLOAD_CHUNK_SIZE_BYTES];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
@@ -568,7 +575,7 @@ public AccountResponse oauth2authenticateCore(StringBuffer requestParamBuffer, S
String clientId = CoreLibrary.getInstance().getSyncConfiguration().getOauthClientId();
String clientSecret = CoreLibrary.getInstance().getSyncConfiguration().getOauthClientSecret();
- final String base64Auth = BaseEncoding.base64().encode(new String(clientId + ":" + clientSecret).getBytes(CharEncoding.UTF_8));
+ final String base64Auth = BaseEncoding.base64().encode(new StringBuffer(clientId).append(':').append(clientSecret).toString().getBytes(CharEncoding.UTF_8));
requestParamBuffer.append("&grant_type=").append(grantType);
requestParamBuffer.append("&client_id=").append(clientId);
@@ -787,6 +794,12 @@ public Response downloadFromURL(String downloadURL_, String file
return new Response(ResponseStatus.success, DownloadStatus.downloaded);
}
+ @VisibleForTesting
+ @NonNull
+ protected FileInputStream getFileInputStream(File uploadImageFile) throws FileNotFoundException {
+ return new FileInputStream(uploadImageFile);
+ }
+
@VisibleForTesting
@NonNull
protected FileOutputStream getFileOutputStream(File file) throws FileNotFoundException {
diff --git a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
index 905b71524..bc5de2873 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
@@ -48,6 +48,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
@@ -105,6 +106,9 @@ public class HTTPAgentTest {
@Mock
private InputStream inputStream;
+ @Mock
+ private FileInputStream fileInputStream;
+
@Mock
private InputStream errorStream;
@@ -120,6 +124,9 @@ public class HTTPAgentTest {
@Mock
private FileOutputStream fileOutputStream;
+ @Mock
+ private PrintWriter printWriter;
+
@Rule
private TemporaryFolder folder = new TemporaryFolder();
@@ -132,6 +139,10 @@ public class HTTPAgentTest {
private static final String KEYClOAK_CONFIGURATION_ENDPOINT = "https://my-server.com/rest/config/keycloak";
private static final String USER_DETAILS_ENDPOINT = "https://my-server.com/opensrp/security/authenticate";
private static final String TEST_IMAGE_DOWNLOAD_ENDPOINT = "https://my-server.com/opensrp/multimedia/myimage.jpg";
+ private static final String TEST_IMAGE_UPLOAD_ENDPOINT = "https://my-server.com/opensrp/multimedia/";
+ private static final String TEST_IMAGE_FILE_PATH = "file://usr/sdcard/dev0/data/org.smartregister.core/localimage.jpg";
+ protected static final String TEST_BASE_ENTITY_ID = "23ka2-3e23h2-n3g2i4-9q3b-yts4-20";
+ protected static final String TEST_ANM_ID = "demo";
private final String SAMPLE_TEST_TOKEN = "sample-test-token";
private final String SAMPLE_REFRESH_TOKEN = "sample-refresh-token";
@@ -164,6 +175,7 @@ public void setUp() {
PowerMockito.mockStatic(AccountHelper.class);
PowerMockito.when(AccountHelper.getOauthAccountByType(accountAuthenticatorXml.getAccountType())).thenReturn(account);
+ PowerMockito.when(AccountHelper.getOAuthToken(accountAuthenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).thenReturn(SAMPLE_TEST_TOKEN);
httpAgent = new HTTPAgent(context, allSharedPreferences, dristhiConfiguration);
httpAgent.setConnectTimeout(60000);
@@ -1014,4 +1026,77 @@ public void testDownloadFromUrlReturnsCorrectResponseIfConnectionStatusIsNOT200(
}
+ @Test
+ public void testHttpImagePostConfiguresConnectionRequestCorrectly() throws Exception {
+
+ PowerMockito.mockStatic(Base64.class);
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(TEST_IMAGE_UPLOAD_ENDPOINT);
+ Mockito.doReturn(outputStream).when(httpURLConnection).getOutputStream();
+ Mockito.doReturn(HttpURLConnection.HTTP_OK).when(httpURLConnection).getResponseCode();
+ Mockito.doReturn(file).when(httpAgentSpy).getDownloadFolder(TEST_IMAGE_FILE_PATH);
+ Mockito.doReturn(fileInputStream).when(httpAgentSpy).getFileInputStream(file);
+ Mockito.doReturn(inputStream).when(httpURLConnection).getInputStream();
+ Mockito.doReturn(-1).when(fileInputStream).read(ArgumentMatchers.any(byte[].class));
+
+ Mockito.doReturn(printWriter).when(httpAgentSpy).getPrintWriter(outputStream);
+ Mockito.doReturn("myFileName").when(file).getName();
+ Mockito.doReturn(printWriter).when(printWriter).append(ArgumentMatchers.any(CharSequence.class));
+
+ ProfileImage profileImage = new ProfileImage();
+ profileImage.setFilepath(TEST_IMAGE_FILE_PATH);
+ profileImage.setAnmId(TEST_ANM_ID);
+ profileImage.setEntityID(TEST_BASE_ENTITY_ID);
+ profileImage.setContenttype("png");
+ profileImage.setFilecategory("coverpic");
+
+ httpAgentSpy.httpImagePost(TEST_IMAGE_UPLOAD_ENDPOINT, profileImage);
+
+ Mockito.verify(httpURLConnection).setDoOutput(true);
+ Mockito.verify(httpURLConnection).setDoInput(true);
+ Mockito.verify(httpURLConnection).setRequestMethod("POST");
+
+ ArgumentCaptor requestAttributeCaptor = ArgumentCaptor.forClass(String.class);
+ ArgumentCaptor requestValueCaptor = ArgumentCaptor.forClass(String.class);
+
+ Mockito.verify(httpURLConnection, Mockito.times(2)).setRequestProperty(requestAttributeCaptor.capture(), requestValueCaptor.capture());
+ List requestAttributeCaptorValues = requestAttributeCaptor.getAllValues();
+ List requestValueCaptorValues = requestValueCaptor.getAllValues();
+
+ Assert.assertEquals("Authorization", requestAttributeCaptorValues.get(0));
+ Assert.assertEquals("Bearer " + SAMPLE_TEST_TOKEN, requestValueCaptorValues.get(0));
+
+ Assert.assertEquals("Content-Type", requestAttributeCaptorValues.get(1));
+ Assert.assertTrue(requestValueCaptorValues.get(1).startsWith("multipart/form-data;boundary="));
+
+ Mockito.verify(httpURLConnection).setUseCaches(false);
+ Mockito.verify(httpURLConnection).setChunkedStreamingMode(HTTPAgent.FILE_UPLOAD_CHUNK_SIZE_BYTES);
+
+ //Attach Image
+ Mockito.verify(httpAgentSpy).getDownloadFolder(TEST_IMAGE_FILE_PATH);
+
+ ArgumentCaptor printWriterCaptor = ArgumentCaptor.forClass(CharSequence.class);
+
+ Mockito.verify(printWriter, Mockito.times(49)).append(printWriterCaptor.capture());
+
+ List printWriterAppendedValues = printWriterCaptor.getAllValues();
+
+ Assert.assertTrue(printWriterAppendedValues.contains("Content-Disposition: form-data; name=\"file\"; filename=\"myFileName\""));
+ Assert.assertTrue(printWriterAppendedValues.contains("Content-Disposition: form-data; name=\"anm-id\""));
+ Assert.assertTrue(printWriterAppendedValues.contains("Content-Disposition: form-data; name=\"entity-id\""));
+ Assert.assertTrue(printWriterAppendedValues.contains("Content-Disposition: form-data; name=\"file-category\""));
+ Assert.assertTrue(printWriterAppendedValues.contains("Content-Disposition: form-data; name=\"content-type\""));
+ Assert.assertTrue(printWriterAppendedValues.contains(profileImage.getAnmId()));
+ Assert.assertTrue(printWriterAppendedValues.contains(profileImage.getEntityID()));
+ Assert.assertTrue(printWriterAppendedValues.contains(profileImage.getFilecategory()));
+ Assert.assertTrue(printWriterAppendedValues.contains(profileImage.getContenttype()));
+ Assert.assertTrue(printWriterAppendedValues.contains("Content-Type: text/plain; charset=UTF-8"));
+
+ Mockito.verify(printWriter, Mockito.times(7)).flush();
+ }
}
From c8d011445cb24bc0418f70c18335824392a7c112 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Thu, 11 Jun 2020 15:51:28 +0300
Subject: [PATCH 26/70] Refactor Verify Authorization - Add support for
verifying if account disabled by Admin - Update logout
---
gradle.properties | 4 ++
.../account/AccountConfiguration.java | 11 ++++
.../smartregister/account/AccountHelper.java | 1 +
.../account/AccountUserInfo.java | 61 +++++++++++++++++++
.../login/task/RemoteLoginTask.java | 1 +
.../org/smartregister/service/HTTPAgent.java | 41 +++++++++----
6 files changed, 106 insertions(+), 13 deletions(-)
create mode 100644 opensrp-app/src/main/java/org/smartregister/account/AccountUserInfo.java
diff --git a/gradle.properties b/gradle.properties
index ac928efda..c7676bb34 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,4 +1,8 @@
+<<<<<<< HEAD
VERSION_NAME=2.0.4-SNAPSHOT
+=======
+VERSION_NAME=2.0.0-PRV-SNAPSHOT
+>>>>>>> Refactor Verify Authorization
VERSION_CODE=1
GROUP=org.smartregister
POM_SETTING_DESCRIPTION=OpenSRP Client Core Application
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountConfiguration.java b/opensrp-app/src/main/java/org/smartregister/account/AccountConfiguration.java
index 171d9a47b..705a5bbf0 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountConfiguration.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountConfiguration.java
@@ -18,6 +18,9 @@ public class AccountConfiguration {
@SerializedName("token_endpoint")
private String tokenEndpoint;
+ @SerializedName("userinfo_endpoint")
+ private String userinfoEndpoint;
+
@SerializedName("grant_types_supported")
private List grantTypesSupported;
@@ -33,6 +36,10 @@ public String getTokenEndpoint() {
return tokenEndpoint;
}
+ public String getUserinfoEndpoint() {
+ return userinfoEndpoint;
+ }
+
public List getGrantTypesSupported() {
return grantTypesSupported;
}
@@ -49,6 +56,10 @@ public void setTokenEndpoint(String tokenEndpoint) {
this.tokenEndpoint = tokenEndpoint;
}
+ public void setUserinfoEndpoint(String userinfoEndpoint) {
+ this.userinfoEndpoint = userinfoEndpoint;
+ }
+
public void setGrantTypesSupported(List grantTypesSupported) {
this.grantTypesSupported = grantTypesSupported;
}
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
index 4a0b4a06b..aad68d1c1 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
@@ -23,6 +23,7 @@ public static final class CONFIGURATION_CONSTANTS {
public final static String TOKEN_ENDPOINT_URL = "token_endpoint_url";
public final static String AUTHORIZATION_ENDPOINT_URL = "authorization_endpoint_url";
public final static String ISSUER_ENDPOINT_URL = "issuer_endpoint_url";
+ public static final String USERINFO_ENDPOINT_URL = "userinfo_endpint_url";
}
public static final class OAUTH {
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountUserInfo.java b/opensrp-app/src/main/java/org/smartregister/account/AccountUserInfo.java
new file mode 100644
index 000000000..4f80cd2ca
--- /dev/null
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountUserInfo.java
@@ -0,0 +1,61 @@
+package org.smartregister.account;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Created by ndegwamartin on 03/06/2020.
+ */
+public class AccountUserInfo {
+
+ private String name;
+
+ private String email;
+
+ private Boolean enabled;
+
+ @SerializedName("preferred_username")
+ private String preferredUsername;
+
+ @SerializedName("email_verified")
+ private Boolean emailVerified;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public Boolean getEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public String getPreferredUsername() {
+ return preferredUsername;
+ }
+
+ public void setPreferredUsername(String preferredUsername) {
+ this.preferredUsername = preferredUsername;
+ }
+
+ public Boolean getEmailVerified() {
+ return emailVerified;
+ }
+
+ public void setEmailVerified(Boolean emailVerified) {
+ this.emailVerified = emailVerified;
+ }
+}
diff --git a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
index 280a181e9..4e39e218d 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
@@ -85,6 +85,7 @@ protected LoginResponse doInBackground(Void... params) {
sharedPrefEditor.putString(AccountHelper.CONFIGURATION_CONSTANTS.TOKEN_ENDPOINT_URL, accountConfiguration.getTokenEndpoint());
sharedPrefEditor.putString(AccountHelper.CONFIGURATION_CONSTANTS.AUTHORIZATION_ENDPOINT_URL, accountConfiguration.getAuthorizationEndpoint());
sharedPrefEditor.putString(AccountHelper.CONFIGURATION_CONSTANTS.ISSUER_ENDPOINT_URL, accountConfiguration.getIssuerEndpoint());
+ sharedPrefEditor.putString(AccountHelper.CONFIGURATION_CONSTANTS.USERINFO_ENDPOINT_URL, accountConfiguration.getUserinfoEndpoint());
sharedPrefEditor.apply();
AccountResponse response = CoreLibrary.getInstance().context().getHttpAgent().oauth2authenticate(mUsername, mPassword, AccountHelper.OAUTH.GRANT_TYPE.PASSWORD, accountConfiguration.getTokenEndpoint());
diff --git a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
index 4c5c6a7b2..8b52fc44c 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
@@ -21,6 +21,7 @@
import org.smartregister.account.AccountError;
import org.smartregister.account.AccountHelper;
import org.smartregister.account.AccountResponse;
+import org.smartregister.account.AccountUserInfo;
import org.smartregister.compression.GZIPCompression;
import org.smartregister.domain.DownloadStatus;
import org.smartregister.domain.LoginResponse;
@@ -831,34 +832,47 @@ protected File getDownloadFolder(String sdcardPathDownload) {
public boolean verifyAuthorization() {
- String baseUrl = configuration.dristhiBaseURL();
-
- if (baseUrl.endsWith("/")) {
- baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
- }
- final String username = allSharedPreferences.fetchRegisteredANM();
- baseUrl = baseUrl + DETAILS_URL + username;
+ String userInfoUrl = allSharedPreferences.getPreferences().getString(AccountHelper.CONFIGURATION_CONSTANTS.USERINFO_ENDPOINT_URL, "");
HttpURLConnection urlConnection = null;
+ InputStream inputStream = null;
+
try {
- urlConnection = initializeHttp(baseUrl, true);
+ urlConnection = initializeHttp(userInfoUrl, true);
- int statusCode = urlConnection.getResponseCode();
+ AccountUserInfo userInfo = null;
if (HttpStatus.SC_UNAUTHORIZED == urlConnection.getResponseCode()) {
invalidateExpiredCachedAccessToken();
- urlConnection = initializeHttp(baseUrl, true);
+ urlConnection = initializeHttp(userInfoUrl, true);
- Timber.i("User not authorized. User access was revoked, will log off user");
- return false;
- } else if (statusCode != HttpStatus.SC_OK) {
+ }
+
+ if (urlConnection.getResponseCode() == HttpStatus.SC_OK) {
+
+ inputStream = urlConnection.getInputStream();
+
+ String responseString = IOUtils.toString(inputStream);
+
+ userInfo = gson.fromJson(responseString, AccountUserInfo.class);
+
+ } else {
Timber.w("Error occurred verifying authorization, User will not be logged off");
+ }
+
+ if (!userInfo.getEnabled()) {
+
+ Timber.i("User not authorized. User access was revoked, will log off user");
+ return true;
+
} else {
+
Timber.i("User is Authorized");
+ return false;
}
} catch (IOException e) {
@@ -866,6 +880,7 @@ public boolean verifyAuthorization() {
} finally {
closeConnection(urlConnection);
+ closeIOStream(inputStream);
}
return true;
}
From c2bbcc546fd46ef0326f4143f8f977e81911f696 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Thu, 11 Jun 2020 22:33:09 +0300
Subject: [PATCH 27/70] Update Verify Authorization - Add backward
compatibility for legacy implementation - Add unit tests - Fix codacy issues
---
.../login/task/RemoteLoginTask.java | 2 +-
.../org/smartregister/service/HTTPAgent.java | 51 +++++++++++++++-
.../smartregister/service/UserService.java | 16 +----
.../smartregister/service/HTTPAgentTest.java | 58 ++++++++++++++++++-
4 files changed, 107 insertions(+), 20 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
index 4e39e218d..2dcbae997 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
@@ -111,7 +111,7 @@ protected LoginResponse doInBackground(Void... params) {
SyncSettingsServiceHelper syncSettingsServiceHelper = new SyncSettingsServiceHelper(getOpenSRPContext().configuration().dristhiBaseURL(), getOpenSRPContext().getHttpAgent());
try {
- JSONArray settings = syncSettingsServiceHelper.pullSettingsFromServer(Utils.getFilterValue(loginResponse, CoreLibrary.getInstance().getSyncConfiguration().getSyncFilterParam()),response.getAccessToken());
+ JSONArray settings = syncSettingsServiceHelper.pullSettingsFromServer(Utils.getFilterValue(loginResponse, CoreLibrary.getInstance().getSyncConfiguration().getSyncFilterParam()), response.getAccessToken());
JSONObject prefSettingsData = new JSONObject();
prefSettingsData.put(AllConstants.PREF_KEY.SETTINGS, settings);
diff --git a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
index 8b52fc44c..a3db60634 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
@@ -864,14 +864,17 @@ public boolean verifyAuthorization() {
Timber.w("Error occurred verifying authorization, User will not be logged off");
}
- if (!userInfo.getEnabled()) {
+ if (userInfo == null || userInfo.getEnabled() == null)
+ return verifyAuthorizationLegacy();
- Timber.i("User not authorized. User access was revoked, will log off user");
+ if (userInfo.getEnabled()) {
+
+ Timber.i("User is Authorized");
return true;
} else {
- Timber.i("User is Authorized");
+ Timber.i("User not authorized. User access was revoked, will log off user");
return false;
}
@@ -885,6 +888,48 @@ public boolean verifyAuthorization() {
return true;
}
+ //For backward compatibility
+ public boolean verifyAuthorizationLegacy() {
+
+ String baseUrl = configuration.dristhiBaseURL();
+
+ if (baseUrl.endsWith("/")) {
+ baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
+ }
+ final String username = allSharedPreferences.fetchRegisteredANM();
+ baseUrl = baseUrl + DETAILS_URL + username;
+
+ HttpURLConnection urlConnection = null;
+
+ try {
+
+ urlConnection = initializeHttp(baseUrl, true);
+
+ int statusCode = urlConnection.getResponseCode();
+
+ if (HttpStatus.SC_UNAUTHORIZED == urlConnection.getResponseCode()) {
+
+ invalidateExpiredCachedAccessToken();
+
+ urlConnection = initializeHttp(baseUrl, true);
+
+ Timber.i("User not authorized. User access was revoked, will log off user");
+ return false;
+ } else if (statusCode != HttpStatus.SC_OK) {
+ Timber.w("Error occurred verifying authorization, User will not be logged off");
+ } else {
+ Timber.i("User is Authorized");
+ }
+
+ } catch (IOException e) {
+ Timber.e(e);
+ } finally {
+
+ closeConnection(urlConnection);
+ }
+ return true;
+ }
+
public AccountConfiguration fetchOAuthConfiguration() {
String baseUrl = configuration.dristhiBaseURL();
diff --git a/opensrp-app/src/main/java/org/smartregister/service/UserService.java b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
index e105de17f..06e700204 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/UserService.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
@@ -323,7 +323,7 @@ public Response getLocationInformation() {
private boolean loginWith(String userName, char[] password) {
boolean loginSuccessful = true;
- if (usesGroupIdAsDBPassword(userName)) {
+ if (usesGroupIdAsDBPassword()) {
String encryptedGroupId = AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType());
@@ -353,20 +353,10 @@ private boolean loginWith(String userName, char[] password) {
/**
* Checks whether to use the groupId for the current user to decrypt the database
*
- * @param userName The user to check
* @return TRUE if the user decrypts the database using the groupId
*/
- private boolean usesGroupIdAsDBPassword(String userName) {
- try {
- if (keyStore != null && keyStore.containsAlias(userName)) {
- if (AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType()) != null) {
- return true;
- }
- }
- } catch (Exception e) {
- Timber.e(e);
- }
- return false;
+ private boolean usesGroupIdAsDBPassword() {
+ return AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType()) != null;
}
public void localLogin(String userName, char[] password) {
diff --git a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
index bc5de2873..3a8f0ad83 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
@@ -140,6 +140,7 @@ public class HTTPAgentTest {
private static final String USER_DETAILS_ENDPOINT = "https://my-server.com/opensrp/security/authenticate";
private static final String TEST_IMAGE_DOWNLOAD_ENDPOINT = "https://my-server.com/opensrp/multimedia/myimage.jpg";
private static final String TEST_IMAGE_UPLOAD_ENDPOINT = "https://my-server.com/opensrp/multimedia/";
+ private static final String TEST_USER_INFO_ENDPOINT = "https://keycloak.my-server.com/auth/userinfo";
private static final String TEST_IMAGE_FILE_PATH = "file://usr/sdcard/dev0/data/org.smartregister.core/localimage.jpg";
protected static final String TEST_BASE_ENTITY_ID = "23ka2-3e23h2-n3g2i4-9q3b-yts4-20";
protected static final String TEST_ANM_ID = "demo";
@@ -154,6 +155,7 @@ public class HTTPAgentTest {
private static final String OAUTH_CONFIGURATION_SERVER_RESPONSE = "{\"issuer\":\"https://my-server.com/oauth/issuer\",\r\n\"authorization_endpoint\": \"https://my-server.com/oauth/auth\",\r\n\"token_endpoint\": \"https://my-server.com/oauth/token\",\r\n\"grant_types_supported\":[\"authorization code\",\"implicit\",\"password\"]\r\n}";
private static final String FETCH_DATA_REQUEST_SERVER_RESPONSE = "{status:{\"response_status\":\"success\"},payload: \"My secure resources from the server\"\r\n\r\n}";
private static final String SAMPLE_POST_REQUEST_PAYLOAD = "{\"payload\":\"My POST Payload\"}";
+ private static final String ACCOUNT_INFO_REQUEST_SERVER_RESPONSE = "{ \"name\":\"Test User\", \"email\":\"demo@smartegister.org\", \"enabled\":true, \"preferred_username\":\"demo\", \"email_verified\":true}";
private static final String TEST_FILE_NAME = "Profile";
@Before
@@ -772,7 +774,7 @@ public void testFetchUserDetailsConstructsCorrectResponseForRequestsWithoutNetwo
@Test
- public void testVerifyAuthorizationReturnsTrueForAuthorizedResponse() throws Exception {
+ public void testVerifyAuthorizationLegacyReturnsTrueForAuthorizedResponse() throws Exception {
URL url = PowerMockito.mock(URL.class);
Assert.assertNotNull(url);
@@ -784,13 +786,13 @@ public void testVerifyAuthorizationReturnsTrueForAuthorizedResponse() throws Exc
Mockito.doReturn(HttpURLConnection.HTTP_OK).when(httpURLConnection).getResponseCode();
- boolean isVerified = httpAgentSpy.verifyAuthorization();
+ boolean isVerified = httpAgentSpy.verifyAuthorizationLegacy();
Assert.assertTrue(isVerified);
}
@Test
- public void testVerifyAuthorizationReturnsFalseForUnauthorizedResponse() throws Exception {
+ public void testVerifyAuthorizationLegacyReturnsFalseForUnauthorizedResponse() throws Exception {
URL url = PowerMockito.mock(URL.class);
Assert.assertNotNull(url);
@@ -803,6 +805,56 @@ public void testVerifyAuthorizationReturnsFalseForUnauthorizedResponse() throws
Mockito.doReturn(HttpURLConnection.HTTP_UNAUTHORIZED).when(httpURLConnection).getResponseCode();
+ boolean isVerified = httpAgentSpy.verifyAuthorizationLegacy();
+ Assert.assertFalse(isVerified);
+
+ }
+
+
+ @Test
+ public void testVerifyAuthorizationReturnsTrueForAuthorizedResponse() throws Exception {
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(sharedPreferences).when(allSharedPreferences).getPreferences();
+
+ Mockito.doReturn(TEST_USER_INFO_ENDPOINT).when(sharedPreferences).getString(AccountHelper.CONFIGURATION_CONSTANTS.USERINFO_ENDPOINT_URL, "");
+
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(TEST_USER_INFO_ENDPOINT);
+
+ Mockito.doReturn(HttpURLConnection.HTTP_OK).when(httpURLConnection).getResponseCode();
+
+ Mockito.doReturn(inputStream).when(httpURLConnection).getInputStream();
+
+ PowerMockito.mockStatic(IOUtils.class);
+ PowerMockito.when(IOUtils.toString(inputStream)).thenReturn(ACCOUNT_INFO_REQUEST_SERVER_RESPONSE);
+
+ boolean isVerified = httpAgentSpy.verifyAuthorization();
+ Assert.assertTrue(isVerified);
+
+ }
+
+ @Test
+ public void testVerifyAuthorizationReturnsFalseForUnauthorizedResponse() throws Exception {
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(sharedPreferences).when(allSharedPreferences).getPreferences();
+
+ Mockito.doReturn(TEST_USER_INFO_ENDPOINT).when(sharedPreferences).getString(AccountHelper.CONFIGURATION_CONSTANTS.USERINFO_ENDPOINT_URL, "");
+
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(TEST_USER_INFO_ENDPOINT);
+
+ Mockito.doReturn(false).when(httpAgentSpy).verifyAuthorizationLegacy();
+
+ Mockito.doReturn(HttpURLConnection.HTTP_UNAUTHORIZED).when(httpURLConnection).getResponseCode();
+
boolean isVerified = httpAgentSpy.verifyAuthorization();
Assert.assertFalse(isVerified);
From 34c14a876e4d3a786fff18e134c0068bded65a9a Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Fri, 12 Jun 2020 08:48:33 +0300
Subject: [PATCH 28/70] Update artifact to correct version
---
gradle.properties | 4 ----
1 file changed, 4 deletions(-)
diff --git a/gradle.properties b/gradle.properties
index c7676bb34..ac928efda 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,8 +1,4 @@
-<<<<<<< HEAD
VERSION_NAME=2.0.4-SNAPSHOT
-=======
-VERSION_NAME=2.0.0-PRV-SNAPSHOT
->>>>>>> Refactor Verify Authorization
VERSION_CODE=1
GROUP=org.smartregister
POM_SETTING_DESCRIPTION=OpenSRP Client Core Application
From 0de5f3580c9a2747e90c97317e1e59d82fc4c50e Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Fri, 12 Jun 2020 09:09:29 +0300
Subject: [PATCH 29/70] Update Arabic and French translations
---
opensrp-app/res/values-ar/strings.xml | 4 ++--
opensrp-app/res/values-fr/strings.xml | 4 ++--
opensrp-app/res/values/strings.xml | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/opensrp-app/res/values-ar/strings.xml b/opensrp-app/res/values-ar/strings.xml
index ce4d5ba5a..2dbd9bcb2 100644
--- a/opensrp-app/res/values-ar/strings.xml
+++ b/opensrp-app/res/values-ar/strings.xml
@@ -33,8 +33,8 @@
هل أنت متأكد أنك تريد تسجيل الخروج؟ سيتم مسح جميع البيانات
استمارات غير متزامنة
- التالي >>
- >>سابق
+ التالية>>
+ >>السابقة
صفحة {0} من {1}
الاسم
diff --git a/opensrp-app/res/values-fr/strings.xml b/opensrp-app/res/values-fr/strings.xml
index 93d9865ef..c5898c615 100644
--- a/opensrp-app/res/values-fr/strings.xml
+++ b/opensrp-app/res/values-fr/strings.xml
@@ -33,8 +33,8 @@
Êtes-vous certain de vouloir se déconnecter? Tous les données seront effacées.
Formulaires non-synchronisés
- Prochain >>
- << Dernier
+ Suivante >>
+ << Précédente
Page {0} de {1}
Nom
diff --git a/opensrp-app/res/values/strings.xml b/opensrp-app/res/values/strings.xml
index 4c1f9e74c..95b289392 100644
--- a/opensrp-app/res/values/strings.xml
+++ b/opensrp-app/res/values/strings.xml
@@ -33,8 +33,8 @@
Are you sure you want to log out? All the data will be cleared.
Forms Unsynced
- "Next >>"
- "<< Previous"
+ Next >>
+ << Previous
Page {0} of {1}
NAME
From 090bb32c53e407d6a68c72536bc19a1ee6dc690c Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Fri, 12 Jun 2020 12:09:59 +0300
Subject: [PATCH 30/70] Location picker show location tag - Add support for
configurability of showing location tags alongside locations on picker tree -
Documentation and tests
---
README.md | 9 +++---
.../java/org/smartregister/AllConstants.java | 1 +
.../location/helper/LocationHelper.java | 9 +++++-
.../org/smartregister/util/AppProperties.java | 8 ++++++
.../java/org/smartregister/util/Utils.java | 3 +-
.../location/helper/LocationHelperTest.java | 28 +++++++++++++++++--
6 files changed, 49 insertions(+), 9 deletions(-)
diff --git a/README.md b/README.md
index 830095add..6e1a95fa8 100644
--- a/README.md
+++ b/README.md
@@ -313,7 +313,8 @@ By placing a file named `app.properties` in your implementation assets folder (S
### Configurable Settings
-| Configuration | Type | Default | Description |
-| ----------------------------------- | ------- | ------- | ----------------------------------------------|
-| `system.toaster.centered` | Boolean | false | Position toaster(s) at the center of the view(s) |
-| `disable.location.picker.view` | Boolean | false | Disables LocationPicker View |
\ No newline at end of file
+| Configuration | Type | Default | Description |
+| ----------------------------------- | ------- | -------- | -------------------------------------------------------------------------|
+| `system.toaster.centered` | Boolean | false | Position toaster(s) at the center of the view(s) |
+| `disable.location.picker.view` | Boolean | false | Disables LocationPicker View |
+| `location.picker.tag.shown` | Boolean | false | Hides/Shows the location tag in the location picker tree view |
diff --git a/opensrp-app/src/main/java/org/smartregister/AllConstants.java b/opensrp-app/src/main/java/org/smartregister/AllConstants.java
index e0f4339b1..2a90b52b9 100644
--- a/opensrp-app/src/main/java/org/smartregister/AllConstants.java
+++ b/opensrp-app/src/main/java/org/smartregister/AllConstants.java
@@ -454,6 +454,7 @@ public class KEY {
public static class PROPERTY {
public static final String SYSTEM_TOASTER_CENTERED = "system.toaster.centered";
public static final String DISABLE_LOCATION_PICKER_VIEW = "disable.location.picker.view";
+ public static final String LOCATION_PICKER_TAG_SHOWN = "location.picker.tag.shown";
}
diff --git a/opensrp-app/src/main/java/org/smartregister/location/helper/LocationHelper.java b/opensrp-app/src/main/java/org/smartregister/location/helper/LocationHelper.java
index 0e0839ec9..d9fec7290 100644
--- a/opensrp-app/src/main/java/org/smartregister/location/helper/LocationHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/location/helper/LocationHelper.java
@@ -1,8 +1,10 @@
package org.smartregister.location.helper;
+import android.support.annotation.VisibleForTesting;
import android.util.Pair;
import org.apache.commons.lang3.StringUtils;
+import org.smartregister.AllConstants;
import org.smartregister.CoreLibrary;
import org.smartregister.domain.form.FormLocation;
import org.smartregister.domain.jsonmapping.Location;
@@ -471,7 +473,7 @@ private List getFormJsonData(TreeNode openMrsLoc
formLocation.key = name;
Set levels = node.getTags();
- formLocation.level = levels != null && !levels.isEmpty() ? levels.iterator().next() : "";
+ formLocation.level = isLocationTagsShownEnabled() && levels != null && !levels.isEmpty() ? levels.iterator().next() : "";
LinkedHashMap> childMap = childMap(openMrsLocationData);
@@ -511,6 +513,11 @@ private List getFormJsonData(TreeNode openMrsLoc
return allLocationData;
}
+ @VisibleForTesting
+ protected boolean isLocationTagsShownEnabled() {
+ return Utils.getBooleanProperty(AllConstants.PROPERTY.LOCATION_PICKER_TAG_SHOWN);
+ }
+
/**
* This method sorts the options provided for a native form tree view question
*
diff --git a/opensrp-app/src/main/java/org/smartregister/util/AppProperties.java b/opensrp-app/src/main/java/org/smartregister/util/AppProperties.java
index f4d343ae0..a6191091c 100644
--- a/opensrp-app/src/main/java/org/smartregister/util/AppProperties.java
+++ b/opensrp-app/src/main/java/org/smartregister/util/AppProperties.java
@@ -26,4 +26,12 @@ public Boolean hasProperty(String key) {
return getProperty(key) != null;
}
+
+ /**
+ * @param key key as present in the properties file
+ * @return returns true if a property with the provided key exists and the Boolean evaluation is true, false otherwise
+ */
+ public Boolean isTrue(String key) {
+ return hasProperty(key) && getPropertyBoolean(key);
+ }
}
diff --git a/opensrp-app/src/main/java/org/smartregister/util/Utils.java b/opensrp-app/src/main/java/org/smartregister/util/Utils.java
index 21dfce6a6..1d91a1ce9 100644
--- a/opensrp-app/src/main/java/org/smartregister/util/Utils.java
+++ b/opensrp-app/src/main/java/org/smartregister/util/Utils.java
@@ -516,8 +516,7 @@ public static CommonPersonObjectClient convert(CommonPersonObject commonPersonOb
public static boolean getBooleanProperty(String key) {
- return CoreLibrary.getInstance().context().getAppProperties().hasProperty(key) ? CoreLibrary.getInstance().context().getAppProperties().getPropertyBoolean(key) : false;
-
+ return CoreLibrary.getInstance().context().getAppProperties().isTrue(key);
}
public static void showToast(Context context, String message) {
diff --git a/opensrp-app/src/test/java/org/smartregister/location/helper/LocationHelperTest.java b/opensrp-app/src/test/java/org/smartregister/location/helper/LocationHelperTest.java
index a6e59847f..f84a8e48a 100644
--- a/opensrp-app/src/test/java/org/smartregister/location/helper/LocationHelperTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/location/helper/LocationHelperTest.java
@@ -299,7 +299,6 @@ public void testGenerateLocationHierarchyTreeWithMapShouldReturnListWithOtherFor
}
-
@Test
public void testGenerateLocationHierarchyTreeWithMapAndOtherOptionFalseShouldReturnEmptyList() {
locationHelper = Mockito.spy(locationHelper);
@@ -341,6 +340,32 @@ public void testGenerateLocationHierarchyTreeWithMapShouldReturnListWithZambiaFo
assertEquals(1, formLocation.nodes.size());
}
+ @Test
+ public void testGenerateLocationHierarchyTreeWithMapShouldReturnListWithZambiaFormLocationAndLocationTags() {
+ locationHelper = Mockito.spy(locationHelper);
+ String locationData = "{\"locationsHierarchy\":{\"map\":{\"9c3e8715-1c59-44db-9709-2b49f440ef00\":{\"children\":{\"2e823ceb-4de6-41ac-8025-e2ae3512a331\":{\"children\":{\"620332e0-6108-4611-bac5-8b48d20051c9\":{\"children\":{\"ed7c4a07-6e02-4784-ae9a-9cd41cfef390\":{\"children\":{\"1b0ba804-54c3-40ef-820b-a8eaffa5d054\":{\"id\":\"1b0ba804-54c3-40ef-820b-a8eaffa5d054\",\"label\":\"ra_ksh_5\",\"node\":{\"locationId\":\"1b0ba804-54c3-40ef-820b-a8eaffa5d054\",\"name\":\"ra_ksh_5\",\"parentLocation\":{\"locationId\":\"ed7c4a07-6e02-4784-ae9a-9cd41cfef390\",\"name\":\"ra Kashikishi HAHC\",\"parentLocation\":{\"locationId\":\"620332e0-6108-4611-bac5-8b48d20051c9\",\"name\":\"ra Nchelenge\",\"serverVersion\":0,\"voided\":false},\"serverVersion\":0,\"voided\":false},\"tags\":[\"Operational Area\"],\"serverVersion\":0,\"voided\":false},\"parent\":\"ed7c4a07-6e02-4784-ae9a-9cd41cfef390\"}},\"id\":\"ed7c4a07-6e02-4784-ae9a-9cd41cfef390\",\"label\":\"ra Kashikishi HAHC\",\"node\":{\"locationId\":\"ed7c4a07-6e02-4784-ae9a-9cd41cfef390\",\"name\":\"ra Kashikishi HAHC\",\"parentLocation\":{\"locationId\":\"620332e0-6108-4611-bac5-8b48d20051c9\",\"name\":\"ra Nchelenge\",\"parentLocation\":{\"locationId\":\"2e823ceb-4de6-41ac-8025-e2ae3512a331\",\"name\":\"ra Luapula\",\"serverVersion\":0,\"voided\":false},\"serverVersion\":0,\"voided\":false},\"tags\":[\"Village\"],\"serverVersion\":0,\"voided\":false},\"parent\":\"620332e0-6108-4611-bac5-8b48d20051c9\"}},\"id\":\"620332e0-6108-4611-bac5-8b48d20051c9\",\"label\":\"ra Nchelenge\",\"node\":{\"locationId\":\"620332e0-6108-4611-bac5-8b48d20051c9\",\"name\":\"ra Nchelenge\",\"parentLocation\":{\"locationId\":\"2e823ceb-4de6-41ac-8025-e2ae3512a331\",\"name\":\"ra Luapula\",\"parentLocation\":{\"locationId\":\"9c3e8715-1c59-44db-9709-2b49f440ef00\",\"name\":\"ra Zambia\",\"serverVersion\":0,\"voided\":false},\"serverVersion\":0,\"voided\":false},\"tags\":[\"District\"],\"serverVersion\":0,\"voided\":false},\"parent\":\"2e823ceb-4de6-41ac-8025-e2ae3512a331\"}},\"id\":\"2e823ceb-4de6-41ac-8025-e2ae3512a331\",\"label\":\"ra Luapula\",\"node\":{\"locationId\":\"2e823ceb-4de6-41ac-8025-e2ae3512a331\",\"name\":\"ra Luapula\",\"parentLocation\":{\"locationId\":\"9c3e8715-1c59-44db-9709-2b49f440ef00\",\"name\":\"ra Zambia\",\"serverVersion\":0,\"voided\":false},\"tags\":[\"Province\"],\"serverVersion\":0,\"voided\":false},\"parent\":\"9c3e8715-1c59-44db-9709-2b49f440ef00\"}},\"id\":\"9c3e8715-1c59-44db-9709-2b49f440ef00\",\"label\":\"ra Zambia\",\"node\":{\"locationId\":\"9c3e8715-1c59-44db-9709-2b49f440ef00\",\"name\":\"ra Zambia\",\"tags\":[\"Country\"],\"serverVersion\":0,\"voided\":false}}},\"parentChildren\":{\"9c3e8715-1c59-44db-9709-2b49f440ef00\":[\"2e823ceb-4de6-41ac-8025-e2ae3512a331\"],\"ed7c4a07-6e02-4784-ae9a-9cd41cfef390\":[\"1b0ba804-54c3-40ef-820b-a8eaffa5d054\"],\"620332e0-6108-4611-bac5-8b48d20051c9\":[\"ed7c4a07-6e02-4784-ae9a-9cd41cfef390\"],\"2e823ceb-4de6-41ac-8025-e2ae3512a331\":[\"620332e0-6108-4611-bac5-8b48d20051c9\"]}}}";
+
+ ArrayList allowedLevels = new ArrayList<>();
+ allowedLevels.add("Country");
+ allowedLevels.add("Province");
+ allowedLevels.add("Region");
+ allowedLevels.add("District");
+ allowedLevels.add("Sub-district");
+ allowedLevels.add("Operational Area");
+
+ LinkedHashMap> map = AssetHandler.jsonStringToJava(locationData, LocationTree.class).getLocationsHierarchy();
+
+ Mockito.doReturn(true).when(locationHelper).isLocationTagsShownEnabled();
+ List formLocationsList = locationHelper.generateLocationHierarchyTree(false, allowedLevels, map);
+
+ assertEquals(1, formLocationsList.size());
+
+ FormLocation formLocation = formLocationsList.get(0);
+ assertEquals("Zambia", formLocation.name);
+ assertEquals("ra Zambia", formLocation.key);
+ assertEquals("Country", formLocation.level);
+ assertEquals(1, formLocation.nodes.size());
+ }
@Test
public void testGenerateLocationHierarchyTreeShouldReturnListWithOtherFormLocationOnly() {
@@ -363,7 +388,6 @@ public void testGenerateLocationHierarchyTreeShouldReturnListWithOtherFormLocati
}
-
@Test
public void testGetOpenMrsReadableName() {
assertEquals("Zambia", locationHelper.getOpenMrsReadableName("ra Zambia"));
From b6b98cbb85699f2f65619a207b9c612230115714 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Sat, 13 Jun 2020 16:04:55 +0300
Subject: [PATCH 31/70] reset android manifest config
---
sample/src/main/AndroidManifest.xml | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml
index cdf071f18..b10bfcf21 100644
--- a/sample/src/main/AndroidManifest.xml
+++ b/sample/src/main/AndroidManifest.xml
@@ -16,22 +16,20 @@
android:theme="@style/AppTheme"
tools:replace="android:theme">
-
+ android:name=".MainActivity"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme.NoActionBar">
-
-
+
From 8bbf545e7a0ec711a3206006ae4b9146a42da51b Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Sat, 13 Jun 2020 16:03:34 +0300
Subject: [PATCH 32/70] Fix clear app settings bug - Fix app crashes on login
after app manually reset
---
.../java/org/smartregister/account/AccountHelper.java | 2 +-
.../org/smartregister/login/task/RemoteLoginTask.java | 1 +
.../java/org/smartregister/security/SecurityHelper.java | 9 ++++++---
.../main/java/org/smartregister/service/UserService.java | 4 ++--
4 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
index aad68d1c1..3aca9500c 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
@@ -23,7 +23,7 @@ public static final class CONFIGURATION_CONSTANTS {
public final static String TOKEN_ENDPOINT_URL = "token_endpoint_url";
public final static String AUTHORIZATION_ENDPOINT_URL = "authorization_endpoint_url";
public final static String ISSUER_ENDPOINT_URL = "issuer_endpoint_url";
- public static final String USERINFO_ENDPOINT_URL = "userinfo_endpint_url";
+ public static final String USERINFO_ENDPOINT_URL = "userinfo_endpoint_url";
}
public static final class OAUTH {
diff --git a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
index 2dcbae997..8f679db33 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
@@ -103,6 +103,7 @@ protected LoginResponse doInBackground(Void... params) {
mAccountManager.addAccountExplicitly(account, response.getRefreshToken(), userData);
mAccountManager.setAuthToken(account, mLoginView.getAuthTokenType(), response.getAccessToken());
mAccountManager.setPassword(account, response.getRefreshToken());
+ mAccountManager.setUserData(account, AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, userData.getString(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID));
if (getOpenSRPContext().userService().getGroupId(mUsername) != null && CoreLibrary.getInstance().getSyncConfiguration().isSyncSettings()) {
diff --git a/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java b/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java
index 91b4f3d7a..4927f0553 100644
--- a/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java
@@ -37,8 +37,9 @@ public static char[] readValue(Editable editable) {
* @param array character array
*/
public static void clearArray(byte[] array) {
-
- Arrays.fill(array, (byte) 0);
+ if (array != null) {
+ Arrays.fill(array, (byte) 0);
+ }
}
/**
@@ -47,7 +48,9 @@ public static void clearArray(byte[] array) {
* @param array character array
*/
public static void clearArray(char[] array) {
- Arrays.fill(array, '*');
+ if (array != null) {
+ Arrays.fill(array, '*');
+ }
}
/**
diff --git a/opensrp-app/src/main/java/org/smartregister/service/UserService.java b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
index 06e700204..71d0387b5 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/UserService.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
@@ -330,8 +330,8 @@ private boolean loginWith(String userName, char[] password) {
try {
KeyStore.PrivateKeyEntry privateKeyEntry = getUserKeyPair(userName);
if (privateKeyEntry != null) {
- byte[] groupId = Base64.decode(encryptedGroupId, Base64.DEFAULT);
- setupContextForLogin(SecurityHelper.toChars(decryptString(privateKeyEntry, encryptedGroupId)));
+ byte[] groupId = decryptString(privateKeyEntry, encryptedGroupId);
+ setupContextForLogin(SecurityHelper.toChars(groupId));
SecurityHelper.clearArray(groupId);
}
} catch (Exception e) {
From 56b13f8fa40a83973093d99d2cbdf725ac111370 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Mon, 15 Jun 2020 09:24:45 +0300
Subject: [PATCH 33/70] Update Manifest configuration - Add explicit
permissions for declared components
---
opensrp-app/AndroidManifest.xml | 45 +++++++++++++++++++++++++-----
opensrp-app/res/values/strings.xml | 9 ++++++
2 files changed, 47 insertions(+), 7 deletions(-)
diff --git a/opensrp-app/AndroidManifest.xml b/opensrp-app/AndroidManifest.xml
index 168741189..27f14f738 100644
--- a/opensrp-app/AndroidManifest.xml
+++ b/opensrp-app/AndroidManifest.xml
@@ -31,9 +31,14 @@
android:largeHeap="true">
+ android:description="@string/component_desc_image_upload_service"
+ android:enabled="true"
+ android:exported="false" />
-
+
@@ -44,74 +49,100 @@
-
-
+
+
+
-
+
+
-
+
-
+
Do you want to clear data to login with a different team/location
You are trying to login with a user in a different team/location. Upload of pending data and clearing of data for user %s in Team %s is required. Do you want to continue?
Replication error occurred
+ Form Update
+ This form has content updates
+ (Current Corrupted Form)
+ Select a rollback form
+ You cannot select this form because it\'s corrupted!
+ Form has been successfully selected! Reopen this form for the changes to take effect
+ Uploads images to the OpenSRP server
+ Manages user authentication and authorization
+ Processes the records when using peer to peer for file sharing
From 1d4e6e43c0c024878288ff66240a3c3ce0d4e173 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Mon, 15 Jun 2020 09:29:47 +0300
Subject: [PATCH 34/70] Fix build error - Remove multidex declaration
---
opensrp-app/build.gradle | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/opensrp-app/build.gradle b/opensrp-app/build.gradle
index 7da0d4b5f..1d43df1f3 100644
--- a/opensrp-app/build.gradle
+++ b/opensrp-app/build.gradle
@@ -208,6 +208,20 @@ dependencies {
implementation 'org.smartregister:opensrp-plan-evaluator:0.0.19-SNAPSHOT'
implementation 'xerces:xercesImpl:2.12.0'
+ implementation('org.smartregister:opensrp-client-native-form:1.9.2-SNAPSHOT@aar') {
+ transitive = true
+ exclude group: 'id.zelory', module: 'compressor'
+ exclude group: 'com.android.support', module: 'recyclerview-v7'
+ exclude group: 'com.android.support', module: 'appcompat-v7'
+ exclude group: 'com.android.support', module: 'cardview-v7'
+ exclude group: 'com.android.support', module: 'support-media-compat'
+ exclude group: 'com.android.support', module: 'support-v4'
+ exclude group: 'com.android.support', module: 'design'
+ exclude group: 'org.yaml', module: 'snakeyaml'
+ exclude group: 'io.ona.rdt-capture', module: 'lib'
+ exclude group: 'com.github.johnkil.print', module: 'print'
+ exclude group: 'com.android.support', module: 'multidex'
+ }
}
dependencies {
@@ -215,6 +229,7 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
androidTestImplementation 'junit:junit:4.12'
+ testImplementation 'com.android.support:multidex:1.0.0'
testImplementation group: 'com.google.android', name: 'android-test', version: '4.1.1.4'
testImplementation 'org.apache.maven:maven-ant-tasks:2.1.3'
testImplementation 'org.mockito:mockito-core:1.9.5'
From cd93e8acff40c94c78b6e72c41eda5566b4f0879 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Mon, 15 Jun 2020 14:12:27 +0300
Subject: [PATCH 35/70] Support for local Multi-tenancy - Refactor offline
multi tenancy implementation for same Team Members - Adds support for No need
to remotely authenticate if all team members had logged in device previously
---
.../smartregister/account/AccountHelper.java | 56 +++++++++++++++----
.../login/interactor/BaseLoginInteractor.java | 6 +-
.../repository/AllSharedPreferences.java | 20 +++++--
.../org/smartregister/service/HTTPAgent.java | 6 +-
.../smartregister/service/UserService.java | 25 +++++----
.../util/OpenSRPImageLoader.java | 2 +-
.../account/AccountHelperTest.java | 8 +--
.../repository/AllSharedPreferencesTest.java | 9 +--
.../smartregister/service/HTTPAgentTest.java | 10 ++--
9 files changed, 96 insertions(+), 46 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
index 3aca9500c..5afb39d36 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
@@ -54,18 +54,46 @@ public static final class TOKEN_TYPE {
public final static String ADMIN = "admin";
}
+ /**
+ * Gets OAuth Account by the account name and account type
+ *
+ * @param accountName name of account within account manage
+ * @param accountType unique name to identify our account type in the Account Manager
+ * @return Account retrieved
+ */
+ public static Account getOauthAccountByNameAndType(String accountName, String accountType) {
- public static Account getOauthAccountByType(String accountType) {
Account[] accounts = accountManager.getAccountsByType(accountType);
- if (accounts.length > 0) {
- Account account = accounts[0];
- return account;
+ return accounts.length > 0 ? selectAccount(accounts, accountName) : null;
+ }
+
+ /**
+ * Get specified Account by name from the list returned by the account manager
+ *
+ * @param accountName name of account within account manage
+ * @param accounts list of Accounts returned by the Account Manager
+ * @return Account selected from list
+ */
+ public static Account selectAccount(Account[] accounts, String accountName) {
+ for (Account account : accounts) {
+ if (accountName.equals(account.name)) {
+ return account;
+ }
}
+
return null;
}
- public static String getAccountManagerValue(String key, String accountType) {
- Account account = AccountHelper.getOauthAccountByType(accountType);
+ /**
+ * Gets user data value with the specified key from the Account Manager
+ *
+ * @param key of the user data value we want to retrieve
+ * @param accountType unique name to identify our account type in the Account Manager
+ * @param accountName name of account within account manage
+ * @return access token
+ */
+ public static String getAccountManagerValue(String key, String accountName, String accountType) {
+ Account account = AccountHelper.getOauthAccountByNameAndType(accountName, accountType);
if (account != null) {
return accountManager.getUserData(account, key);
}
@@ -73,12 +101,15 @@ public static String getAccountManagerValue(String key, String accountType) {
}
/**
+ * Gets OAuth Token
+ *
+ * @param accountName name of account within account manage
* @param accountType unique name to identify our account type in the Account Manager
* @param authTokenType type of token requested from server e.g. PROVIDER, ADMIN
* @return access token
*/
- public static String getOAuthToken(String accountType, String authTokenType) {
- Account account = getOauthAccountByType(accountType);
+ public static String getOAuthToken(String accountName, String accountType, String authTokenType) {
+ Account account = getOauthAccountByNameAndType(accountName, accountType);
try {
return accountManager.blockingGetAuthToken(account, authTokenType, true);
@@ -88,7 +119,9 @@ public static String getOAuthToken(String accountType, String authTokenType) {
}
}
- /** This method invalidates the auth token so that the Authenticator can fetch a new one from server
+ /**
+ * This method invalidates the auth token so that the Authenticator can fetch a new one from server
+ *
* @param accountType unique name to identify our account type in the Account Manager
* @param authToken token to invalidate
*/
@@ -98,12 +131,13 @@ public static void invalidateAuthToken(String accountType, String authToken) {
/**
+ * @param accountName name of account within account manage
* @param accountType unique name to identify our account type in the Account Manager
* @param authTokenType type of token requested from server e.g. PROVIDER, ADMIN
* @return access token cached
*/
- public static String getCachedOAuthToken(String accountType, String authTokenType) {
- Account account = getOauthAccountByType(accountType);
+ public static String getCachedOAuthToken(String accountName, String accountType, String authTokenType) {
+ Account account = getOauthAccountByNameAndType(accountName, accountType);
return accountManager.peekAuthToken(account, authTokenType);
}
diff --git a/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java b/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java
index 4c64006eb..a4c06ba20 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java
@@ -62,8 +62,10 @@ public void onDestroy(boolean isChangingConfiguration) {
@Override
public void login(WeakReference view, String userName, char[] password) {
- loginWithLocalFlag(view, !getSharedPreferences().fetchForceRemoteLogin()
- && userName.equalsIgnoreCase(getSharedPreferences().fetchRegisteredANM()), userName, password);
+
+ boolean localLogin = !getSharedPreferences().fetchForceRemoteLogin(userName);
+ loginWithLocalFlag(view, localLogin && getSharedPreferences().isRegisteredANM(userName), userName, password);
+
}
public void loginWithLocalFlag(WeakReference view, boolean localLogin, String userName, char[] password) {
diff --git a/opensrp-app/src/main/java/org/smartregister/repository/AllSharedPreferences.java b/opensrp-app/src/main/java/org/smartregister/repository/AllSharedPreferences.java
index 8c374f56c..284d8e468 100644
--- a/opensrp-app/src/main/java/org/smartregister/repository/AllSharedPreferences.java
+++ b/opensrp-app/src/main/java/org/smartregister/repository/AllSharedPreferences.java
@@ -8,6 +8,8 @@
import java.net.MalformedURLException;
import java.net.URL;
+import java.util.HashSet;
+import java.util.Set;
import static org.smartregister.AllConstants.CURRENT_LOCALITY;
import static org.smartregister.AllConstants.DEFAULT_LOCALE;
@@ -27,6 +29,7 @@
public class AllSharedPreferences {
public static final String ANM_IDENTIFIER_PREFERENCE_KEY = "anmIdentifier";
+ public static final String ANM_IDENTIFIER_SET_PREFERENCE_KEY = "anmIdentifierSet";
private static final String HOST = "HOST";
private static final String PORT = "PORT";
private static final String LAST_SYNC_DATE = "LAST_SYNC_DATE";
@@ -49,18 +52,26 @@ public AllSharedPreferences(SharedPreferences preferences) {
public void updateANMUserName(String userName) {
preferences.edit().putString(ANM_IDENTIFIER_PREFERENCE_KEY, userName).commit();
+
+ Set anmIdentifiers = new HashSet<>(preferences.getStringSet(ANM_IDENTIFIER_SET_PREFERENCE_KEY, new HashSet<>()));
+ anmIdentifiers.add(userName);
+ preferences.edit().putStringSet(ANM_IDENTIFIER_SET_PREFERENCE_KEY, anmIdentifiers).commit();
}
public String fetchRegisteredANM() {
return preferences.getString(ANM_IDENTIFIER_PREFERENCE_KEY, "").trim();
}
- public boolean fetchForceRemoteLogin() {
- return preferences.getBoolean(FORCE_REMOTE_LOGIN, true);
+ public boolean isRegisteredANM(String userName) {
+ return preferences.getStringSet(ANM_IDENTIFIER_SET_PREFERENCE_KEY, new HashSet<>()).contains(userName);
+ }
+
+ public boolean fetchForceRemoteLogin(String username) {
+ return preferences.getBoolean(new StringBuffer(FORCE_REMOTE_LOGIN).append('_').append(username).toString(), true);
}
- public void saveForceRemoteLogin(boolean forceRemoteLogin) {
- preferences.edit().putBoolean(FORCE_REMOTE_LOGIN, forceRemoteLogin).commit();
+ public void saveForceRemoteLogin(boolean forceRemoteLogin, String username) {
+ preferences.edit().putBoolean(new StringBuffer(FORCE_REMOTE_LOGIN).append('_').append(username).toString(), forceRemoteLogin).commit();
}
public String fetchServerTimeZone() {
@@ -336,5 +347,6 @@ public String fetchFormsVersion() {
public SharedPreferences getPreferences() {
return preferences;
}
+
}
diff --git a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
index a3db60634..1f8569af7 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
@@ -127,8 +127,8 @@ private HttpURLConnection initializeHttp(String requestURLPath, boolean setOauth
urlConnection.setReadTimeout(getReadTimeout());
if (setOauthToken) {
AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
- if (AccountHelper.getOauthAccountByType(authenticatorXml.getAccountType()) != null)
- urlConnection.setRequestProperty(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, new StringBuilder(AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BEARER + " ").append(AccountHelper.getOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).toString());
+ if (AccountHelper.getOauthAccountByNameAndType(allSharedPreferences.fetchRegisteredANM(), authenticatorXml.getAccountType()) != null)
+ urlConnection.setRequestProperty(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, new StringBuilder(AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BEARER + " ").append(AccountHelper.getOAuthToken(allSharedPreferences.fetchRegisteredANM(), authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).toString());
}
return urlConnection;
}
@@ -163,7 +163,7 @@ public Response fetch(String requestURLPath) {
public void invalidateExpiredCachedAccessToken() {
AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
- String authToken = AccountHelper.getCachedOAuthToken(authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
+ String authToken = AccountHelper.getCachedOAuthToken(allSharedPreferences.fetchRegisteredANM(), authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
if (authToken != null)
AccountHelper.invalidateAuthToken(authenticatorXml.getAccountType(), authToken);
}
diff --git a/opensrp-app/src/main/java/org/smartregister/service/UserService.java b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
index 71d0387b5..c49400f2b 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/UserService.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
@@ -168,7 +168,7 @@ public TimeStatus validateStoredServerTimeZone() {
}
if (!result.equals(TimeStatus.OK)) {
- forceRemoteLogin();
+ forceRemoteLogin(allSharedPreferences.fetchRegisteredANM());
}
return result;
@@ -209,12 +209,12 @@ public TimeStatus validateDeviceTime(LoginResponseData userInfo, long serverTime
public boolean isValidLocalLogin(String userName, char[] password) {
return allSharedPreferences.fetchRegisteredANM().equals(userName) && DrishtiApplication.getInstance().getRepository()
- .canUseThisPassword(password) && !allSharedPreferences.fetchForceRemoteLogin();
+ .canUseThisPassword(password) && !allSharedPreferences.fetchForceRemoteLogin(userName);
}
public boolean isUserInValidGroup(final String userName, final char[] password) {
// Check if everything OK for local login
- if (keyStore != null && userName != null && password != null && !allSharedPreferences.fetchForceRemoteLogin()) {
+ if (keyStore != null && userName != null && password != null && !allSharedPreferences.fetchForceRemoteLogin(userName)) {
String username = userName.equalsIgnoreCase(allSharedPreferences.fetchRegisteredANM()) ? allSharedPreferences.fetchRegisteredANM() : userName;
try {
KeyStore.PrivateKeyEntry privateKeyEntry = getUserKeyPair(username);
@@ -251,7 +251,7 @@ public char[] getGroupId(String userName) {
public char[] getGroupId(String userName, KeyStore.PrivateKeyEntry privateKeyEntry) {
if (privateKeyEntry != null) {
- String encryptedGroupId = AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType());
+ String encryptedGroupId = AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, userName, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType());
if (encryptedGroupId != null) {
try {
@@ -323,14 +323,14 @@ public Response getLocationInformation() {
private boolean loginWith(String userName, char[] password) {
boolean loginSuccessful = true;
- if (usesGroupIdAsDBPassword()) {
+ if (usesGroupIdAsDBPassword(userName)) {
- String encryptedGroupId = AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType());
+ String encryptedGroupId = AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, userName, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType());
try {
KeyStore.PrivateKeyEntry privateKeyEntry = getUserKeyPair(userName);
if (privateKeyEntry != null) {
- byte[] groupId = decryptString(privateKeyEntry, encryptedGroupId);
+ byte[] groupId = decryptString(privateKeyEntry, encryptedGroupId);
setupContextForLogin(SecurityHelper.toChars(groupId));
SecurityHelper.clearArray(groupId);
}
@@ -353,10 +353,11 @@ private boolean loginWith(String userName, char[] password) {
/**
* Checks whether to use the groupId for the current user to decrypt the database
*
+ * @param username the username
* @return TRUE if the user decrypts the database using the groupId
*/
- private boolean usesGroupIdAsDBPassword() {
- return AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType()) != null;
+ private boolean usesGroupIdAsDBPassword(String username) {
+ return AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, username, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType()) != null;
}
public void localLogin(String userName, char[] password) {
@@ -384,11 +385,11 @@ public void processLoginResponseDataForUser(String userName, char[] password, Lo
StringUtils.isNotBlank(allSharedPreferences.fetchDefaultTeamId(username))) &&
(getUserLocation(userInfo) != null ||
StringUtils.isNotBlank(allSettings.fetchANMLocation())))
- allSharedPreferences.saveForceRemoteLogin(false);
+ allSharedPreferences.saveForceRemoteLogin(false, username);
}
- public void forceRemoteLogin() {
- allSharedPreferences.saveForceRemoteLogin(true);
+ public void forceRemoteLogin(String userName) {
+ allSharedPreferences.saveForceRemoteLogin(true, userName);
}
public User getUserData(LoginResponseData userInfo) {
diff --git a/opensrp-app/src/main/java/org/smartregister/util/OpenSRPImageLoader.java b/opensrp-app/src/main/java/org/smartregister/util/OpenSRPImageLoader.java
index 4a56c87e9..6375a4059 100644
--- a/opensrp-app/src/main/java/org/smartregister/util/OpenSRPImageLoader.java
+++ b/opensrp-app/src/main/java/org/smartregister/util/OpenSRPImageLoader.java
@@ -186,7 +186,7 @@ public HttpResponse performRequest(Request> request, Map heade
}
private static void addBearerTokenAuthorizationHeader(Map headers) {
- String accessToken = AccountHelper.getOAuthToken(CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
+ String accessToken = AccountHelper.getOAuthToken(CoreLibrary.getInstance().context().allSharedPreferences().fetchRegisteredANM(), CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
headers.put(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, new StringBuilder(AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BEARER + " ").append(accessToken).toString());
}
diff --git a/opensrp-app/src/test/java/org/smartregister/account/AccountHelperTest.java b/opensrp-app/src/test/java/org/smartregister/account/AccountHelperTest.java
index 307e058dd..0a6417d15 100644
--- a/opensrp-app/src/test/java/org/smartregister/account/AccountHelperTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/account/AccountHelperTest.java
@@ -45,7 +45,7 @@ public void setUp() throws Exception {
@Test
public void testGetOauthAccountByType() {
- Account account = AccountHelper.getOauthAccountByType(CORE_ACCOUNT_TYPE);
+ Account account = AccountHelper.getOauthAccountByNameAndType(CORE_ACCOUNT_NAME, CORE_ACCOUNT_TYPE);
Assert.assertNotNull(account);
Assert.assertEquals(CORE_ACCOUNT_NAME, account.name);
}
@@ -57,7 +57,7 @@ public void testGetAccountManagerValue() {
Mockito.doReturn(TEST_VALUE).when(accountManager).getUserData(ArgumentMatchers.any(Account.class), ArgumentMatchers.eq(TEST_KEY));
- String value = AccountHelper.getAccountManagerValue(TEST_KEY, CORE_ACCOUNT_TYPE);
+ String value = AccountHelper.getAccountManagerValue(TEST_KEY, CORE_ACCOUNT_NAME, CORE_ACCOUNT_TYPE);
Assert.assertNotNull(value);
Assert.assertEquals(TEST_VALUE, value);
}
@@ -66,7 +66,7 @@ public void testGetAccountManagerValue() {
public void testGetOAuthToken() throws AuthenticatorException, OperationCanceledException, IOException {
Mockito.doReturn(TEST_TOKEN_VALUE).when(accountManager).blockingGetAuthToken(new Account(CORE_ACCOUNT_NAME, CORE_ACCOUNT_TYPE), AUTH_TOKEN_TYPE, true);
- String myToken = AccountHelper.getOAuthToken(CORE_ACCOUNT_TYPE, AUTH_TOKEN_TYPE);
+ String myToken = AccountHelper.getOAuthToken(CORE_ACCOUNT_NAME, CORE_ACCOUNT_TYPE, AUTH_TOKEN_TYPE);
Assert.assertNotNull(myToken);
Assert.assertEquals(TEST_TOKEN_VALUE, myToken);
}
@@ -84,7 +84,7 @@ public void testGetCachedOAuthToken() {
Account account = new Account(CORE_ACCOUNT_NAME, CORE_ACCOUNT_TYPE);
Mockito.doReturn(TEST_TOKEN_VALUE).when(accountManager).peekAuthToken(account, AUTH_TOKEN_TYPE);
- String cachedAuthToken = AccountHelper.getCachedOAuthToken(CORE_ACCOUNT_TYPE, AUTH_TOKEN_TYPE);
+ String cachedAuthToken = AccountHelper.getCachedOAuthToken(CORE_ACCOUNT_NAME, CORE_ACCOUNT_TYPE, AUTH_TOKEN_TYPE);
Mockito.verify(accountManager).peekAuthToken(account, AUTH_TOKEN_TYPE);
Assert.assertEquals(TEST_TOKEN_VALUE, cachedAuthToken);
diff --git a/opensrp-app/src/test/java/org/smartregister/repository/AllSharedPreferencesTest.java b/opensrp-app/src/test/java/org/smartregister/repository/AllSharedPreferencesTest.java
index 1807a988f..b95139098 100644
--- a/opensrp-app/src/test/java/org/smartregister/repository/AllSharedPreferencesTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/repository/AllSharedPreferencesTest.java
@@ -30,6 +30,7 @@ public class AllSharedPreferencesTest extends TestCase {
private SharedPreferences preferences;
private static final String HOST = "HOST";
private static final String PORT = "PORT";
+ private static final String USERNAME = "USERNAME";
AllSharedPreferences allSharedPreferences;
private final String str = "default";
@@ -54,13 +55,13 @@ public void tearDown() {
@Test
public void assertupdateANMUserNameCallsPreferenceEdit() {
allSharedPreferences.updateANMUserName("");
- Mockito.verify(preferences, Mockito.times(1)).edit();
+ Mockito.verify(preferences, Mockito.times(2)).edit();
}
@Test
public void assertFetchForceRemoteLogin() {
- Mockito.when(preferences.getBoolean(AllConstants.FORCE_REMOTE_LOGIN, true)).thenReturn(true);
- Assert.assertEquals(allSharedPreferences.fetchForceRemoteLogin(), true);
+ Mockito.when(preferences.getBoolean(AllConstants.FORCE_REMOTE_LOGIN + "_" + USERNAME, true)).thenReturn(true);
+ Assert.assertEquals(allSharedPreferences.fetchForceRemoteLogin(USERNAME), true);
}
@Test
@@ -168,7 +169,7 @@ public void assertFetchDefaultLocalityId() {
@Test
public void assertSaveForceRemoteLogin() {
- allSharedPreferences.saveForceRemoteLogin(true);
+ allSharedPreferences.saveForceRemoteLogin(true, USERNAME);
Mockito.verify(preferences, Mockito.times(1)).edit();
}
diff --git a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
index 3a8f0ad83..87c46c40b 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
@@ -176,8 +176,8 @@ public void setUp() {
Mockito.doReturn(1).when(syncConfiguration).getMaxAuthenticationRetries();
PowerMockito.mockStatic(AccountHelper.class);
- PowerMockito.when(AccountHelper.getOauthAccountByType(accountAuthenticatorXml.getAccountType())).thenReturn(account);
- PowerMockito.when(AccountHelper.getOAuthToken(accountAuthenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).thenReturn(SAMPLE_TEST_TOKEN);
+ PowerMockito.when(AccountHelper.getOauthAccountByNameAndType(TEST_USERNAME, accountAuthenticatorXml.getAccountType())).thenReturn(account);
+ PowerMockito.when(AccountHelper.getOAuthToken(TEST_USERNAME, accountAuthenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).thenReturn(SAMPLE_TEST_TOKEN);
httpAgent = new HTTPAgent(context, allSharedPreferences, dristhiConfiguration);
httpAgent.setConnectTimeout(60000);
@@ -509,7 +509,7 @@ public void testFetchInvalidatesCacheIfUnauthorizedAndReturnsCorrectResponse() t
PowerMockito.when(IOUtils.toString(errorStream)).thenReturn(FETCH_DATA_REQUEST_SERVER_RESPONSE);
PowerMockito.mockStatic(AccountHelper.class);
- PowerMockito.when(AccountHelper.getCachedOAuthToken(accountAuthenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).thenReturn(SAMPLE_TEST_TOKEN);
+ PowerMockito.when(AccountHelper.getCachedOAuthToken(TEST_USERNAME, accountAuthenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).thenReturn(SAMPLE_TEST_TOKEN);
Response response = httpAgentSpy.fetch(SECURE_RESOURCE_ENDPOINT);
Assert.assertNotNull(response);
@@ -534,7 +534,7 @@ public void testPostInvokesInvalidateCacheIfUnauthorizedOnFirstAttempt() throws
PowerMockito.when(IOUtils.toString(errorStream)).thenReturn(FETCH_DATA_REQUEST_SERVER_RESPONSE);
PowerMockito.mockStatic(AccountHelper.class);
- PowerMockito.when(AccountHelper.getCachedOAuthToken(accountAuthenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).thenReturn(SAMPLE_TEST_TOKEN);
+ PowerMockito.when(AccountHelper.getCachedOAuthToken(TEST_USERNAME, accountAuthenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).thenReturn(SAMPLE_TEST_TOKEN);
Mockito.doReturn(httpURLConnection).when(httpAgentSpy).generatePostRequest(SECURE_RESOURCE_ENDPOINT, SAMPLE_POST_REQUEST_PAYLOAD);
@@ -557,7 +557,7 @@ public void testFetchWithCredentialsInvokesInvalidateCacheIfUnauthorizedOnFirstA
PowerMockito.when(IOUtils.toString(errorStream)).thenReturn(FETCH_DATA_REQUEST_SERVER_RESPONSE);
PowerMockito.mockStatic(AccountHelper.class);
- PowerMockito.when(AccountHelper.getCachedOAuthToken(accountAuthenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).thenReturn(SAMPLE_TEST_TOKEN);
+ PowerMockito.when(AccountHelper.getCachedOAuthToken(TEST_USERNAME, accountAuthenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).thenReturn(SAMPLE_TEST_TOKEN);
Response response = httpAgentSpy.fetchWithCredentials(SECURE_RESOURCE_ENDPOINT, SAMPLE_TEST_TOKEN);
Assert.assertNotNull(response);
From 75cded41ec3d2589fe0be69ed1caf434526ebcf4 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Mon, 15 Jun 2020 14:23:32 +0300
Subject: [PATCH 36/70] Refactor forced logout re-authentication - Refactor
Re-Authentication to use Account manager - For clean up/Maintainalibility
---
.../account/AccountAuthenticator.java | 12 +++++--
.../smartregister/account/AccountHelper.java | 17 +++++++++-
.../smartregister/account/AccountService.java | 9 +++--
.../login/task/RemoteLoginTask.java | 4 +--
.../helper/SyncSettingsServiceHelper.java | 32 +++++------------
.../org/smartregister/util/SyncUtils.java | 34 +++++++++++++++----
.../account/AccountHelperTest.java | 14 ++++++++
.../helper/SyncSettingsServiceHelperTest.java | 2 +-
8 files changed, 86 insertions(+), 38 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java b/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
index 0c2a75bdb..92d3a9389 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
@@ -111,11 +111,19 @@ public Bundle editProperties(AccountAuthenticatorResponse response, String accou
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
- return null;
+ return options;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
- return null;
+
+ final Intent intent = new Intent(mContext, CoreLibrary.getInstance().getSyncConfiguration().getAuthenticationActivity());
+ intent.putExtra(AccountHelper.INTENT_KEY.AUTH_TYPE, authTokenType);
+ intent.putExtra(AccountHelper.INTENT_KEY.IS_NEW_ACCOUNT, false);
+ intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
+
+ final Bundle bundle = new Bundle();
+ bundle.putParcelable(AccountManager.KEY_INTENT, intent);
+ return bundle;
}
}
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
index 5afb39d36..91d757993 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
@@ -2,6 +2,8 @@
import android.accounts.Account;
import android.accounts.AccountManager;
+import android.accounts.AccountManagerFuture;
+import android.os.Bundle;
import org.smartregister.CoreLibrary;
@@ -14,7 +16,6 @@ public class AccountHelper {
private static AccountManager accountManager = CoreLibrary.getInstance().getAccountManager();
- public final static String KEY_REFRESH_TOKEN = "KEY_REFRESH_TOKEN";
public final static int MAX_AUTH_RETRIES = 1;
@@ -141,5 +142,19 @@ public static String getCachedOAuthToken(String accountName, String accountType,
return accountManager.peekAuthToken(account, authTokenType);
}
+ /**
+ * Prompt the user to re-authenticate
+ *
+ * @param accountName name of account within account manage
+ * @param accountType unique name to identify our account type in the Account Manager
+ * @param authTokenType type of token requested from server e.g. PROVIDER, ADMIN
+ * @return access token
+ */
+ public static AccountManagerFuture reAuthenticateUserAfterSessionExpired(String accountName, String accountType, String authTokenType) {
+ Account account = getOauthAccountByNameAndType(accountName, accountType);
+ return accountManager.updateCredentials(account, authTokenType, null, null, null, null);
+
+
+ }
}
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountService.java b/opensrp-app/src/main/java/org/smartregister/account/AccountService.java
index 2056fc9c8..51da428bf 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountService.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountService.java
@@ -9,10 +9,15 @@
*/
public class AccountService extends Service {
+ private AccountAuthenticator authenticator;
+
@Override
- public IBinder onBind(Intent intent) {
+ public void onCreate() {
+ authenticator = new AccountAuthenticator(this);
+ }
- AccountAuthenticator authenticator = new AccountAuthenticator(this);
+ @Override
+ public IBinder onBind(Intent intent) {
return authenticator.getIBinder();
}
diff --git a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
index 8f679db33..c86268f6a 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
@@ -159,10 +159,10 @@ public static Context getOpenSRPContext() {
return CoreLibrary.getInstance().context();
}
- protected JSONArray pullSetting(SyncSettingsServiceHelper syncSettingsServiceHelper, LoginResponse loginResponse) {
+ protected JSONArray pullSetting(SyncSettingsServiceHelper syncSettingsServiceHelper, LoginResponse loginResponse, String accessToken) {
JSONArray settings = new JSONArray();
try {
- settings = syncSettingsServiceHelper.pullSettingsFromServer(Utils.getFilterValue(loginResponse, CoreLibrary.getInstance().getSyncConfiguration().getSyncFilterParam()));
+ settings = syncSettingsServiceHelper.pullSettingsFromServer(Utils.getFilterValue(loginResponse, CoreLibrary.getInstance().getSyncConfiguration().getSyncFilterParam()), accessToken);
} catch (JSONException e) {
Timber.e(e);
}
diff --git a/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java b/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java
index 0c52b8535..e802bcb75 100644
--- a/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java
@@ -74,8 +74,8 @@ private JSONArray getSettings() throws JSONException {
String authToken = AccountHelper.getCachedOAuthToken(sharedPreferences.fetchRegisteredANM(), authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
JSONArray settings = pullSettingsFromServer(getInstance().getSyncConfiguration().getSettingsSyncFilterValue(), authToken);
- getGlobalSettings(settings);
- getExtraSettings(settings);
+ getGlobalSettings(settings, authToken);
+ getExtraSettings(settings, authToken);
return settings;
}
@@ -85,14 +85,14 @@ protected CoreLibrary getInstance() {
}
// will automatically use the resolve check
- private void getExtraSettings(JSONArray settings) throws JSONException {
+ private void getExtraSettings(JSONArray settings, String accessToken) throws JSONException {
JSONArray completeExtraSettings = new JSONArray();
if (getInstance().getSyncConfiguration().hasExtraSettingsSync()) {
List syncParams = getInstance().getSyncConfiguration().getExtraSettingsParameters();
if (syncParams.size() > 0) {
for (String params : syncParams) {
String url = SettingsSyncIntentService.SETTINGS_URL + "?" + params + "&" + AllConstants.SERVER_VERSION + "=" + sharedPreferences.fetchLastSettingsSyncTimeStamp() + "&" + AllConstants.RESOLVE + "=" + getInstance().getSyncConfiguration().resolveSettings();
- JSONArray extraSettings = pullSettings(url);
+ JSONArray extraSettings = pullSettings(url, accessToken);
if (extraSettings != null) {
aggregateSettings(completeExtraSettings, extraSettings);
}
@@ -103,10 +103,10 @@ private void getExtraSettings(JSONArray settings) throws JSONException {
}
}
- private void getGlobalSettings(JSONArray settings) throws JSONException {
+ private void getGlobalSettings(JSONArray settings, String accessToken) throws JSONException {
JSONArray globalSettings = new JSONArray();
if (getInstance().getSyncConfiguration().hasGlobalSettings()) {
- globalSettings = pullGlobalSettingsFromServer();
+ globalSettings = pullGlobalSettingsFromServer(accessToken);
}
aggregateSettings(settings, globalSettings);
@@ -170,9 +170,9 @@ protected SyncFilter getSettingsSyncFilterParam() {
* @return settings {@link JSONArray} -- a JSON array of all the settings
* @throws JSONException
*/
- public JSONArray pullGlobalSettingsFromServer() throws JSONException {
+ public JSONArray pullGlobalSettingsFromServer(String accessToken) throws JSONException {
String url = SettingsSyncIntentService.SETTINGS_URL + "?" + AllConstants.SERVER_VERSION + "=" + sharedPreferences.fetchLastSettingsSyncTimeStamp();
- return pullSettings(url);
+ return pullSettings(url, accessToken);
}
@@ -219,20 +219,4 @@ private JSONArray pullSettings(String directoryUrl, String accessToken) throws J
protected Response getResponse(String completeUrl, String accessToken) {
return httpAgent.fetchWithCredentials(completeUrl, accessToken);
}
-
- public String getUsername() {
- return username != null ? username : sharedPreferences.fetchRegisteredANM();
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public String getPassword() {
- return password != null ? password : getInstance().context().allSettings().fetchANMPassword();
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
}
diff --git a/opensrp-app/src/main/java/org/smartregister/util/SyncUtils.java b/opensrp-app/src/main/java/org/smartregister/util/SyncUtils.java
index 5a31f61c4..5d34dd85a 100644
--- a/opensrp-app/src/main/java/org/smartregister/util/SyncUtils.java
+++ b/opensrp-app/src/main/java/org/smartregister/util/SyncUtils.java
@@ -1,9 +1,15 @@
package org.smartregister.util;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
@@ -11,10 +17,12 @@
import org.json.JSONObject;
import org.smartregister.CoreLibrary;
import org.smartregister.R;
+import org.smartregister.account.AccountHelper;
import org.smartregister.domain.Setting;
import org.smartregister.repository.AllSettings;
import org.smartregister.repository.BaseRepository;
+import java.io.IOException;
import java.util.List;
import timber.log.Timber;
@@ -44,12 +52,27 @@ public boolean verifyAuthorization() {
return CoreLibrary.getInstance().context().getHttpAgent().verifyAuthorization();
}
- public void logoutUser() {
+ public void logoutUser() throws AuthenticatorException, OperationCanceledException, IOException {
//force remote login
- opensrpContent.userService().forceRemoteLogin();
+ opensrpContent.userService().forceRemoteLogin(opensrpContent.allSharedPreferences().fetchRegisteredANM());
+
+ Intent logoutUserIntent = getLogoutUserIntent();
+
+ AccountManagerFuture reAuthenticateFuture = AccountHelper.reAuthenticateUserAfterSessionExpired(opensrpContent.allSharedPreferences().fetchRegisteredANM(), CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
+ Intent accountAuthenticatorIntent = reAuthenticateFuture.getResult().getParcelable(AccountManager.KEY_INTENT);
+ accountAuthenticatorIntent.putExtras(logoutUserIntent);
+ context.startActivity(logoutUserIntent);
+
+ //logoff opensrp session
+ opensrpContent.userService().logoutSession();
+ }
+
+ @NonNull
+ private Intent getLogoutUserIntent() {
+
Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setPackage(context.getPackageName());
+
//retrieve the main/launcher activity defined in the manifest and open it
List activities = context.getPackageManager().queryIntentActivities(intent, 0);
if (activities.size() == 1) {
@@ -58,10 +81,9 @@ public void logoutUser() {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(ACCOUNT_DISABLED, context.getString(R.string.account_disabled_logged_off));
- context.startActivity(intent);
}
- //logoff opensrp session
- opensrpContent.userService().logoutSession();
+
+ return intent;
}
public boolean isAppVersionAllowed() throws PackageManager.NameNotFoundException {
diff --git a/opensrp-app/src/test/java/org/smartregister/account/AccountHelperTest.java b/opensrp-app/src/test/java/org/smartregister/account/AccountHelperTest.java
index 0a6417d15..9346512c7 100644
--- a/opensrp-app/src/test/java/org/smartregister/account/AccountHelperTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/account/AccountHelperTest.java
@@ -2,8 +2,10 @@
import android.accounts.Account;
import android.accounts.AccountManager;
+import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
+import android.os.Bundle;
import org.junit.Assert;
import org.junit.Before;
@@ -90,4 +92,16 @@ public void testGetCachedOAuthToken() {
Assert.assertEquals(TEST_TOKEN_VALUE, cachedAuthToken);
}
+ @Test
+ public void testReAuthenticateUserAfterSessionExpired() {
+
+ Account account = new Account(CORE_ACCOUNT_NAME, CORE_ACCOUNT_TYPE);
+ Mockito.doReturn(Mockito.mock(AccountManagerFuture.class)).when(accountManager).updateCredentials(account, AUTH_TOKEN_TYPE, null, null, null, null);
+
+ AccountManagerFuture reAuthenticationFuture = AccountHelper.reAuthenticateUserAfterSessionExpired(CORE_ACCOUNT_NAME, CORE_ACCOUNT_TYPE, AUTH_TOKEN_TYPE);
+ Assert.assertNotNull(reAuthenticationFuture);
+
+ Mockito.verify(accountManager).updateCredentials(account, AUTH_TOKEN_TYPE, null, null, null, null);
+ }
+
}
diff --git a/opensrp-app/src/test/java/org/smartregister/sync/helper/SyncSettingsServiceHelperTest.java b/opensrp-app/src/test/java/org/smartregister/sync/helper/SyncSettingsServiceHelperTest.java
index 26d513133..97b3d3959 100644
--- a/opensrp-app/src/test/java/org/smartregister/sync/helper/SyncSettingsServiceHelperTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/sync/helper/SyncSettingsServiceHelperTest.java
@@ -83,7 +83,7 @@ public void testProcessIntent() throws JSONException {
params.add("locationId=location-uuid");
Mockito.doReturn(params).when(syncConfiguration).getExtraSettingsParameters();
- Mockito.doReturn(new Response<>(ResponseStatus.success, settingsResponse)).when(syncSettingsServiceHelper).getResponse(ArgumentMatchers.anyString());
+ Mockito.doReturn(new Response<>(ResponseStatus.success, settingsResponse)).when(syncSettingsServiceHelper).getResponse(ArgumentMatchers.anyString(), ArgumentMatchers.anyString());
int size = syncSettingsServiceHelper.processIntent();
Assert.assertEquals(3, size);
}
From dd367d9594ae4a46530bcabb642786e97b7ce5e1 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Mon, 15 Jun 2020 10:18:10 +0300
Subject: [PATCH 37/70] Implement password hashing - Implement password salting
and hashing for offline authentication
---
.../smartregister/account/AccountHelper.java | 4 +-
.../login/task/RemoteLoginTask.java | 8 +
.../smartregister/security/PasswordHash.java | 23 +++
.../security/SecurityHelper.java | 69 +++++++-
.../smartregister/service/UserService.java | 31 +++-
.../security/SecurityHelperTest.java | 165 ++++++++++++++++++
6 files changed, 289 insertions(+), 11 deletions(-)
create mode 100644 opensrp-app/src/main/java/org/smartregister/security/PasswordHash.java
create mode 100644 opensrp-app/src/test/java/org/smartregister/security/SecurityHelperTest.java
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
index 91d757993..9ebf9cb37 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
@@ -44,10 +44,10 @@ public static final class INTENT_KEY {
public final static String ACCOUNT_TYPE = "ACCOUNT_TYPE";
public final static String AUTH_TYPE = "AUTH_TYPE";
public final static String ACCOUNT_NAME = "ACCOUNT_NAME";
- public static final String ERROR_MESSAGE = "ERROR_MESSAGE";
- public final static String ACCOUNT_PASSWORD = "ACCOUNT_PASSWORD";
public final static String IS_NEW_ACCOUNT = "IS_NEW_ACCOUNT";
public final static String ACCOUNT_GROUP_ID = "ACCOUNT_GROUP_ID";
+ public final static String ACCOUNT_PASSWORD = "ACCOUNT_PASSWORD";
+ public final static String ACCOUNT_PASSWORD_SALT = "ACCOUNT_PASSWORD_SALT";
}
public static final class TOKEN_TYPE {
diff --git a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
index c86268f6a..f08eff894 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
@@ -5,6 +5,7 @@
import android.accounts.AccountsException;
import android.content.SharedPreferences;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Bundle;
import org.json.JSONArray;
@@ -104,6 +105,13 @@ protected LoginResponse doInBackground(Void... params) {
mAccountManager.setAuthToken(account, mLoginView.getAuthTokenType(), response.getAccessToken());
mAccountManager.setPassword(account, response.getRefreshToken());
mAccountManager.setUserData(account, AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, userData.getString(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID));
+ mAccountManager.setUserData(account, AccountHelper.INTENT_KEY.ACCOUNT_NAME, userData.getString(AccountHelper.INTENT_KEY.ACCOUNT_NAME));
+ mAccountManager.setUserData(account, AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD, userData.getString(AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD));
+ mAccountManager.setUserData(account, AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD_SALT, userData.getString(AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD_SALT));
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ mAccountManager.notifyAccountAuthenticated(account);
+ }
if (getOpenSRPContext().userService().getGroupId(mUsername) != null && CoreLibrary.getInstance().getSyncConfiguration().isSyncSettings()) {
diff --git a/opensrp-app/src/main/java/org/smartregister/security/PasswordHash.java b/opensrp-app/src/main/java/org/smartregister/security/PasswordHash.java
new file mode 100644
index 000000000..5aa684126
--- /dev/null
+++ b/opensrp-app/src/main/java/org/smartregister/security/PasswordHash.java
@@ -0,0 +1,23 @@
+package org.smartregister.security;
+
+/**
+ * Created by ndegwamartin on 13/06/2020.
+ */
+public class PasswordHash {
+
+ private byte[] salt;
+ private byte[] password;
+
+ PasswordHash(byte[] salt, byte[] password) {
+ this.salt = salt;
+ this.password = password;
+ }
+
+ public byte[] getSalt() {
+ return salt;
+ }
+
+ public byte[] getPassword() {
+ return password;
+ }
+}
diff --git a/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java b/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java
index 4927f0553..8ba95e78b 100644
--- a/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java
@@ -1,22 +1,33 @@
package org.smartregister.security;
+import android.os.Build;
import android.text.Editable;
+import android.util.Base64;
import org.apache.commons.codec.CharEncoding;
+import org.apache.commons.lang3.StringUtils;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
import java.util.Arrays;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+
/**
* Created by ndegwamartin on 04/06/2020.
*/
public class SecurityHelper {
- private static Charset charset = Charset.forName(CharEncoding.UTF_8);
+ private static final Charset CHARSET = Charset.forName(CharEncoding.UTF_8);
+ public static final int ITERATION_COUNT = 200048;
/**
* This method ensures that sensitive info can be collected for the edit text in a safer way
@@ -62,7 +73,7 @@ public static void clearArray(char[] array) {
public static byte[] toBytes(StringBuffer stringBuffer) throws CharacterCodingException {
- CharsetEncoder encoder = charset.newEncoder();
+ CharsetEncoder encoder = CHARSET.newEncoder();
CharBuffer buffer = CharBuffer.wrap(stringBuffer);
@@ -83,15 +94,15 @@ private static void clearStringBuffer(StringBuffer stringBuffer) {
}
/**
- * This method converts characters in the string buffer to byte array without creating a String object
+ * This method converts characters in the char array buffer to a byte array
*
* @param chars array
- * @return an array of bytes , a conversion from the chars array
+ * @return an array of bytes, a conversion from the chars array
*/
public static byte[] toBytes(char[] chars) {
CharBuffer charBuffer = CharBuffer.wrap(chars);
- ByteBuffer byteBuffer = charset.encode(charBuffer);
+ ByteBuffer byteBuffer = CHARSET.encode(charBuffer);
byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
@@ -101,6 +112,12 @@ public static byte[] toBytes(char[] chars) {
}
+ /**
+ * This method converts characters in the byte array buffer to a char array
+ *
+ * @param bytes array
+ * @return an array of chars, a conversion from the bytes array
+ */
public static char[] toChars(byte[] bytes) {
char[] convertedChar = new char[bytes.length];
@@ -109,6 +126,48 @@ public static char[] toChars(byte[] bytes) {
}
return convertedChar;
+ }
+
+ /**
+ * @param password password to hash
+ * @return Password Hash object containing the bytes of the salt and the hashed password
+ * @throws NoSuchAlgorithmException when the Android API level doesn't support the specified Algorithm
+ * @throws InvalidKeySpecException when the Key used for generation has an invalid configuration
+ */
+ public static PasswordHash getPasswordHash(char[] password) throws NoSuchAlgorithmException, InvalidKeySpecException {
+
+ byte[] salt = new byte[128];
+
+ SecureRandom secureRandom = new SecureRandom();
+ secureRandom.nextBytes(salt);
+
+ return new PasswordHash(salt, hashPassword(password, salt));
+ }
+
+ /**
+ * @param password password to hash
+ * @return byte array of the bytes of the salt and the hashed password
+ */
+ public static byte[] hashPassword(char[] password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
+
+ int keyLength = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? 256 : 160;
+
+ SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? "PBKDF2withHmacSHA256" : "PBKDF2WithHmacSHA1");
+ KeySpec pbKeySpec = new PBEKeySpec(password, salt, ITERATION_COUNT, keyLength);
+
+ return secretKeyFactory.generateSecret(pbKeySpec).getEncoded();
+ }
+
+ /**
+ * @param base64EncodedValue The base64 Encoded value
+ * @return decoded array of bytes
+ */
+ public static byte[] nullSafeBase64Decode(String base64EncodedValue) {
+ if (!StringUtils.isBlank(base64EncodedValue)) {
+ return Base64.decode(base64EncodedValue, Base64.DEFAULT);
+ } else {
+ return null;
+ }
}
}
diff --git a/opensrp-app/src/main/java/org/smartregister/service/UserService.java b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
index c49400f2b..2dfa00c50 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/UserService.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
@@ -24,6 +24,7 @@
import org.smartregister.domain.jsonmapping.util.TeamMember;
import org.smartregister.repository.AllSettings;
import org.smartregister.repository.AllSharedPreferences;
+import org.smartregister.security.PasswordHash;
import org.smartregister.security.SecurityHelper;
import org.smartregister.sync.SaveANMLocationTask;
import org.smartregister.sync.SaveANMTeamTask;
@@ -45,6 +46,7 @@
import java.security.interfaces.RSAPublicKey;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
@@ -216,16 +218,33 @@ public boolean isUserInValidGroup(final String userName, final char[] password)
// Check if everything OK for local login
if (keyStore != null && userName != null && password != null && !allSharedPreferences.fetchForceRemoteLogin(userName)) {
String username = userName.equalsIgnoreCase(allSharedPreferences.fetchRegisteredANM()) ? allSharedPreferences.fetchRegisteredANM() : userName;
+ byte[] storedHash = null;
+ byte[] passwordHash = null;
+ byte[] passwordSalt = null;
try {
KeyStore.PrivateKeyEntry privateKeyEntry = getUserKeyPair(username);
if (privateKeyEntry != null) {
- char[] groupId = getGroupId(username, privateKeyEntry);
- if (groupId != null) {
- return isValidGroupId(groupId);
+
+ // Compare stored password hash with provided password hash
+ storedHash = SecurityHelper.nullSafeBase64Decode(AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD, userName, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType()));
+
+ passwordSalt = SecurityHelper.nullSafeBase64Decode(AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD_SALT, userName, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType()));
+ passwordHash = SecurityHelper.hashPassword(password, passwordSalt);
+
+ if (storedHash != null && Arrays.equals(storedHash, passwordHash)) {
+ char[] groupId = getGroupId(username, privateKeyEntry);
+ if (groupId != null) {
+ return isValidGroupId(groupId);
+ }
}
}
} catch (Exception e) {
Timber.e(e);
+ } finally {
+ SecurityHelper.clearArray(password);
+ SecurityHelper.clearArray(passwordHash);
+ SecurityHelper.clearArray(passwordSalt);
+ SecurityHelper.clearArray(storedHash);
}
}
@@ -563,6 +582,7 @@ public Bundle saveUserGroup(String userName, char[] password, LoginResponseData
return null;
}
+ PasswordHash passwordHash = SecurityHelper.getPasswordHash(password);
SyncConfiguration syncConfiguration = CoreLibrary.getInstance().getSyncConfiguration();
if (syncConfiguration.getEncryptionParam() != null) {
SyncFilter syncFilter = syncConfiguration.getEncryptionParam();
@@ -571,7 +591,7 @@ public Bundle saveUserGroup(String userName, char[] password, LoginResponseData
} else if (SyncFilter.LOCATION.equals(syncFilter) || SyncFilter.LOCATION_ID.equals(syncFilter)) {
groupId = SecurityHelper.toBytes(getUserLocationId(userInfo).toCharArray());
} else if (SyncFilter.PROVIDER.equals(syncFilter)) {
- groupId = SecurityHelper.toBytes(new StringBuffer(username).append('-').append(password));
+ groupId = SecurityHelper.toBytes(new StringBuffer(username).append('-').append(passwordHash.getPassword()));
}
}
@@ -585,6 +605,9 @@ public Bundle saveUserGroup(String userName, char[] password, LoginResponseData
String encryptedGroupId = encryptString(privateKeyEntry, groupId);
bundle.putString(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, encryptedGroupId);
+ bundle.putString(AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD, Base64.encodeToString(passwordHash.getPassword(), Base64.DEFAULT));
+ bundle.putString(AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD_SALT, Base64.encodeToString(passwordHash.getSalt(), Base64.DEFAULT));
+
// Finally, save the pioneer user
if (allSharedPreferences.fetchPioneerUser() == null) {
allSharedPreferences.savePioneerUser(username);
diff --git a/opensrp-app/src/test/java/org/smartregister/security/SecurityHelperTest.java b/opensrp-app/src/test/java/org/smartregister/security/SecurityHelperTest.java
new file mode 100644
index 000000000..afc6b6998
--- /dev/null
+++ b/opensrp-app/src/test/java/org/smartregister/security/SecurityHelperTest.java
@@ -0,0 +1,165 @@
+package org.smartregister.security;
+
+import android.text.Editable;
+import android.util.Base64;
+
+import org.apache.commons.codec.CharEncoding;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.CharacterCodingException;
+import java.util.Arrays;
+
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+
+
+/**
+ * Created by ndegwamartin on 15/06/2020.
+ */
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({Base64.class, SecretKeyFactory.class})
+public class SecurityHelperTest {
+
+ @Mock
+ private Editable editable;
+
+ @Mock
+ private SecretKey secretKey;
+
+ @Mock
+ private PBEKeySpec keySpec;
+
+ private char[] TEST_PASSWORD;
+
+ private static final String TEST_DATA = "Some Random Test Data";
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ TEST_PASSWORD = "TEST_PASSWORD".toCharArray();
+ }
+
+
+ @Test
+ public void testReadValueClearsEditableAfterReadingValue() {
+
+ Mockito.doReturn(2).when(editable).length();
+
+ SecurityHelper.readValue(editable);
+
+ ArgumentCaptor lengthCaptor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor charsCaptor = ArgumentCaptor.forClass(char[].class);
+ ArgumentCaptor firstArgCaptor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor lastArgCaptor = ArgumentCaptor.forClass(Integer.class);
+
+ Mockito.verify(editable).getChars(firstArgCaptor.capture(), lengthCaptor.capture(), charsCaptor.capture(), lastArgCaptor.capture());
+ Mockito.verify(editable).clear();
+
+ Assert.assertEquals(2, lengthCaptor.getValue().intValue());
+ Assert.assertEquals(0, firstArgCaptor.getValue().intValue());
+ Assert.assertEquals(0, lastArgCaptor.getValue().intValue());
+
+ }
+
+ @Test
+ public void clearArray() {
+ byte[] sensitiveDataArray = SecurityHelper.toBytes(TEST_PASSWORD);
+ SecurityHelper.clearArray(sensitiveDataArray);
+
+ Assert.assertNotNull(sensitiveDataArray);
+
+ for (byte c : sensitiveDataArray) {
+ Assert.assertEquals((byte) 0, c);
+
+ }
+ }
+
+ @Test
+ public void testClearArrayOverwritesCharArrayValuesWithAsterisk() {
+ char[] sensitiveDataArray = TEST_PASSWORD;
+ SecurityHelper.clearArray(sensitiveDataArray);
+
+ Assert.assertNotNull(sensitiveDataArray);
+
+ for (char c : sensitiveDataArray) {
+ Assert.assertEquals('*', c);
+
+ }
+ }
+
+ @Test
+ public void testToBytes() throws CharacterCodingException {
+
+ StringBuffer stringBuffer = new StringBuffer();
+ stringBuffer.append(TEST_PASSWORD);
+
+ byte[] testPasswordBytes = SecurityHelper.toBytes(stringBuffer);
+ Assert.assertNotNull(testPasswordBytes);
+ Assert.assertEquals(TEST_PASSWORD.length + 1, testPasswordBytes.length);
+ }
+
+ @Test
+ public void testToCharsConvertsByteArrayToCorrectCharArray() {
+
+ byte[] testPasswordBytes = SecurityHelper.toBytes(TEST_PASSWORD);
+ Assert.assertNotNull(testPasswordBytes);
+
+ char[] testPasswordChars = SecurityHelper.toChars(testPasswordBytes);
+ Assert.assertNotNull(testPasswordChars);
+ Assert.assertTrue(Arrays.equals(TEST_PASSWORD, testPasswordChars));
+ }
+
+ @Test
+ public void nullSafeBase64DecodeDecodesValidBase64EncodedCorrectly() throws UnsupportedEncodingException {
+
+ String base64EncodedString = "U29tZSBSYW5kb20gVGVzdCBEYXRh";
+
+ PowerMockito.mockStatic(Base64.class);
+ PowerMockito.when(Base64.decode(base64EncodedString, Base64.DEFAULT)).thenReturn(TEST_DATA.getBytes(CharEncoding.UTF_8));
+
+ byte[] decoded = SecurityHelper.nullSafeBase64Decode(base64EncodedString);
+ Assert.assertNotNull(decoded);
+ Assert.assertTrue(Arrays.equals(SecurityHelper.toBytes(TEST_DATA.toCharArray()), decoded));
+
+ }
+
+ @Test
+ public void nullSafeBase64DecodeDoesNotThrowExceptionIfParameterIsNull() {
+ PowerMockito.mockStatic(Base64.class);
+ PowerMockito.when(Base64.decode(ArgumentMatchers.anyString(), ArgumentMatchers.eq(Base64.DEFAULT))).thenReturn(new byte[]{0, 1});
+
+ byte[] decoded = SecurityHelper.nullSafeBase64Decode(null);
+ Assert.assertNull(decoded);
+
+ }
+
+ @Test
+ public void testGetPsswordHashReturnsHashedPasswordObject() throws Exception {
+
+ PowerMockito.mockStatic(SecretKeyFactory.class);
+ SecretKeyFactory keyFactory = PowerMockito.mock(SecretKeyFactory.class);
+ PowerMockito.when(SecretKeyFactory.getInstance(ArgumentMatchers.anyString())).thenReturn(keyFactory);
+ PowerMockito.when(keyFactory.generateSecret(ArgumentMatchers.any(PBEKeySpec.class))).thenReturn(secretKey);
+ Mockito.doReturn(SecurityHelper.toBytes(TEST_PASSWORD)).when(secretKey).getEncoded();
+
+ PasswordHash passwordHash = SecurityHelper.getPasswordHash(TEST_PASSWORD);
+
+ Assert.assertNotNull(passwordHash);
+ Assert.assertNotNull(passwordHash.getPassword());
+ Assert.assertNotNull(passwordHash.getSalt());
+ }
+}
\ No newline at end of file
From e3ca2b498b15a2d37fee12e0a3d49c96874589a8 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Mon, 15 Jun 2020 14:25:45 +0300
Subject: [PATCH 38/70] Migrate Sample app to new core library
---
.../sample/application/SampleSyncConfiguration.java | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/sample/src/main/java/org/smartregister/sample/application/SampleSyncConfiguration.java b/sample/src/main/java/org/smartregister/sample/application/SampleSyncConfiguration.java
index 205ab4b80..75333cca1 100644
--- a/sample/src/main/java/org/smartregister/sample/application/SampleSyncConfiguration.java
+++ b/sample/src/main/java/org/smartregister/sample/application/SampleSyncConfiguration.java
@@ -5,6 +5,7 @@
import org.smartregister.SyncFilter;
import org.smartregister.repository.AllSharedPreferences;
import org.smartregister.sample.BuildConfig;
+import org.smartregister.view.activity.BaseLoginActivity;
import java.util.List;
@@ -72,4 +73,9 @@ public String getOauthClientId() {
public String getOauthClientSecret() {
return BuildConfig.OAUTH_CLIENT_SECRET;
}
+
+ @Override
+ public Class extends BaseLoginActivity> getAuthenticationActivity() {
+ return null;
+ }
}
From d03f352b3cf4c8a35ebeb85a972426b1eba46fe8 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Mon, 15 Jun 2020 14:47:43 +0300
Subject: [PATCH 39/70] Refactor local login - Refactor local login to run in
backgroud - Fixes UI freeze during offline Authentication
---
.../login/interactor/BaseLoginInteractor.java | 23 ++++++---
.../login/task/LocalLoginTask.java | 49 +++++++++++++++++++
.../login/task/RemoteLoginTask.java | 4 +-
.../smartregister/service/UserService.java | 4 +-
.../view/activity/BaseLoginActivity.java | 14 +++---
5 files changed, 76 insertions(+), 18 deletions(-)
create mode 100644 opensrp-app/src/main/java/org/smartregister/login/task/LocalLoginTask.java
diff --git a/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java b/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java
index a4c06ba20..cc4c8ac7b 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java
@@ -20,6 +20,7 @@
import org.smartregister.job.PullUniqueIdsServiceJob;
import org.smartregister.job.SyncSettingsServiceJob;
import org.smartregister.listener.OnCompleteClearDataCallback;
+import org.smartregister.login.task.LocalLoginTask;
import org.smartregister.login.task.RemoteLoginTask;
import org.smartregister.multitenant.ResetAppHelper;
import org.smartregister.repository.AllSharedPreferences;
@@ -83,18 +84,24 @@ public void loginWithLocalFlag(WeakReference view, boole
private void localLogin(WeakReference view, String userName, char[] password) {
getLoginView().enableLoginButton(true);
- boolean isAuthenticated = getUserService().isUserInValidGroup(userName, password);
- if (!isAuthenticated) {
- getLoginView().showErrorDialog(getApplicationContext().getResources().getString(R.string.unauthorized));
+ new LocalLoginTask(view.get(), userName, password, isAuthenticated -> {
- } else if (isAuthenticated && (!AllConstants.TIME_CHECK || TimeStatus.OK.equals(getUserService().validateStoredServerTimeZone()))) {
+ if (!isAuthenticated) {
- navigateToHomePage(userName, password);
+ getLoginView().showErrorDialog(getApplicationContext().getResources().getString(R.string.unauthorized));
+
+ } else if (isAuthenticated && (!AllConstants.TIME_CHECK || TimeStatus.OK.equals(getUserService().validateStoredServerTimeZone()))) {
+
+ navigateToHomePage(userName, password);
+
+ } else {
+ loginWithLocalFlag(view, false, userName, password);
+ }
+
+
+ }).execute();
- } else {
- loginWithLocalFlag(view, false, userName, password);
- }
}
private void navigateToHomePage(String userName, char[] password) {
diff --git a/opensrp-app/src/main/java/org/smartregister/login/task/LocalLoginTask.java b/opensrp-app/src/main/java/org/smartregister/login/task/LocalLoginTask.java
new file mode 100644
index 000000000..316db3c2b
--- /dev/null
+++ b/opensrp-app/src/main/java/org/smartregister/login/task/LocalLoginTask.java
@@ -0,0 +1,49 @@
+package org.smartregister.login.task;
+
+import android.os.AsyncTask;
+
+import org.smartregister.CoreLibrary;
+import org.smartregister.event.Listener;
+import org.smartregister.service.UserService;
+import org.smartregister.view.contract.BaseLoginContract;
+
+/**
+ * Created by ndegwamartin on 13/06/2020.
+ */
+public class LocalLoginTask extends AsyncTask {
+
+ private BaseLoginContract.View mLoginView;
+ private final String mUsername;
+ private final char[] mPassword;
+ private final Listener mAfterLoginCheck;
+
+ public LocalLoginTask(BaseLoginContract.View loginView, String username, char[] password, Listener afterLoginCheck) {
+ mLoginView = loginView;
+ mUsername = username;
+ mPassword = password;
+ mAfterLoginCheck = afterLoginCheck;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ mLoginView.showProgress(true);
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... voids) {
+ return getUserService().isUserInValidGroup(mUsername, mPassword);
+ }
+
+ @Override
+ protected void onPostExecute(final Boolean loginResponse) {
+ super.onPostExecute(loginResponse);
+
+ mLoginView.showProgress(false);
+ mAfterLoginCheck.onEvent(loginResponse);
+ }
+
+ private UserService getUserService() {
+ return CoreLibrary.getInstance().context().userService();
+ }
+}
diff --git a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
index f08eff894..2050ca72d 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
@@ -65,9 +65,9 @@ protected LoginResponse doInBackground(Void... params) {
AccountConfiguration accountConfiguration = CoreLibrary.getInstance().context().getHttpAgent().fetchOAuthConfiguration();
- boolean isKeyclockConfigured = accountConfiguration != null;
+ boolean isKeycloakConfigured = accountConfiguration != null;
- if (!isKeyclockConfigured) {
+ if (!isKeycloakConfigured) {
accountConfiguration = new AccountConfiguration();
accountConfiguration.setGrantTypesSupported(Arrays.asList(AccountHelper.OAUTH.GRANT_TYPE.PASSWORD));
accountConfiguration.setTokenEndpoint(CoreLibrary.getInstance().context().configuration().dristhiBaseURL() + AccountHelper.OAUTH.TOKEN_ENDPOINT);
diff --git a/opensrp-app/src/main/java/org/smartregister/service/UserService.java b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
index 2dfa00c50..2dfcf4edb 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/UserService.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
@@ -298,7 +298,7 @@ public boolean isUserInPioneerGroup(String userName) {
char[] userGroupId = getGroupId(userName);
char[] pioneerGroupId = getGroupId(pioneerUser);
- if (userGroupId != null && userGroupId.equals(pioneerGroupId)) {
+ if (userGroupId != null && Arrays.equals(pioneerGroupId, userGroupId)) {
return isValidGroupId(userGroupId);
}
}
@@ -570,12 +570,12 @@ public Bundle saveUserGroup(String userName, char[] password, LoginResponseData
String username = userInfo.user != null && StringUtils.isNotBlank(userInfo.user.getUsername()) ? userInfo.user.getUsername() : userName;
bundle.putString(AccountHelper.INTENT_KEY.ACCOUNT_NAME, username);
-
if (keyStore != null && username != null) {
byte[] groupId = null;
try {
+
KeyStore.PrivateKeyEntry privateKeyEntry = createUserKeyPair(username);
if (password == null) {
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java b/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java
index b21445e79..eddf5a256 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/activity/BaseLoginActivity.java
@@ -207,9 +207,7 @@ public void enableLoginButton(boolean isClickable) {
@Override
public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
if (actionId == R.integer.login || actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE) {
- String username = userNameEditText.getText().toString();
- char[] password = SecurityHelper.readValue(passwordEditText.getText());
- mLoginPresenter.attemptLogin(username, password);
+ attemptLogin();
return true;
}
return false;
@@ -218,12 +216,16 @@ public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent
@Override
public void onClick(View v) {
if (v.getId() == R.id.login_login_btn) {
- String username = userNameEditText.getText().toString();
- char[] password = SecurityHelper.readValue(passwordEditText.getText());
- mLoginPresenter.attemptLogin(username, password);
+ attemptLogin();
}
}
+ protected void attemptLogin() {
+ String username = userNameEditText.getText().toString().trim();
+ char[] password = SecurityHelper.readValue(passwordEditText.getText());
+ mLoginPresenter.attemptLogin(username, password);
+ }
+
@Override
public void setUsernameError(int resourceId) {
userNameEditText.setError(getString(resourceId));
From d5aee185c2b11a5d63b608b3617d40d2c2f16e61 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Mon, 15 Jun 2020 14:12:27 +0300
Subject: [PATCH 40/70] Support for local Multi-tenancy - Refactor offline
multi tenancy implementation for same Team Members - Adds support for No need
to remotely authenticate if all team members had logged in device previously
---
.../src/test/java/org/smartregister/service/HTTPAgentTest.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
index 87c46c40b..0d17bf656 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
@@ -175,6 +175,8 @@ public void setUp() {
Mockito.doReturn(syncConfiguration).when(coreLibrary).getSyncConfiguration();
Mockito.doReturn(1).when(syncConfiguration).getMaxAuthenticationRetries();
+ Mockito.doReturn(TEST_USERNAME).when(allSharedPreferences).fetchRegisteredANM();
+
PowerMockito.mockStatic(AccountHelper.class);
PowerMockito.when(AccountHelper.getOauthAccountByNameAndType(TEST_USERNAME, accountAuthenticatorXml.getAccountType())).thenReturn(account);
PowerMockito.when(AccountHelper.getOAuthToken(TEST_USERNAME, accountAuthenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER)).thenReturn(SAMPLE_TEST_TOKEN);
From 2172f350c579c3d4ab033e215f295b90ab41fcf5 Mon Sep 17 00:00:00 2001
From: qiarie
Date: Tue, 30 Jun 2020 18:50:12 +0300
Subject: [PATCH 41/70] Support for specifying key-value metadata map when
launching form - Add support for key-value pairs for start JSON form -
overload startFormActivity - Refactor LocationHelper to use List interface
instead of ArrayList - Expose AllowedLevels and DefaultLocationId fields on
LocationHelper
Co-authored-by: Martin Ndegwa
---
.../adapter/ServiceLocationsAdapter.java | 7 ++-
.../location/helper/LocationHelper.java | 56 +++++++++++--------
.../view/LocationPickerView.java | 5 +-
.../view/activity/BaseRegisterActivity.java | 4 +-
.../view/activity/SecuredActivity.java | 8 +++
.../view/contract/BaseRegisterContract.java | 1 +
6 files changed, 50 insertions(+), 31 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/adapter/ServiceLocationsAdapter.java b/opensrp-app/src/main/java/org/smartregister/adapter/ServiceLocationsAdapter.java
index e12bffc89..08ea4b06b 100644
--- a/opensrp-app/src/main/java/org/smartregister/adapter/ServiceLocationsAdapter.java
+++ b/opensrp-app/src/main/java/org/smartregister/adapter/ServiceLocationsAdapter.java
@@ -13,18 +13,19 @@
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
/**
* @author Jason Rogena - jrogena@ona.io
* @since 03/03/2017
*/
public class ServiceLocationsAdapter extends BaseAdapter {
- private final ArrayList locationNames;
+ private final List locationNames;
private final HashMap views;
private final Context context;
private String selectedLocation;
- public ServiceLocationsAdapter(Context context, ArrayList locationNames) {
+ public ServiceLocationsAdapter(Context context, List locationNames) {
this.context = context;
this.locationNames = locationNames == null ? new ArrayList<>() : locationNames;
this.views = new HashMap<>();
@@ -91,7 +92,7 @@ public String getLocationAt(int position) {
return locationNames.get(position);
}
- public ArrayList getLocationNames() {
+ public List getLocationNames() {
return locationNames;
}
}
diff --git a/opensrp-app/src/main/java/org/smartregister/location/helper/LocationHelper.java b/opensrp-app/src/main/java/org/smartregister/location/helper/LocationHelper.java
index d9fec7290..9222eeea1 100644
--- a/opensrp-app/src/main/java/org/smartregister/location/helper/LocationHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/location/helper/LocationHelper.java
@@ -40,18 +40,18 @@ public class LocationHelper {
private String childLocationId;
private String parentLocationId;
private List locationIds;
- private ArrayList locationNames;
+ private List locationNames;
private List locationNameHierarchy;
- private HashMap> childAndParentLocationIds;
+ private Map> childAndParentLocationIds;
private String defaultLocation;
- private ArrayList ALLOWED_LEVELS;
+ private List ALLOWED_LEVELS;
private String DEFAULT_LOCATION_LEVEL;
private List allCampaigns = new ArrayList<>();
private List allOperationalArea = new ArrayList<>();
private AllSharedPreferences allSharedPreferences = CoreLibrary.getInstance().context().allSharedPreferences();
- private LocationHelper(ArrayList allowedLevels, String defaultLocationLevel) {
+ private LocationHelper(List allowedLevels, String defaultLocationLevel) {
childAndParentLocationIds = new HashMap<>();
setParentAndChildLocationIds(getDefaultLocation());
@@ -59,7 +59,7 @@ private LocationHelper(ArrayList allowedLevels, String defaultLocationLe
this.DEFAULT_LOCATION_LEVEL = defaultLocationLevel;
}
- public static void init(ArrayList allowedLevels, String defaultLocationLevel) {
+ public static void init(List allowedLevels, String defaultLocationLevel) {
if (instance == null && StringUtils.isNotEmpty(defaultLocationLevel) && allowedLevels != null && allowedLevels.contains(defaultLocationLevel)) {
instance = new LocationHelper(allowedLevels, defaultLocationLevel);
}
@@ -80,15 +80,15 @@ public String locationIdsFromHierarchy() {
return null;
}
- public ArrayList locationNamesFromHierarchy(String defaultLocation) {
+ public List locationNamesFromHierarchy(String defaultLocation) {
if (Utils.isEmptyCollection(locationNames)) {
locationNames = locationsFromHierarchy(false, defaultLocation);
}
return locationNames;
}
- public ArrayList locationsFromHierarchy(boolean fetchLocationIds, String defaultLocation) {
- ArrayList locations = new ArrayList<>();
+ public List locationsFromHierarchy(boolean fetchLocationIds, String defaultLocation) {
+ List locations = new ArrayList<>();
try {
LinkedHashMap> map = map();
if (!Utils.isEmptyMap(map)) {
@@ -195,7 +195,7 @@ public List getOpenMrsLocationHierarchy(String locationId, boolean onlyA
LinkedHashMap> map = map();
if (!Utils.isEmptyMap(map)) {
for (Map.Entry> entry : map.entrySet()) {
- List curResult = getOpenMrsLocationHierarchy(locationId, entry.getValue(), new ArrayList(), onlyAllowedLevels);
+ List curResult = getOpenMrsLocationHierarchy(locationId, entry.getValue(), new ArrayList<>(), onlyAllowedLevels);
if (!Utils.isEmptyCollection(curResult)) {
response = curResult;
break;
@@ -211,7 +211,7 @@ public List getOpenMrsLocationHierarchy(String locationId, boolean onlyA
}
- public List generateDefaultLocationHierarchy(ArrayList allowedLevels) {
+ public List generateDefaultLocationHierarchy(List allowedLevels) {
if (Utils.isEmptyCollection(allowedLevels)) {
return new ArrayList<>();
}
@@ -222,7 +222,7 @@ public List generateDefaultLocationHierarchy(ArrayList allowedLe
LinkedHashMap> map = map();
if (!Utils.isEmptyMap(map)) {
for (Map.Entry> entry : map.entrySet()) {
- List curResult = getDefaultLocationHierarchy(defaultLocationUuid, entry.getValue(), new ArrayList(), allowedLevels);
+ List curResult = getDefaultLocationHierarchy(defaultLocationUuid, entry.getValue(), new ArrayList<>(), allowedLevels);
if (!Utils.isEmptyCollection(curResult)) {
return curResult;
}
@@ -234,12 +234,12 @@ public List generateDefaultLocationHierarchy(ArrayList allowedLe
return null;
}
- public List generateLocationHierarchyTree(boolean withOtherOption, ArrayList allowedLevels) {
+ public List generateLocationHierarchyTree(boolean withOtherOption, List allowedLevels) {
LinkedHashMap> map = map();
- return generateLocationHierarchyTree(withOtherOption,allowedLevels,map);
+ return generateLocationHierarchyTree(withOtherOption, allowedLevels, map);
}
- public List generateLocationHierarchyTree(boolean withOtherOption, ArrayList allowedLevels, Map> map) {
+ public List generateLocationHierarchyTree(boolean withOtherOption, List allowedLevels, Map> map) {
if (Utils.isEmptyCollection(allowedLevels)) {
return new ArrayList<>();
}
@@ -296,9 +296,9 @@ public String getOpenMrsReadableName(String name) {
}
// Private methods
- private ArrayList extractLocations(TreeNode rawLocationData, boolean fetchLocationIds, String defaultLocation) {
+ private List extractLocations(TreeNode rawLocationData, boolean fetchLocationIds, String defaultLocation) {
- ArrayList locationList = new ArrayList<>();
+ List locationList = new ArrayList<>();
try {
if (rawLocationData == null) {
return null;
@@ -335,7 +335,7 @@ private ArrayList extractLocations(TreeNode rawLocatio
LinkedHashMap> childMap = childMap(rawLocationData);
if (!Utils.isEmptyMap(childMap)) {
for (Map.Entry> childEntry : childMap.entrySet()) {
- ArrayList childLocations = extractLocations(childEntry.getValue(), fetchLocationIds, defaultLocation);
+ List childLocations = extractLocations(childEntry.getValue(), fetchLocationIds, defaultLocation);
if (!Utils.isEmptyCollection(childLocations)) {
locationList.addAll(childLocations);
}
@@ -413,9 +413,9 @@ private String getOpenMrsLocationName(String locationId, TreeNode getDefaultLocationHierarchy(String defaultLocationUuid, TreeNode openMrsLocationData, List parents, ArrayList allowedLevels) {
+ Location> openMrsLocationData, List parents, List allowedLevels) {
try {
- List heirachy = new ArrayList<>(parents);
+ List hierarchy = new ArrayList<>(parents);
if (openMrsLocationData == null) {
return null;
}
@@ -429,19 +429,19 @@ public List getDefaultLocationHierarchy(String defaultLocationUuid, Tree
if (!Utils.isEmptyCollection(levels)) {
for (String level : levels) {
if (allowedLevels.contains(level)) {
- heirachy.add(node.getName());
+ hierarchy.add(node.getName());
}
}
}
if (defaultLocationUuid.equals(node.getLocationId())) {
- return heirachy;
+ return hierarchy;
}
LinkedHashMap> childMap = childMap(openMrsLocationData);
if (!Utils.isEmptyMap(childMap)) {
for (Map.Entry> childEntry : childMap.entrySet()) {
- List curResult = getDefaultLocationHierarchy(defaultLocationUuid, childEntry.getValue(), heirachy, allowedLevels);
+ List curResult = getDefaultLocationHierarchy(defaultLocationUuid, childEntry.getValue(), hierarchy, allowedLevels);
if (!Utils.isEmptyCollection(curResult)) {
return curResult;
}
@@ -454,7 +454,7 @@ public List getDefaultLocationHierarchy(String defaultLocationUuid, Tree
}
private List getFormJsonData(TreeNode openMrsLocationData,
- ArrayList allowedLevels) {
+ List allowedLevels) {
List allLocationData = new ArrayList<>();
try {
FormLocation formLocation = new FormLocation();
@@ -536,7 +536,7 @@ protected boolean isLocationTagsShownEnabled() {
sortMap.put(formLocation.name, formLocation);
}
- ArrayList sortedKeys = new ArrayList<>(sortMap.keySet());
+ List sortedKeys = new ArrayList<>(sortMap.keySet());
Collections.sort(sortedKeys);
for (String curOptionName : sortedKeys) {
@@ -681,4 +681,12 @@ public LinkedHashMap> map() {
return null;
}
+ public List getAllowedLevels() {
+ return ALLOWED_LEVELS;
+ }
+
+ public String getDefaultLocationLevel() {
+ return DEFAULT_LOCATION_LEVEL;
+ }
+
}
\ No newline at end of file
diff --git a/opensrp-app/src/main/java/org/smartregister/view/LocationPickerView.java b/opensrp-app/src/main/java/org/smartregister/view/LocationPickerView.java
index 90bde89c4..dd177afbe 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/LocationPickerView.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/LocationPickerView.java
@@ -20,6 +20,7 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
/**
* @author Jason Rogena - jrogena@ona.io
@@ -94,8 +95,8 @@ public void setOnLocationChangeListener(final OnLocationChangeListener onLocatio
this.onLocationChangeListener = onLocationChangeListener;
}
- private ArrayList getLocations(String defaultLocation) {
- ArrayList locations = LocationHelper.getInstance().locationNamesFromHierarchy(defaultLocation);
+ private List getLocations(String defaultLocation) {
+ List locations = LocationHelper.getInstance().locationNamesFromHierarchy(defaultLocation);
if (locations.contains(defaultLocation)) {
locations.remove(defaultLocation);
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/BaseRegisterActivity.java b/opensrp-app/src/main/java/org/smartregister/view/activity/BaseRegisterActivity.java
index da8e505c7..41dc7be64 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/BaseRegisterActivity.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/activity/BaseRegisterActivity.java
@@ -33,6 +33,7 @@
import org.smartregister.view.viewpager.OpenSRPViewPager;
import java.util.List;
+import java.util.Map;
import timber.log.Timber;
@@ -198,8 +199,7 @@ protected void onInitialization() {//Implement Abstract Method
}
@Override
- public abstract void startFormActivity(String formName, String entityId, String metaData);
-
+ public abstract void startFormActivity(String formName, String entityId, Map metaData);
@Override
public abstract void startFormActivity(JSONObject form);
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/SecuredActivity.java b/opensrp-app/src/main/java/org/smartregister/view/activity/SecuredActivity.java
index 9c56aeab4..2f1af677a 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/SecuredActivity.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/activity/SecuredActivity.java
@@ -17,6 +17,7 @@
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
+import org.apache.commons.collections.MapUtils;
import org.smartregister.AllConstants;
import org.smartregister.Context;
import org.smartregister.CoreLibrary;
@@ -32,6 +33,7 @@
import org.smartregister.view.customcontrols.ProcessingInProgressSnackbar;
import java.util.Map;
+import java.util.Optional;
import timber.log.Timber;
@@ -164,6 +166,12 @@ public void startFormActivity(String formName, String entityId, String metaData)
launchForm(formName, entityId, metaData, FormActivity.class);
}
+ public void startFormActivity(String formName, String entityId, Map metaData) {
+ String metaDataString = MapUtils.getString(metaData, FIELD_OVERRIDES_PARAM, "");
+
+ launchForm(formName, entityId, metaDataString, FormActivity.class);
+ }
+
public void startMicroFormActivity(String formName, String entityId, String metaData) {
launchForm(formName, entityId, metaData, MicroFormActivity.class);
}
diff --git a/opensrp-app/src/main/java/org/smartregister/view/contract/BaseRegisterContract.java b/opensrp-app/src/main/java/org/smartregister/view/contract/BaseRegisterContract.java
index 16a235981..ab717e495 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/contract/BaseRegisterContract.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/contract/BaseRegisterContract.java
@@ -6,6 +6,7 @@
import org.smartregister.domain.FetchStatus;
import java.util.List;
+import java.util.Map;
/**
* Created by keyamn on 27/06/2018.
From 8db5c2d92cc1beff5c1bff8f2e7fd50caff02d7e Mon Sep 17 00:00:00 2001
From: qiarie
Date: Thu, 2 Jul 2020 16:44:20 +0300
Subject: [PATCH 42/70] Add unit tests - Add unit tests for startFormActivity
with map metadata params - Refactor tests to use List interface instead of
ArrayList class
Co-authored-by: Martin Ndegwa
---
.../adapter/ServiceLocationsAdapterTest.java | 3 +-
.../location/helper/LocationHelperTest.java | 2 +-
.../smartregister/service/HTTPAgentTest.java | 7 +++-
.../viewholder/OnClickFormLauncherTest.java | 39 +++++++++++++++++--
4 files changed, 44 insertions(+), 7 deletions(-)
diff --git a/opensrp-app/src/test/java/org/smartregister/adapter/ServiceLocationsAdapterTest.java b/opensrp-app/src/test/java/org/smartregister/adapter/ServiceLocationsAdapterTest.java
index d1b3ffd2e..709e448d2 100644
--- a/opensrp-app/src/test/java/org/smartregister/adapter/ServiceLocationsAdapterTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/adapter/ServiceLocationsAdapterTest.java
@@ -18,6 +18,7 @@
import org.smartregister.location.helper.LocationHelper;
import java.util.ArrayList;
+import java.util.List;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
@@ -79,7 +80,7 @@ public void testGetLocationAt() {
public void testGetLocationNames() {
ServiceLocationsAdapter adapter = getAdapterWithFakeClients();
- ArrayList names = adapter.getLocationNames();
+ List names = adapter.getLocationNames();
Assert.assertEquals(names.get(0), "test1");
}
diff --git a/opensrp-app/src/test/java/org/smartregister/location/helper/LocationHelperTest.java b/opensrp-app/src/test/java/org/smartregister/location/helper/LocationHelperTest.java
index f84a8e48a..5f6b1d131 100644
--- a/opensrp-app/src/test/java/org/smartregister/location/helper/LocationHelperTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/location/helper/LocationHelperTest.java
@@ -227,7 +227,7 @@ public void testLocationsFromHierarchyWhenAllowedLevelsContainsReveal() {
ReflectionHelpers.setField(spiedLocationHelper, "allCampaigns", campaignIds);
ReflectionHelpers.setField(spiedLocationHelper, "allOperationalArea", operationalArea);
- ArrayList locations = spiedLocationHelper.locationsFromHierarchy(true, null);
+ List locations = spiedLocationHelper.locationsFromHierarchy(true, null);
Mockito.verify(allSharedPreferences).savePreference(Mockito.eq(AllConstants.CAMPAIGNS), Mockito.eq("campaign-1,campaign-2,campaign-3"));
Mockito.verify(allSharedPreferences).savePreference(Mockito.eq(AllConstants.OPERATIONAL_AREAS), Mockito.eq("operational-area-1,operational-area-2,operational-area-3"));
diff --git a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
index 0d17bf656..85849f16a 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
@@ -143,7 +143,7 @@ public class HTTPAgentTest {
private static final String TEST_USER_INFO_ENDPOINT = "https://keycloak.my-server.com/auth/userinfo";
private static final String TEST_IMAGE_FILE_PATH = "file://usr/sdcard/dev0/data/org.smartregister.core/localimage.jpg";
protected static final String TEST_BASE_ENTITY_ID = "23ka2-3e23h2-n3g2i4-9q3b-yts4-20";
- protected static final String TEST_ANM_ID = "demo";
+ protected static final String TEST_ACCOUNT_TYPE = "org.smartregister.my-health-app";
private final String SAMPLE_TEST_TOKEN = "sample-test-token";
private final String SAMPLE_REFRESH_TOKEN = "sample-refresh-token";
@@ -169,11 +169,14 @@ public void setUp() {
PowerMockito.mockStatic(CoreLibrary.class);
PowerMockito.when(CoreLibrary.getInstance()).thenReturn(coreLibrary);
Mockito.doReturn(accountManager).when(coreLibrary).getAccountManager();
+ Mockito.doReturn(TEST_ACCOUNT_TYPE).when(accountAuthenticatorXml).getAccountType();
+ Mockito.doReturn(TEST_USERNAME).when(accountAuthenticatorXml).getAccountName();
Mockito.doReturn(accountAuthenticatorXml).when(coreLibrary).getAccountAuthenticatorXml();
Mockito.doReturn(accountManager).when(coreLibrary).getAccountManager();
Mockito.doReturn(syncConfiguration).when(coreLibrary).getSyncConfiguration();
Mockito.doReturn(1).when(syncConfiguration).getMaxAuthenticationRetries();
+ Mockito.doReturn(TEST_USERNAME).when(allSharedPreferences).fetchRegisteredANM();
Mockito.doReturn(TEST_USERNAME).when(allSharedPreferences).fetchRegisteredANM();
@@ -1104,7 +1107,7 @@ public void testHttpImagePostConfiguresConnectionRequestCorrectly() throws Excep
ProfileImage profileImage = new ProfileImage();
profileImage.setFilepath(TEST_IMAGE_FILE_PATH);
- profileImage.setAnmId(TEST_ANM_ID);
+ profileImage.setAnmId(TEST_USERNAME);
profileImage.setEntityID(TEST_BASE_ENTITY_ID);
profileImage.setContenttype("png");
profileImage.setFilecategory("coverpic");
diff --git a/opensrp-app/src/test/java/org/smartregister/view/viewholder/OnClickFormLauncherTest.java b/opensrp-app/src/test/java/org/smartregister/view/viewholder/OnClickFormLauncherTest.java
index d21f0b839..ff190011b 100644
--- a/opensrp-app/src/test/java/org/smartregister/view/viewholder/OnClickFormLauncherTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/view/viewholder/OnClickFormLauncherTest.java
@@ -11,6 +11,9 @@
import org.smartregister.BaseUnitTest;
import org.smartregister.view.activity.SecuredActivity;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* Created by ndegwamartin on 2020-04-14.
*/
@@ -46,15 +49,45 @@ public void testOnClickInvokesStartFormActivityWithCorrectParams() {
@Test
public void testOnClickInvokesStartFormActivityWithCorrectParamsNoMetadata() {
-
- Mockito.doNothing().when(activity).startFormActivity(TEST_FORM_NAME, TEST_BASE_ENTITY_ID, null);
+ Mockito.doNothing().when(activity).startFormActivity(TEST_FORM_NAME, TEST_BASE_ENTITY_ID, "");
OnClickFormLauncher onClickFormLauncher = new OnClickFormLauncher(activity, TEST_FORM_NAME, TEST_BASE_ENTITY_ID);
Assert.assertNotNull(onClickFormLauncher);
onClickFormLauncher.onClick(view);
- Mockito.verify(activity).startFormActivity(TEST_FORM_NAME, TEST_BASE_ENTITY_ID, null);
+ Mockito.verify(activity).startFormActivity(TEST_FORM_NAME, TEST_BASE_ENTITY_ID, (String) null);
+
+ }
+
+ @Test
+ public void testOnClickInvokesStartFormActivityWithCorrectMapMetadataParams() {
+ Map testMetadata = new HashMap<>();
+ testMetadata.put("first_name", "john");
+ testMetadata.put("last_name", "doe");
+
+ Mockito.doNothing().when(activity).startFormActivity(TEST_FORM_NAME, TEST_BASE_ENTITY_ID, testMetadata);
+
+ OnClickFormLauncher onClickFormLauncher = new OnClickFormLauncher(activity, TEST_FORM_NAME, TEST_BASE_ENTITY_ID, testMetadata.toString());
+ Assert.assertNotNull(onClickFormLauncher);
+
+ onClickFormLauncher.onClick(view);
+ Mockito.verify(activity, Mockito.times(1)).startFormActivity(TEST_FORM_NAME, TEST_BASE_ENTITY_ID, testMetadata.toString());
}
+
+ @Test
+ public void testOnClickInvokesStartFormActivityWithEmptyMapMetadataParams() {
+ Map testMetadata = new HashMap<>();
+
+ Mockito.doNothing().when(activity).startFormActivity(TEST_FORM_NAME, TEST_BASE_ENTITY_ID, testMetadata);
+
+ OnClickFormLauncher onClickFormLauncher = new OnClickFormLauncher(activity, TEST_FORM_NAME, TEST_BASE_ENTITY_ID, testMetadata.toString());
+ Assert.assertNotNull(onClickFormLauncher);
+
+ onClickFormLauncher.onClick(view);
+
+ Mockito.verify(activity, Mockito.times(1)).startFormActivity(TEST_FORM_NAME, TEST_BASE_ENTITY_ID, testMetadata.toString());
+ }
+
}
From 4b18d628a2b29f8599dbfd8ed08782b6cfa313ce Mon Sep 17 00:00:00 2001
From: qiarie
Date: Thu, 2 Jul 2020 17:25:36 +0300
Subject: [PATCH 43/70] Fix Codacy issues
---
.../src/main/java/org/smartregister/view/LocationPickerView.java | 1 -
.../java/org/smartregister/view/activity/SecuredActivity.java | 1 -
.../org/smartregister/view/contract/BaseRegisterContract.java | 1 -
3 files changed, 3 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/view/LocationPickerView.java b/opensrp-app/src/main/java/org/smartregister/view/LocationPickerView.java
index dd177afbe..ad17588d8 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/LocationPickerView.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/LocationPickerView.java
@@ -18,7 +18,6 @@
import org.smartregister.location.helper.LocationHelper;
import org.smartregister.view.customcontrols.CustomFontTextView;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/SecuredActivity.java b/opensrp-app/src/main/java/org/smartregister/view/activity/SecuredActivity.java
index 2f1af677a..7ae42b168 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/SecuredActivity.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/activity/SecuredActivity.java
@@ -33,7 +33,6 @@
import org.smartregister.view.customcontrols.ProcessingInProgressSnackbar;
import java.util.Map;
-import java.util.Optional;
import timber.log.Timber;
diff --git a/opensrp-app/src/main/java/org/smartregister/view/contract/BaseRegisterContract.java b/opensrp-app/src/main/java/org/smartregister/view/contract/BaseRegisterContract.java
index ab717e495..16a235981 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/contract/BaseRegisterContract.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/contract/BaseRegisterContract.java
@@ -6,7 +6,6 @@
import org.smartregister.domain.FetchStatus;
import java.util.List;
-import java.util.Map;
/**
* Created by keyamn on 27/06/2018.
From f3538c87f9fd708e700048c609ea63fd3350e82e Mon Sep 17 00:00:00 2001
From: qiarie
Date: Fri, 3 Jul 2020 12:25:19 +0300
Subject: [PATCH 44/70] Add unit tests for - getAllowedLevels -
getDefaultLocationLevel - getOpenMrsLocationHierarchy
---
.../location/helper/LocationHelperTest.java | 78 +++++++++++++++++--
1 file changed, 73 insertions(+), 5 deletions(-)
diff --git a/opensrp-app/src/test/java/org/smartregister/location/helper/LocationHelperTest.java b/opensrp-app/src/test/java/org/smartregister/location/helper/LocationHelperTest.java
index 5f6b1d131..4cb0d6a73 100644
--- a/opensrp-app/src/test/java/org/smartregister/location/helper/LocationHelperTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/location/helper/LocationHelperTest.java
@@ -1,5 +1,7 @@
package org.smartregister.location.helper;
+import android.util.Pair;
+
import net.sqlcipher.database.SQLiteDatabase;
import org.junit.After;
@@ -11,7 +13,6 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.powermock.reflect.Whitebox;
-import android.util.Pair;
import org.robolectric.util.ReflectionHelpers;
import org.smartregister.AllConstants;
import org.smartregister.BaseRobolectricUnitTest;
@@ -32,6 +33,7 @@
import java.util.List;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
public class LocationHelperTest extends BaseRobolectricUnitTest {
@@ -182,7 +184,6 @@ public void testLocationIdsFromHierarchy() {
assertEquals("718b2864-7d6a-44c8-b5b6-bb375f82654e,2c3a0ebd-f79d-4128-a6d3-5dfbffbd01c8", locationIds);
}
-
@Test
public void testLocationsFromHierarchyWhenAllowedLevelsContainsReveal() {
ReflectionHelpers.setStaticField(LocationHelper.class, "instance", null);
@@ -277,7 +278,6 @@ public void testGenerateLocationHierarchyTreeShouldReturnEmptyList() {
assertEquals(0, formLocationsList.size());
}
-
@Test
public void testGenerateLocationHierarchyTreeWithMapShouldReturnListWithOtherFormLocationOnly() {
locationHelper = Mockito.spy(locationHelper);
@@ -298,7 +298,6 @@ public void testGenerateLocationHierarchyTreeWithMapShouldReturnListWithOtherFor
assertEquals("", formLocation.level);
}
-
@Test
public void testGenerateLocationHierarchyTreeWithMapAndOtherOptionFalseShouldReturnEmptyList() {
locationHelper = Mockito.spy(locationHelper);
@@ -313,7 +312,6 @@ public void testGenerateLocationHierarchyTreeWithMapAndOtherOptionFalseShouldRet
assertEquals(0, formLocationsList.size());
}
-
@Test
public void testGenerateLocationHierarchyTreeWithMapShouldReturnListWithZambiaFormLocation() {
locationHelper = Mockito.spy(locationHelper);
@@ -424,4 +422,74 @@ public void testGetOpenMrsLocationName() {
.when(anmLocationController).get();
assertEquals("Jambula Girls School", locationHelper.getOpenMrsLocationName("982eb3f3-b7e3-450f-a38e-d067f2345212"));
}
+
+ public void testGetOpenMrsLocationHierarchyWithEmptyLocationIdShouldReturnEmptyList() {
+ locationHelper = Mockito.spy(locationHelper);
+ List hierarchy = locationHelper.getOpenMrsLocationHierarchy("", false);
+ assertEquals(0, hierarchy.size());
+ }
+
+ @Test
+ public void testGetOpenMrsLocationHierarchyWithLocationIdAndAllowedLevelsFlagShouldReturnListOfLocationNames() {
+ locationHelper = Mockito.spy(locationHelper);
+
+ ArrayList allowedLevels = new ArrayList<>();
+ allowedLevels.add("District");
+ allowedLevels.add("Facility");
+ allowedLevels.add("Commune");
+
+ AllSharedPreferences spiedAllSharedPreferences = Mockito.spy((AllSharedPreferences) ReflectionHelpers.getField(locationHelper, "allSharedPreferences"));
+ ReflectionHelpers.setField(locationHelper, "allSharedPreferences", spiedAllSharedPreferences);
+
+ Mockito.doReturn("Mabebe").when(spiedAllSharedPreferences).fetchRegisteredANM();
+ Mockito.doReturn("11").when(spiedAllSharedPreferences).fetchDefaultLocalityId("Mabebe");
+
+ ANMLocationController anmLocationController = Mockito.spy(CoreLibrary.getInstance().context().anmLocationController());
+ ReflectionHelpers.setField(CoreLibrary.getInstance().context(), "anmLocationController", anmLocationController);
+
+ String locationTree = "{\"locationsHierarchy\":{\"map\":{\"1\":{\"id\":\"1\",\"label\":\"Kiamb\",\"node\":{\"locationId\":\"1\",\"name\":\"Kiamb\",\"tags\":[\"District\"],\"voided\":false},\"children\":{\"11\":{\"id\":\"11\",\"label\":\"Mabebe\",\"node\":{\"locationId\":\"11\",\"name\":\"Mabebe\",\"parentLocation\":{\"locationId\":\"1\",\"voided\":false},\"tags\":[\"Commune\"],\"voided\":false},\"children\":{\"111\":{\"id\":\"111\",\"label\":\"Omshindi\",\"node\":{\"locationId\":\"111\",\"name\":\"Omshindi\",\"parentLocation\":{\"locationId\":\"11\",\"voided\":false},\"tags\":[\"District\"],\"voided\":false},\"parent\":\"11\"}},\"parent\":\"1\"}}}},\"parentChildren\":{\"1\":[\"11\"],\"11\":[\"111\"]}}}";
+ Mockito.doReturn(locationTree).when(anmLocationController).get();
+
+ List hierarchy = locationHelper.getOpenMrsLocationHierarchy("1", false);
+
+ assertEquals(1, hierarchy.size());
+ assertEquals(true, hierarchy.contains("Kiamb"));
+ }
+
+ @Test
+ public void testGetAllowedLevelsReturnsListOfLevelNames() {
+ ReflectionHelpers.setStaticField(LocationHelper.class, "instance", null);
+
+ ArrayList allowedLevels = new ArrayList<>();
+ allowedLevels.add("District");
+ allowedLevels.add("Commune");
+ allowedLevels.add("Facility");
+
+ LocationHelper.init(allowedLevels, "Facility");
+ locationHelper = LocationHelper.getInstance();
+
+ List actualAllowedLevels = locationHelper.getAllowedLevels();
+
+ assertNotNull(actualAllowedLevels);
+ assertEquals(3, actualAllowedLevels.size());
+ assertEquals(true, actualAllowedLevels.contains("Facility"));
+ }
+
+ @Test
+ public void testGetDefaultLocationLevelReturnsLocationLevelName() {
+ ReflectionHelpers.setStaticField(LocationHelper.class, "instance", null);
+
+ ArrayList allowedLevels = new ArrayList<>();
+ allowedLevels.add("District");
+ allowedLevels.add("Commune");
+ allowedLevels.add("Facility");
+
+ LocationHelper.init(allowedLevels, "Facility");
+ locationHelper = LocationHelper.getInstance();
+
+ String defaultLocationLevel = locationHelper.getDefaultLocationLevel();
+
+ assertEquals("Facility", defaultLocationLevel);
+ }
+
}
From 4e64270da54c0d6d492654f2390f2fcbaf7a176e Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Tue, 7 Jul 2020 10:52:37 +0300
Subject: [PATCH 45/70] Fix failing build - Fix failing tests
---
opensrp-app/build.gradle | 43 +++++++++++++------
.../security/SecurityHelperTest.java | 5 +--
.../view/activity/BaseLoginActivityTest.java | 6 +--
.../mock/BaseRegisterActivityMock.java | 11 +++++
4 files changed, 44 insertions(+), 21 deletions(-)
diff --git a/opensrp-app/build.gradle b/opensrp-app/build.gradle
index 1d43df1f3..82eb74095 100644
--- a/opensrp-app/build.gradle
+++ b/opensrp-app/build.gradle
@@ -206,22 +206,38 @@ dependencies {
implementation 'org.smartregister:opensrp-client-utils:0.0.2-SNAPSHOT'
implementation 'org.smartregister:opensrp-plan-evaluator:0.0.19-SNAPSHOT'
+ implementation('ch.acra:acra:4.5.0') {
+ exclude group: 'org.json', module: 'json'
+ }
- implementation 'xerces:xercesImpl:2.12.0'
- implementation('org.smartregister:opensrp-client-native-form:1.9.2-SNAPSHOT@aar') {
- transitive = true
- exclude group: 'id.zelory', module: 'compressor'
+ implementation 'com.github.ybq:Android-SpinKit:1.2.0'
+ implementation 'com.mcxiaoke.volley:library:1.0.19'
+
+ implementation fileTree(include: ['*.jar'], dir: 'libs')
+ annotationProcessor fileTree(include: ['butterknife*.jar'], dir: 'libs')
+
+ implementation 'com.cloudant:cloudant-http:2.7.0'
+ implementation 'com.android.support:recyclerview-v7:28.0.0'
+
+ implementation('com.android.support:design:28.0.0') {
exclude group: 'com.android.support', module: 'recyclerview-v7'
- exclude group: 'com.android.support', module: 'appcompat-v7'
- exclude group: 'com.android.support', module: 'cardview-v7'
- exclude group: 'com.android.support', module: 'support-media-compat'
+ }
+
+ implementation 'com.evernote:android-job:1.2.6'
+ implementation group: 'commons-validator', name: 'commons-validator', version: '1.6'
+ implementation 'de.hdodenhof:circleimageview:2.2.0'
+
+ implementation('org.smartregister:android-p2p-sync:0.3.6-SNAPSHOT') {
exclude group: 'com.android.support', module: 'support-v4'
- exclude group: 'com.android.support', module: 'design'
- exclude group: 'org.yaml', module: 'snakeyaml'
- exclude group: 'io.ona.rdt-capture', module: 'lib'
- exclude group: 'com.github.johnkil.print', module: 'print'
- exclude group: 'com.android.support', module: 'multidex'
+ exclude group: 'com.android.support', module: 'appcompat-v7'
+ exclude group: 'android.arch.core', module: 'runtime'
}
+
+ implementation 'org.smartregister:opensrp-client-utils:0.0.2-SNAPSHOT'
+
+ implementation 'org.smartregister:opensrp-plan-evaluator:0.0.13-SNAPSHOT'
+
+ implementation 'xerces:xercesImpl:2.12.0'
}
dependencies {
@@ -229,7 +245,6 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
androidTestImplementation 'junit:junit:4.12'
- testImplementation 'com.android.support:multidex:1.0.0'
testImplementation group: 'com.google.android', name: 'android-test', version: '4.1.1.4'
testImplementation 'org.apache.maven:maven-ant-tasks:2.1.3'
testImplementation 'org.mockito:mockito-core:1.9.5'
@@ -296,4 +311,4 @@ coveralls {
sourceDirs = ["$project.projectDir/src/main/java"]
}
-apply from: '../maven.gradle'
+apply from: '../maven.gradle'
\ No newline at end of file
diff --git a/opensrp-app/src/test/java/org/smartregister/security/SecurityHelperTest.java b/opensrp-app/src/test/java/org/smartregister/security/SecurityHelperTest.java
index afc6b6998..d627c22c3 100644
--- a/opensrp-app/src/test/java/org/smartregister/security/SecurityHelperTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/security/SecurityHelperTest.java
@@ -40,9 +40,6 @@ public class SecurityHelperTest {
@Mock
private SecretKey secretKey;
- @Mock
- private PBEKeySpec keySpec;
-
private char[] TEST_PASSWORD;
private static final String TEST_DATA = "Some Random Test Data";
@@ -162,4 +159,4 @@ public void testGetPsswordHashReturnsHashedPasswordObject() throws Exception {
Assert.assertNotNull(passwordHash.getPassword());
Assert.assertNotNull(passwordHash.getSalt());
}
-}
\ No newline at end of file
+}
diff --git a/opensrp-app/src/test/java/org/smartregister/view/activity/BaseLoginActivityTest.java b/opensrp-app/src/test/java/org/smartregister/view/activity/BaseLoginActivityTest.java
index 1b11d6c3b..27ddf9eb8 100644
--- a/opensrp-app/src/test/java/org/smartregister/view/activity/BaseLoginActivityTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/view/activity/BaseLoginActivityTest.java
@@ -99,7 +99,7 @@ public void onClickLoginShouldCallAttemptLogin() {
Mockito.doReturn(R.id.login_login_btn).when(view).getId();
baseLoginActivity.onClick(view);
- Mockito.verify(baseLoginActivity.mLoginPresenter).attemptLogin(Mockito.anyString(), Mockito.anyString());
+ Mockito.verify(baseLoginActivity.mLoginPresenter).attemptLogin(Mockito.anyString(), Mockito.any(char[].class));
}
@@ -109,7 +109,7 @@ public void onEditorActionShouldShouldCallAttemptLoginAndReturnTrueWhenActionIsD
Mockito.doReturn(R.id.login_login_btn).when(view).getId();
Assert.assertTrue(baseLoginActivity.onEditorAction(null, EditorInfo.IME_ACTION_DONE, null));
- Mockito.verify(baseLoginActivity.mLoginPresenter).attemptLogin(Mockito.anyString(), Mockito.anyString());
+ Mockito.verify(baseLoginActivity.mLoginPresenter).attemptLogin(Mockito.anyString(), Mockito.any(char[].class));
}
@Test
@@ -166,7 +166,7 @@ public void setPasswordErrorShouldCallSetErrorAndShowErrorDialog() {
@Test
public void isAppVersionAllowedShouldReturnSyncUtilsValue() throws PackageManager.NameNotFoundException {
- SyncUtils syncUtils = Mockito.spy((SyncUtils) ReflectionHelpers.getField(baseLoginActivity, "syncUtils"));
+ SyncUtils syncUtils = Mockito.spy((SyncUtils) ReflectionHelpers.getField(baseLoginActivity, "syncUtils"));
ReflectionHelpers.setField(baseLoginActivity, "syncUtils", syncUtils);
Mockito.doReturn(false).when(syncUtils).isAppVersionAllowed();
diff --git a/opensrp-app/src/test/java/org/smartregister/view/activity/mock/BaseRegisterActivityMock.java b/opensrp-app/src/test/java/org/smartregister/view/activity/mock/BaseRegisterActivityMock.java
index 994b25921..4f9ec2e17 100644
--- a/opensrp-app/src/test/java/org/smartregister/view/activity/mock/BaseRegisterActivityMock.java
+++ b/opensrp-app/src/test/java/org/smartregister/view/activity/mock/BaseRegisterActivityMock.java
@@ -14,6 +14,7 @@
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import static org.mockito.Mockito.mock;
@@ -43,6 +44,11 @@ protected Fragment[] getOtherFragments() {
return new Fragment[0];
}
+ @Override
+ public void startFormActivity(String formName, String entityId, Map metaData) {
+ // mock do nothing
+ }
+
@Override
public void startFormActivity(String formName, String entityId, String metaData) {
//mock do nothing
@@ -68,6 +74,11 @@ public void startRegistration() {
//mock do nothing
}
+ @Override
+ public void onPointerCaptureChanged(boolean hasCapture) {
+
+ }
+
public static class BaseRegisterFragmentMock extends BaseRegisterFragment {
From 4ecdc70c5e12a39bd933e083c1563c4721c7f6be Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Tue, 7 Jul 2020 15:04:26 +0300
Subject: [PATCH 46/70] Codacy clean up
---
.../view/activity/mock/BaseRegisterActivityMock.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/opensrp-app/src/test/java/org/smartregister/view/activity/mock/BaseRegisterActivityMock.java b/opensrp-app/src/test/java/org/smartregister/view/activity/mock/BaseRegisterActivityMock.java
index 4f9ec2e17..d61a59876 100644
--- a/opensrp-app/src/test/java/org/smartregister/view/activity/mock/BaseRegisterActivityMock.java
+++ b/opensrp-app/src/test/java/org/smartregister/view/activity/mock/BaseRegisterActivityMock.java
@@ -76,7 +76,7 @@ public void startRegistration() {
@Override
public void onPointerCaptureChanged(boolean hasCapture) {
-
+ //mock do nothing
}
public static class BaseRegisterFragmentMock extends BaseRegisterFragment {
From e7c511bce976962b8e0d01c601a12e87ed8cb0c2 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Tue, 7 Jul 2020 17:24:23 +0300
Subject: [PATCH 47/70] Fix failing build - Fix unit tests - Downgrade travis
config to Android API 27
---
.travis.yml | 6 +++---
.../src/test/java/org/smartregister/BaseUnitTest.java | 2 +-
.../smartregister/view/activity/DrishtiApplicationTest.java | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 64aefe376..d3dada302 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -35,9 +35,9 @@ before_install:
- mkdir -p $HOME/.android && touch $HOME/.android/repositories.cfg
install:
# accept licenses for all available packages that have not already been accepted
- - yes | sdkmanager "platforms;android-28"
+ - yes | sdkmanager "platforms;android-27"
- yes | sdkmanager --licenses >/dev/null
- - yes | sdkmanager "platforms;android-28"
+ - yes | sdkmanager "platforms;android-27"
before_script:
- echo "Travis branch is $TRAVIS_BRANCH"
- echo "Travis branch is in pull request $TRAVIS_PULL+REQUEST"
@@ -70,4 +70,4 @@ deploy:
provider: script
script: ./gradlew :opensrp-app:uploadArchives -PmavenLocal=false
on:
- tags: true
\ No newline at end of file
+ tags: true
diff --git a/opensrp-app/src/test/java/org/smartregister/BaseUnitTest.java b/opensrp-app/src/test/java/org/smartregister/BaseUnitTest.java
index b5487690e..c1039a923 100644
--- a/opensrp-app/src/test/java/org/smartregister/BaseUnitTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/BaseUnitTest.java
@@ -21,7 +21,7 @@
*/
@RunWith(RobolectricTestRunner.class)
-@Config(application = TestApplication.class, shadows = {FontTextViewShadow.class, ShadowDrawableResourcesImpl.class}, sdk = Build.VERSION_CODES.P)
+@Config(application = TestApplication.class, shadows = {FontTextViewShadow.class, ShadowDrawableResourcesImpl.class}, sdk = Build.VERSION_CODES.O_MR1)
@PowerMockIgnore({"org.mockito.*",
"org.robolectric.*",
"android.*",
diff --git a/opensrp-app/src/test/java/org/smartregister/view/activity/DrishtiApplicationTest.java b/opensrp-app/src/test/java/org/smartregister/view/activity/DrishtiApplicationTest.java
index 84dc2faa8..df7313f5a 100644
--- a/opensrp-app/src/test/java/org/smartregister/view/activity/DrishtiApplicationTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/view/activity/DrishtiApplicationTest.java
@@ -71,7 +71,7 @@ public void getRepository() {
@Test
public void getPassword() {
String username = "anm";
- String password = "pwd";
+ char[] password = "pwd".toCharArray();
drishtiApplication.onCreate();
From f2632c19442feb4f203859bd97fa52da4a3e2a0b Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Wed, 15 Jul 2020 21:20:45 +0300
Subject: [PATCH 48/70] Fix bug :bug: - Fix bug with verify authorization
causing premature return when processing
---
.../src/main/java/org/smartregister/service/HTTPAgent.java | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
index 1f8569af7..16f1d1f38 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
@@ -834,6 +834,11 @@ public boolean verifyAuthorization() {
String userInfoUrl = allSharedPreferences.getPreferences().getString(AccountHelper.CONFIGURATION_CONSTANTS.USERINFO_ENDPOINT_URL, "");
+ if (StringUtils.isBlank(userInfoUrl)) {
+
+ return verifyAuthorizationLegacy();
+ }
+
HttpURLConnection urlConnection = null;
InputStream inputStream = null;
@@ -879,7 +884,9 @@ public boolean verifyAuthorization() {
}
} catch (IOException e) {
+
Timber.e(e);
+
} finally {
closeConnection(urlConnection);
From e2ae820c03a52d4b063b0d001a70853a7da0ab95 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Thu, 16 Jul 2020 18:10:11 +0300
Subject: [PATCH 49/70] Authentication with oauth2 credentials - Add
conditional for sending client id and secret depending on whether keycloak is
configured or not - Fix HTTPAgent unit tests
---
.../smartregister/account/AccountHelper.java | 1 +
.../login/task/RemoteLoginTask.java | 11 ++--
.../org/smartregister/service/HTTPAgent.java | 14 +++-
.../smartregister/service/HTTPAgentTest.java | 64 +++++++++++++++++--
4 files changed, 79 insertions(+), 11 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
index 9ebf9cb37..71ec29ea6 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
@@ -21,6 +21,7 @@ public class AccountHelper {
public static final class CONFIGURATION_CONSTANTS {
+ public static final String IS_KEYCLOAK_CONFIGURED = "is_keycloack_configured";
public final static String TOKEN_ENDPOINT_URL = "token_endpoint_url";
public final static String AUTHORIZATION_ENDPOINT_URL = "authorization_endpoint_url";
public final static String ISSUER_ENDPOINT_URL = "issuer_endpoint_url";
diff --git a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
index 2050ca72d..005f7f817 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
@@ -67,6 +67,12 @@ protected LoginResponse doInBackground(Void... params) {
boolean isKeycloakConfigured = accountConfiguration != null;
+ //Persist config resources
+ SharedPreferences.Editor sharedPrefEditor = CoreLibrary.getInstance().context().allSharedPreferences().getPreferences().edit();
+ sharedPrefEditor.putBoolean(AccountHelper.CONFIGURATION_CONSTANTS.IS_KEYCLOAK_CONFIGURED, isKeycloakConfigured);
+ sharedPrefEditor.apply();
+
+
if (!isKeycloakConfigured) {
accountConfiguration = new AccountConfiguration();
accountConfiguration.setGrantTypesSupported(Arrays.asList(AccountHelper.OAUTH.GRANT_TYPE.PASSWORD));
@@ -80,9 +86,6 @@ protected LoginResponse doInBackground(Void... params) {
if (!accountConfiguration.getGrantTypesSupported().contains(AccountHelper.OAUTH.GRANT_TYPE.PASSWORD))
throw new AccountsException("OAuth configuration DOES NOT support the Password Grant Type");
- //Persist config resources
- SharedPreferences.Editor sharedPrefEditor = CoreLibrary.getInstance().context().allSharedPreferences().getPreferences().edit();
-
sharedPrefEditor.putString(AccountHelper.CONFIGURATION_CONSTANTS.TOKEN_ENDPOINT_URL, accountConfiguration.getTokenEndpoint());
sharedPrefEditor.putString(AccountHelper.CONFIGURATION_CONSTANTS.AUTHORIZATION_ENDPOINT_URL, accountConfiguration.getAuthorizationEndpoint());
sharedPrefEditor.putString(AccountHelper.CONFIGURATION_CONSTANTS.ISSUER_ENDPOINT_URL, accountConfiguration.getIssuerEndpoint());
@@ -120,7 +123,7 @@ protected LoginResponse doInBackground(Void... params) {
SyncSettingsServiceHelper syncSettingsServiceHelper = new SyncSettingsServiceHelper(getOpenSRPContext().configuration().dristhiBaseURL(), getOpenSRPContext().getHttpAgent());
try {
- JSONArray settings = syncSettingsServiceHelper.pullSettingsFromServer(Utils.getFilterValue(loginResponse, CoreLibrary.getInstance().getSyncConfiguration().getSyncFilterParam()), response.getAccessToken());
+ JSONArray settings = pullSetting(syncSettingsServiceHelper, loginResponse, response.getAccessToken());
JSONObject prefSettingsData = new JSONObject();
prefSettingsData.put(AllConstants.PREF_KEY.SETTINGS, settings);
diff --git a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
index 16f1d1f38..40c43700e 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/HTTPAgent.java
@@ -579,8 +579,17 @@ public AccountResponse oauth2authenticateCore(StringBuffer requestParamBuffer, S
final String base64Auth = BaseEncoding.base64().encode(new StringBuffer(clientId).append(':').append(clientSecret).toString().getBytes(CharEncoding.UTF_8));
requestParamBuffer.append("&grant_type=").append(grantType);
- requestParamBuffer.append("&client_id=").append(clientId);
- requestParamBuffer.append("&client_secret=").append(clientSecret);
+
+ if (allSharedPreferences.getPreferences().getBoolean(AccountHelper.CONFIGURATION_CONSTANTS.IS_KEYCLOAK_CONFIGURED, false)) {
+
+ requestParamBuffer.append("&client_id=").append(clientId);
+ requestParamBuffer.append("&client_secret=").append(clientSecret);
+
+ } else {
+
+ urlConnection.setRequestProperty(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BASIC + " " + base64Auth);
+
+ }
byte[] postData = requestParamBuffer.toString().getBytes(CharEncoding.UTF_8);
int postDataLength = postData.length;
@@ -592,7 +601,6 @@ public AccountResponse oauth2authenticateCore(StringBuffer requestParamBuffer, S
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("charset", "utf-8");
urlConnection.setRequestProperty("Content-Length", Integer.toString(postDataLength));
- urlConnection.setRequestProperty(AllConstants.HTTP_REQUEST_HEADERS.AUTHORIZATION, AllConstants.HTTP_REQUEST_AUTH_TOKEN_TYPE.BASIC + " " + base64Auth);
urlConnection.setUseCaches(false);
outputStream = urlConnection.getOutputStream();
diff --git a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
index 85849f16a..a20c03758 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/HTTPAgentTest.java
@@ -178,7 +178,8 @@ public void setUp() {
Mockito.doReturn(1).when(syncConfiguration).getMaxAuthenticationRetries();
Mockito.doReturn(TEST_USERNAME).when(allSharedPreferences).fetchRegisteredANM();
- Mockito.doReturn(TEST_USERNAME).when(allSharedPreferences).fetchRegisteredANM();
+ Mockito.doReturn(sharedPreferences).when(allSharedPreferences).getPreferences();
+ Mockito.doReturn(true).when(sharedPreferences).getBoolean(AccountHelper.CONFIGURATION_CONSTANTS.IS_KEYCLOAK_CONFIGURED, false);
PowerMockito.mockStatic(AccountHelper.class);
PowerMockito.when(AccountHelper.getOauthAccountByNameAndType(TEST_USERNAME, accountAuthenticatorXml.getAccountType())).thenReturn(account);
@@ -286,7 +287,9 @@ public void testPostWithJsonResponse() {
@Test
- public void testOauth2authenticateCreatesUrlConnectionWithCorrectParameters() throws Exception {
+ public void testOauth2authenticateCreatesUrlConnectionWithCorrectParametersWhenKeycloakNotConfigured() throws Exception {
+
+ Mockito.doReturn(false).when(sharedPreferences).getBoolean(AccountHelper.CONFIGURATION_CONSTANTS.IS_KEYCLOAK_CONFIGURED, false);
URL url = PowerMockito.mock(URL.class);
Assert.assertNotNull(url);
@@ -320,9 +323,11 @@ public void testOauth2authenticateCreatesUrlConnectionWithCorrectParameters() th
Mockito.verify(httpURLConnection).setConnectTimeout(60000);
Mockito.verify(httpURLConnection).setReadTimeout(60000);
- String requestParams = "&grant_type=" + AccountHelper.OAUTH.GRANT_TYPE.PASSWORD + "&username=" + TEST_USERNAME + "&password=" + String.valueOf(TEST_PASSWORD) + "&client_id=" + TEST_CLIENT_ID + "&client_secret=" + TEST_CLIENT_SECRET;
+ String requestParams = "&grant_type=" + AccountHelper.OAUTH.GRANT_TYPE.PASSWORD + "&username=" + TEST_USERNAME + "&password=" + String.valueOf(TEST_PASSWORD);
+ ArgumentCaptor paramLengthCaptor = ArgumentCaptor.forClass(Integer.class);
+ Mockito.verify(httpURLConnection).setFixedLengthStreamingMode(paramLengthCaptor.capture());
+ Assert.assertEquals((Integer) requestParams.getBytes(CharEncoding.UTF_8).length, paramLengthCaptor.getValue());
- Mockito.verify(httpURLConnection).setFixedLengthStreamingMode(requestParams.getBytes(CharEncoding.UTF_8).length);
Mockito.verify(httpURLConnection).setDoOutput(true);
Mockito.verify(httpURLConnection).setInstanceFollowRedirects(false);
Mockito.verify(httpURLConnection).setRequestMethod("POST");
@@ -336,6 +341,57 @@ public void testOauth2authenticateCreatesUrlConnectionWithCorrectParameters() th
}
+ @Test
+ public void testOauth2authenticateCreatesUrlConnectionWithCorrectParametersWhenKeycloakConfigured() throws Exception {
+
+ URL url = PowerMockito.mock(URL.class);
+ Assert.assertNotNull(url);
+
+ HTTPAgent httpAgentSpy = Mockito.spy(httpAgent);
+
+ Mockito.doReturn(httpURLConnection).when(httpAgentSpy).getHttpURLConnection(TEST_TOKEN_ENDPOINT);
+ Mockito.doReturn(TEST_CLIENT_ID).when(syncConfiguration).getOauthClientId();
+ Mockito.doReturn(TEST_CLIENT_SECRET).when(syncConfiguration).getOauthClientSecret();
+
+ Mockito.doReturn(outputStream).when(httpURLConnection).getOutputStream();
+ Mockito.doReturn(inputStream).when(httpURLConnection).getInputStream();
+ Mockito.doReturn(HttpURLConnection.HTTP_OK).when(httpURLConnection).getResponseCode();
+
+ PowerMockito.mockStatic(IOUtils.class);
+ PowerMockito.when(IOUtils.toString(inputStream)).thenReturn(TOKEN_REQUEST_SERVER_RESPONSE);
+
+
+ AccountResponse accountResponse = httpAgentSpy.oauth2authenticate(TEST_USERNAME, TEST_PASSWORD, AccountHelper.OAUTH.GRANT_TYPE.PASSWORD, TEST_TOKEN_ENDPOINT);
+
+ Assert.assertNotNull(accountResponse);
+ Assert.assertEquals(200, accountResponse.getStatus());
+ Assert.assertEquals("1r9A8zi5E3r@Zz", accountResponse.getAccessToken());
+ Assert.assertEquals("bearer", accountResponse.getTokenType());
+ Assert.assertEquals("text_token", accountResponse.getRefreshToken());
+ Assert.assertEquals(Integer.valueOf("3600"), accountResponse.getExpiresIn());
+ Assert.assertEquals(Integer.valueOf("36000"), accountResponse.getRefreshExpiresIn());
+ Assert.assertEquals("read write trust", accountResponse.getScope());
+
+
+ Mockito.verify(httpURLConnection).setConnectTimeout(60000);
+ Mockito.verify(httpURLConnection).setReadTimeout(60000);
+
+ String requestParams = "&grant_type=" + AccountHelper.OAUTH.GRANT_TYPE.PASSWORD + "&username=" + TEST_USERNAME + "&password=" + String.valueOf(TEST_PASSWORD) + "&client_id=" + TEST_CLIENT_ID + "&client_secret=" + TEST_CLIENT_SECRET;
+ ArgumentCaptor paramLengthCaptor = ArgumentCaptor.forClass(Integer.class);
+ Mockito.verify(httpURLConnection).setFixedLengthStreamingMode(paramLengthCaptor.capture());
+ Assert.assertEquals((Integer) requestParams.getBytes(CharEncoding.UTF_8).length, paramLengthCaptor.getValue());
+
+ Mockito.verify(httpURLConnection).setDoOutput(true);
+ Mockito.verify(httpURLConnection).setInstanceFollowRedirects(false);
+ Mockito.verify(httpURLConnection).setRequestMethod("POST");
+ Mockito.verify(httpURLConnection).setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+ Mockito.verify(httpURLConnection).setRequestProperty("charset", "utf-8");
+ Mockito.verify(httpURLConnection).setRequestProperty(ArgumentMatchers.eq("Content-Length"), ArgumentMatchers.anyString());
+ Mockito.verify(httpURLConnection).setUseCaches(false);
+ Mockito.verify(httpURLConnection).setInstanceFollowRedirects(false);
+
+ }
+
@Test
public void testOauth2authenticateReturnsCorrectResponseForBadRequest() throws Exception {
From 366ecc9ecba2376fa3510604733d7e9ed54cbd10 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Thu, 16 Jul 2020 19:13:12 +0300
Subject: [PATCH 50/70] Login Activity clean up - Remove deprecated
LoginActivity class and dependencies
---
.../view/activity/LoginActivityTest.java | 68 ----
.../view/activity/LoginActivity.java | 293 ------------------
.../view/fragment/SecuredFragment.java | 10 +-
.../view/activity/LoginActivityTest.java | 141 ---------
.../LoginActivityWithRemoteLoginTest.java | 145 ---------
.../view/activity/mock/LoginActivityMock.java | 42 ---
.../view/fragment/SecuredFragmentTest.java | 17 -
7 files changed, 3 insertions(+), 713 deletions(-)
delete mode 100644 opensrp-app/src/androidTest/java/org/smartregister/view/activity/LoginActivityTest.java
delete mode 100644 opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java
delete mode 100644 opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityTest.java
delete mode 100644 opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityWithRemoteLoginTest.java
delete mode 100644 opensrp-app/src/test/java/org/smartregister/view/activity/mock/LoginActivityMock.java
diff --git a/opensrp-app/src/androidTest/java/org/smartregister/view/activity/LoginActivityTest.java b/opensrp-app/src/androidTest/java/org/smartregister/view/activity/LoginActivityTest.java
deleted file mode 100644
index 71d4263ff..000000000
--- a/opensrp-app/src/androidTest/java/org/smartregister/view/activity/LoginActivityTest.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package org.smartregister.view.activity;
-
-import static org.smartregister.util.Wait.waitForFilteringToFinish;
-
-public class LoginActivityTest {
-// private DrishtiSolo solo;
-// private FakeUserService userService;
-
- public LoginActivityTest() {
-// super(LoginActivity.class);
- }
-
- // @Override
- protected void setUp() throws Exception {
-// FakeDrishtiService drishtiService = new FakeDrishtiService(String.valueOf(new Date().getTime() - 1));
-// userService = new FakeUserService();
-//
-// setupService(drishtiService, userService, -1000).updateApplicationContext(getActivity());
-// CoreLibrary.getInstance().context().session().setPassword(null);
-//
-// solo = new DrishtiSolo(getInstrumentation(), getActivity());
- }
-
- public void ignoreTestShouldAllowLoginWithoutCheckingRemoteLoginWhenLocalLoginSucceeds() throws Exception {
-// userService.setupFor("user", "password", true, true, UNKNOWN_RESPONSE);
-//
-// solo.assertCanLogin("user", "password");
-//
-// userService.assertOrderOfCalls("local", "login");
- }
-
- public void ignoreTestShouldTryRemoteLoginWhenThereIsNoRegisteredUser() throws Exception {
-// userService.setupFor("user", "password", false, false, SUCCESS);
-//
-// solo.assertCanLogin("user", "password");
-//
-// userService.assertOrderOfCalls("remote", "login");
- }
-
- public void ignoreTestShouldFailToLoginWhenBothLoginMethodsFail() throws Exception {
-// userService.setupFor("user", "password", false, false, UNKNOWN_RESPONSE);
-//
-// solo.assertCannotLogin("user", "password");
-//
-// userService.assertOrderOfCalls("remote");
- }
-
- public void ignoreTestShouldNotTryRemoteLoginWhenRegisteredUserExistsEvenIfLocalLoginFails() throws Exception {
-// userService.setupFor("user", "password", true, false, SUCCESS);
-//
-// solo.assertCannotLogin("user", "password");
-// userService.assertOrderOfCalls("local");
- }
-
- public void ignoreTestShouldNotTryLocalLoginWhenRegisteredUserDoesNotExist() throws Exception {
-// userService.setupFor("user", "password", false, true, SUCCESS);
-//
-// solo.assertCanLogin("user", "password");
-// userService.assertOrderOfCalls("remote", "login");
- }
-
- // @Override
- public void tearDown() throws Exception {
- waitForFilteringToFinish();
-// waitForProgressBarToGoAway(getActivity());
-// solo.finishOpenedActivities();
- }
-}
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java b/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java
deleted file mode 100644
index ddee8f121..000000000
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/LoginActivity.java
+++ /dev/null
@@ -1,293 +0,0 @@
-package org.smartregister.view.activity;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.TextView;
-
-import org.smartregister.Context;
-import org.smartregister.CoreLibrary;
-import org.smartregister.R;
-import org.smartregister.domain.LoginResponse;
-import org.smartregister.domain.jsonmapping.LoginResponseData;
-import org.smartregister.event.Listener;
-import org.smartregister.security.SecurityHelper;
-import org.smartregister.sync.DrishtiSyncScheduler;
-import org.smartregister.util.LangUtils;
-import org.smartregister.util.SyncUtils;
-import org.smartregister.util.Utils;
-import org.smartregister.view.BackgroundAction;
-import org.smartregister.view.LockingBackgroundTask;
-import org.smartregister.view.ProgressIndicator;
-
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-import timber.log.Timber;
-
-import static android.view.inputmethod.InputMethodManager.HIDE_NOT_ALWAYS;
-import static org.smartregister.domain.LoginResponse.SUCCESS;
-import static org.smartregister.util.Log.logError;
-import static org.smartregister.util.Log.logVerbose;
-
-public class LoginActivity extends Activity implements View.OnClickListener {
- private Context context;
- private EditText userNameEditText;
- private EditText passwordEditText;
- private ProgressDialog progressDialog;
- private Button loginButton;
- private SyncUtils syncUtils;
-
- @Override
- protected void attachBaseContext(android.content.Context base) {
- // get language from prefs
- String lang = LangUtils.getLanguage(base.getApplicationContext());
- super.attachBaseContext(LangUtils.setAppLocale(base, lang));
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- logVerbose("Initializing ...");
- setContentView(R.layout.login);
-
- context = CoreLibrary.getInstance().context().updateApplicationContext(this.getApplicationContext());
- syncUtils = new SyncUtils(this);
- initializeLoginFields();
- initializeBuildDetails();
- setDoneActionHandlerOnPasswordField();
- initializeProgressDialog();
-
-
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- menu.add("Settings");
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getTitle().toString().equalsIgnoreCase("Settings")) {
- startActivity(new Intent(this, SettingsActivity.class));
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- private void initializeBuildDetails() {
- TextView buildDetailsTextView = findViewById(R.id.login_build);
- try {
- buildDetailsTextView.setText(String.format(getString(R.string.app_version), Utils.getVersion(this
- .getApplicationContext()), Utils.getBuildDate(true)));
- } catch (Exception e) {
- logError("Error fetching build details: " + e);
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- if (!context.IsUserLoggedOut()) {
- goToHome();
- }
-
- fillUserIfExists();
- }
-
- public void login(final View view) {
- hideKeyboard();
- view.setClickable(false);
-
- final String userName = userNameEditText.getText().toString();
- final char[] password = SecurityHelper.readValue(passwordEditText.getText());
-
- if (context.userService().hasARegisteredUser()) {
- localLogin(view, userName, password);
- } else {
- remoteLogin(view, userName, password);
- }
- }
-
- private void initializeLoginFields() {
- userNameEditText = findViewById(R.id.login_userNameText);
- passwordEditText = findViewById(R.id.login_passwordText);
- loginButton = findViewById(R.id.login_loginButton);
- loginButton.setOnClickListener(this);
- }
-
- private void setDoneActionHandlerOnPasswordField() {
- passwordEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
- @Override
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- if (actionId == EditorInfo.IME_ACTION_DONE) {
- login(loginButton);
- }
- return false;
- }
- });
- }
-
- private void initializeProgressDialog() {
- progressDialog = new ProgressDialog(this);
- progressDialog.setCancelable(false);
- progressDialog.setTitle(getString(R.string.loggin_in_dialog_title));
- progressDialog.setMessage(getString(R.string.loggin_in_dialog_message));
- }
-
- private void localLogin(View view, String userName, char[] password) {
- try {
- if (!syncUtils.isAppVersionAllowed()) {
- showErrorDialog(getString(R.string.outdated_app));
- return;
- }
-
- if (context.userService().isValidLocalLogin(userName, password)) {
- localLoginWith(userName, password);
- } else {
- showErrorDialog(getString(R.string.login_failed_dialog_message));
- view.setClickable(true);
- }
- } catch (PackageManager.NameNotFoundException e) {
- Timber.e(e);
- }
- }
-
-
- private void remoteLogin(final View view, final String userName, final char[] password) {
-
- try {
- if (!syncUtils.isAppVersionAllowed()) {
- showErrorDialog(getString(R.string.outdated_app));
- return;
- }
-
- if (!context.allSharedPreferences().fetchBaseURL("").isEmpty()) {
-
- tryRemoteLogin(userName, password, new Listener() {
- public void onEvent(LoginResponse loginResponse) {
- if (loginResponse == SUCCESS) {
- remoteLoginWith(userName, password, loginResponse.payload());
- } else {
- if (loginResponse == null) {
- showErrorDialog("Login failed. Unknown reason. Try Again");
- } else {
- showErrorDialog(loginResponse.message());
- }
- view.setClickable(true);
- }
- }
- });
-
- } else {
- view.setClickable(true);
- showErrorDialog("OpenSRP Base URL is missing, Please add it in Settings and try again");
- }
- } catch (PackageManager.NameNotFoundException e) {
- Timber.e(e);
- }
- }
-
- private void showErrorDialog(String message) {
- AlertDialog dialog = new AlertDialog.Builder(this)
- .setTitle(getString(R.string.login_failed_dialog_title)).setMessage(message)
- .setPositiveButton("OK", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- }
- }).create();
- dialog.show();
- }
-
- private void tryRemoteLogin(final String userName, final char[] password, final
- Listener afterLoginCheck) {
- LockingBackgroundTask task = new LockingBackgroundTask(new ProgressIndicator() {
- @Override
- public void setVisible() {
- progressDialog.show();
- }
-
- @Override
- public void setInvisible() {
- progressDialog.dismiss();
- }
- });
-
- task.doActionInBackground(new BackgroundAction() {
- public LoginResponse actionToDoInBackgroundThread() {
- return context.userService().isValidRemoteLogin(userName, password);
- }
-
- public void postExecuteInUIThread(LoginResponse result) {
- afterLoginCheck.onEvent(result);
- }
- });
- }
-
- private void fillUserIfExists() {
- if (context.userService().hasARegisteredUser()) {
- userNameEditText.setText(context.allSharedPreferences().fetchRegisteredANM());
- userNameEditText.setEnabled(false);
- }
- }
-
- private void hideKeyboard() {
- InputMethodManager inputManager = (InputMethodManager) getSystemService(
- INPUT_METHOD_SERVICE);
- inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), HIDE_NOT_ALWAYS);
- }
-
- private void localLoginWith(String userName, char[] password) {
- context.userService().localLogin(userName, password);
- goToHome();
- DrishtiSyncScheduler.startOnlyIfConnectedToNetwork(getApplicationContext());
- }
-
- private void remoteLoginWith(String userName, char[] password, LoginResponseData userInfo) {
- context.userService().processLoginResponseDataForUser(userName, password, userInfo);
- goToHome();
- DrishtiSyncScheduler.startOnlyIfConnectedToNetwork(getApplicationContext());
- }
-
- protected void goToHome() {
- startActivity(new Intent(this, NativeHomeActivity.class));
- finish();
- }
-
- private String getVersion() throws PackageManager.NameNotFoundException {
- PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
- return packageInfo.versionName;
- }
-
- private String getBuildDate() throws PackageManager.NameNotFoundException, IOException {
- ApplicationInfo applicationInfo = getPackageManager()
- .getApplicationInfo(getPackageName(), 0);
- ZipFile zf = new ZipFile(applicationInfo.sourceDir);
- ZipEntry ze = zf.getEntry("classes.dex");
- return new SimpleDateFormat("dd MMM yyyy", Utils.getDefaultLocale())
- .format(new java.util.Date(ze.getTime()));
- }
-
- @Override
- public void onClick(View view) {
- login(view);
- }
-}
diff --git a/opensrp-app/src/main/java/org/smartregister/view/fragment/SecuredFragment.java b/opensrp-app/src/main/java/org/smartregister/view/fragment/SecuredFragment.java
index 3eadfe51f..3ad371a7b 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/fragment/SecuredFragment.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/fragment/SecuredFragment.java
@@ -18,7 +18,6 @@
import org.smartregister.util.Utils;
import org.smartregister.view.activity.DrishtiApplication;
import org.smartregister.view.activity.FormActivity;
-import org.smartregister.view.activity.LoginActivity;
import org.smartregister.view.activity.MicroFormActivity;
import org.smartregister.view.activity.SecuredActivity;
import org.smartregister.view.controller.ANMController;
@@ -44,11 +43,9 @@ public abstract class SecuredFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- logoutListener = new Listener() {
- public void onEvent(Boolean data) {
- if (getActivity() != null && !getActivity().isFinishing()) {
- getActivity().finish();
- }
+ logoutListener = data -> {
+ if (getActivity() != null && !getActivity().isFinishing()) {
+ getActivity().finish();
}
};
Event.ON_LOGOUT.addListener(logoutListener);
@@ -104,7 +101,6 @@ public boolean onOptionsItemSelected(MenuItem item) {
public void logoutUser() {
context().userService().logout();
- startActivity(new Intent(getActivity(), LoginActivity.class));
}
protected abstract void onCreation();
diff --git a/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityTest.java b/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityTest.java
deleted file mode 100644
index 624f3de9d..000000000
--- a/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityTest.java
+++ /dev/null
@@ -1,141 +0,0 @@
-package org.smartregister.view.activity;
-
-import android.app.AlarmManager;
-import android.content.Intent;
-import android.os.IBinder;
-import android.util.Log;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.Button;
-import android.widget.EditText;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.reflect.Whitebox;
-import org.robolectric.Robolectric;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.android.controller.ActivityController;
-import org.robolectric.annotation.Config;
-import org.smartregister.BaseUnitTest;
-import org.smartregister.CoreLibrary;
-import org.smartregister.R;
-import org.smartregister.customshadows.AndroidTreeViewShadow;
-import org.smartregister.customshadows.FontTextViewShadow;
-import org.smartregister.repository.AllSharedPreferences;
-import org.smartregister.service.UserService;
-import org.smartregister.shadows.AlarmManagerShadow;
-import org.smartregister.shadows.PendingIntentShadow;
-import org.smartregister.shadows.ShadowContext;
-import org.smartregister.sync.DrishtiSyncScheduler;
-import org.smartregister.view.activity.mock.LoginActivityMock;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.isNull;
-
-/**
- * Created by kaderchowdhury on 11/11/17.
- */
-@PowerMockIgnore({"javax.xml.*", "org.xml.sax.*", "org.w3c.dom.*", "org.springframework.context.*", "org.apache.log4j.*"})
-@PrepareForTest({CoreLibrary.class})
-@Config(shadows = {ShadowContext.class, FontTextViewShadow.class, AndroidTreeViewShadow.class, PendingIntentShadow.class, AlarmManagerShadow.class})
-public class LoginActivityTest extends BaseUnitTest {
-
- private ActivityController controller;
-
- @Mock
- private android.content.Context applicationContext;
-
- @Mock
- private AllSharedPreferences allSharedPreferences;
-
- @Mock
- private InputMethodManager inputManager;
-
- @Mock
- private AlarmManager alarmManager;
-
- @InjectMocks
- private LoginActivityMock activity;
-
- @Mock
- private org.smartregister.Context context_;
-
- @Mock
- private UserService userService;
-
- @Before
- public void setUp() throws Exception {
- org.mockito.MockitoAnnotations.initMocks(this);
- CoreLibrary.init(context_);
- LoginActivityMock.inputManager = inputManager;
-// ShadowSystemClock shadowClock = new ShadowSystemClock();
-// shadowClock.setCurrentTimeMillis(142436987);
-
- DrishtiSyncScheduler.setReceiverClass(LoginActivityMock.class);
-
-// Context context = CoreLibrary.getInstance().context().updateApplicationContext(activity.getApplicationContext());
-// this.context_ = context;
- Mockito.doReturn(applicationContext).when(context_).applicationContext();
- Mockito.doReturn(context_).when(context_).updateApplicationContext(any(android.content.Context.class));
- Mockito.doReturn(userService).when(context_).userService();
- Mockito.doReturn(alarmManager).when(applicationContext).getSystemService(android.content.Context.ALARM_SERVICE);
- Mockito.doReturn("admin").when(allSharedPreferences).fetchRegisteredANM();
- Mockito.doReturn(true).when(inputManager).hideSoftInputFromWindow(isNull(IBinder.class), anyInt());
- Intent intent = new Intent(RuntimeEnvironment.application, LoginActivityMock.class);
- controller = Robolectric.buildActivity(LoginActivityMock.class, intent);
- controller.create()
- .start()
- .visible()
- .resume();
- activity = controller.get();
-
- Whitebox.setInternalState(activity, "context", context_);
- }
-
-
- @Test
- public void assertActivityNotNull() {
- Assert.assertNotNull(activity);
- }
-
- @Test
- public void localLoginTest() {
- Mockito.doReturn(true).when(userService).hasARegisteredUser();
- Mockito.doReturn(true).when(userService).isValidLocalLogin(anyString(), any(char[].class));
- Mockito.doReturn(allSharedPreferences).when(context_).allSharedPreferences();
-
- EditText username = activity.findViewById(R.id.login_userNameText);
- EditText password = activity.findViewById(R.id.login_passwordText);
-
- username.setText("admin");
- password.setText("password");
-
- Button login_button = activity.findViewById(R.id.login_loginButton);
- login_button.performClick();
-
- Mockito.verify(userService, Mockito.atLeastOnce()).localLogin(anyString(), any(char[].class));
-
- }
-
- @After
- public void destroyController() {
- try {
- activity.finish();
- controller.pause().stop().destroy(); //destroy controller if we can
-
- } catch (Exception e) {
- Log.e(getClass().getCanonicalName(), e.getMessage());
- }
-
- System.gc();
- }
-
-}
diff --git a/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityWithRemoteLoginTest.java b/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityWithRemoteLoginTest.java
deleted file mode 100644
index 3dd2d2359..000000000
--- a/opensrp-app/src/test/java/org/smartregister/view/activity/LoginActivityWithRemoteLoginTest.java
+++ /dev/null
@@ -1,145 +0,0 @@
-package org.smartregister.view.activity;
-
-import android.app.AlarmManager;
-import android.content.Intent;
-import android.os.IBinder;
-import android.util.Log;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.Button;
-import android.widget.EditText;
-
-import junit.framework.Assert;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.reflect.Whitebox;
-import org.robolectric.Robolectric;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.android.controller.ActivityController;
-import org.robolectric.annotation.Config;
-import org.smartregister.BaseUnitTest;
-import org.smartregister.Context;
-import org.smartregister.CoreLibrary;
-import org.smartregister.R;
-import org.smartregister.customshadows.AndroidTreeViewShadow;
-import org.smartregister.customshadows.FontTextViewShadow;
-import org.smartregister.domain.LoginResponse;
-import org.smartregister.domain.jsonmapping.LoginResponseData;
-import org.smartregister.repository.AllSharedPreferences;
-import org.smartregister.service.UserService;
-import org.smartregister.shadows.AlarmManagerShadow;
-import org.smartregister.shadows.MyShadowAsyncTask;
-import org.smartregister.shadows.PendingIntentShadow;
-import org.smartregister.shadows.ShadowContext;
-import org.smartregister.sync.DrishtiSyncScheduler;
-import org.smartregister.view.activity.mock.LoginActivityMock;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.when;
-
-/**
- * Created by kaderchowdhury on 11/11/17.
- */
-@PowerMockIgnore({"javax.xml.*", "org.xml.sax.*", "org.w3c.dom.*", "org.springframework.context.*", "org.apache.log4j.*"})
-@PrepareForTest({CoreLibrary.class})
-@Config(shadows = {ShadowContext.class, FontTextViewShadow.class, AndroidTreeViewShadow.class, PendingIntentShadow.class, AlarmManagerShadow.class, MyShadowAsyncTask.class})
-public class LoginActivityWithRemoteLoginTest extends BaseUnitTest {
-
- private ActivityController controller;
-
- @Mock
- private android.content.Context applicationContext;
-
- @Mock
- private AllSharedPreferences allSharedPreferences;
-
- @Mock
- private InputMethodManager inputManager;
-
- @Mock
- private AlarmManager alarmManager;
-
- @InjectMocks
- private LoginActivityMock activity;
-
- @Mock
- private Context context_;
-
- @Mock
- private UserService userService;
-
- @Before
- public void setUp() throws Exception {
- org.mockito.MockitoAnnotations.initMocks(this);
- CoreLibrary.init(context_);
- LoginActivityMock.inputManager = inputManager;
-// ShadowSystemClock shadowClock = new ShadowSystemClock();
-// shadowClock.setCurrentTimeMillis(142436987);
-
- DrishtiSyncScheduler.setReceiverClass(LoginActivityMock.class);
-
-// Context context = CoreLibrary.getInstance().context().updateApplicationContext(activity.getApplicationContext());
-// this.context_ = context;
- when(context_.applicationContext()).thenReturn(applicationContext);
- when(context_.updateApplicationContext(any(android.content.Context.class))).thenReturn(context_);
- when(context_.userService()).thenReturn(userService);
- when(applicationContext.getSystemService(android.content.Context.ALARM_SERVICE)).thenReturn(alarmManager);
- when(allSharedPreferences.fetchRegisteredANM()).thenReturn("admin");
- when(inputManager.hideSoftInputFromWindow(isNull(IBinder.class), anyInt())).thenReturn(true);
- Intent intent = new Intent(RuntimeEnvironment.application, LoginActivityMock.class);
- controller = Robolectric.buildActivity(LoginActivityMock.class, intent);
- controller.create()
- .start()
- .resume()
- .visible();
- activity = controller.get();
-
- Whitebox.setInternalState(activity, "context", context_);
- }
-
-
- @Test
- public void assertActivityNotNull() {
- Assert.assertNotNull(activity);
- }
-
- private void destroyController() {
- try {
- activity.finish();
- controller.pause().stop().destroy(); //destroy controller if we can
-
- } catch (Exception e) {
- Log.e(getClass().getCanonicalName(), e.getMessage());
- }
-
- System.gc();
- }
-
- @Test
- public void remoteLoginTest() {
- when(userService.hasARegisteredUser()).thenReturn(false);
- when(userService.isValidLocalLogin(anyString(), any(char[].class))).thenReturn(true);
- when(userService.isValidRemoteLogin(anyString(),any(char[].class))).thenReturn(LoginResponse.SUCCESS.withPayload(new LoginResponseData()));
- when(context_.allSharedPreferences()).thenReturn(allSharedPreferences);
- when(allSharedPreferences.fetchBaseURL(anyString())).thenReturn("base url");
- EditText username = activity.findViewById(R.id.login_userNameText);
- EditText password = activity.findViewById(R.id.login_passwordText);
- username.setText("admin");
- password.setText("password");
- Button login_button = activity.findViewById(R.id.login_loginButton);
- login_button.performClick();
- Mockito.verify(userService, Mockito.atLeastOnce()).processLoginResponseDataForUser(anyString(),any(char[].class), any(LoginResponseData.class));
- destroyController();
-
-
- }
-
-}
diff --git a/opensrp-app/src/test/java/org/smartregister/view/activity/mock/LoginActivityMock.java b/opensrp-app/src/test/java/org/smartregister/view/activity/mock/LoginActivityMock.java
deleted file mode 100644
index dd990871c..000000000
--- a/opensrp-app/src/test/java/org/smartregister/view/activity/mock/LoginActivityMock.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package org.smartregister.view.activity.mock;
-
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-
-import org.smartregister.Context;
-import org.smartregister.R;
-import org.smartregister.view.activity.LoginActivity;
-
-/**
- * Created by Raihan Ahmed on 11/11/17.
- */
-
-public class LoginActivityMock extends LoginActivity {
-
- static Context mockactivitycontext;
-
- public static InputMethodManager inputManager;
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- setTheme(R.style.AppTheme); //we need this here
- super.onCreate(savedInstanceState);
- }
-
- @Override
- public Object getSystemService(String name) {
- if (name.equalsIgnoreCase(INPUT_METHOD_SERVICE)) {
- return inputManager;
- } else {
- return super.getSystemService(name);
- }
- }
-
- @Nullable
- @Override
- public View getCurrentFocus() {
- return findViewById(R.id.login_userNameText);
- }
-}
diff --git a/opensrp-app/src/test/java/org/smartregister/view/fragment/SecuredFragmentTest.java b/opensrp-app/src/test/java/org/smartregister/view/fragment/SecuredFragmentTest.java
index c82c20e09..94b1bf7c0 100644
--- a/opensrp-app/src/test/java/org/smartregister/view/fragment/SecuredFragmentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/view/fragment/SecuredFragmentTest.java
@@ -177,23 +177,6 @@ public void assertLogoutUserInvokesUserserviceLogoutMethod() {
}
- @Test
- public void assertLogoutUserNavigatesToLoginPage() {
-
- Mockito.doNothing().when(securedFragment).startActivity(ArgumentMatchers.any(Intent.class));
- Mockito.doNothing().when(userService).logout();
-
- securedFragment.logoutUser();
-
- Mockito.verify(securedFragment).startActivity(intentArgumentCaptor.capture());
- Intent navigationIntent = intentArgumentCaptor.getValue();
-
- Assert.assertNotNull(navigationIntent);
-
- Assert.assertEquals("org.smartregister.view.activity.LoginActivity", navigationIntent.getComponent().getClassName());
-
- }
-
@Test
public void assertStartFormActivityInvokesNavigationToFormActivity() {
From adf7b83575b714003bebdeb176285230d4bfb1fe Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Fri, 17 Jul 2020 12:31:05 +0300
Subject: [PATCH 51/70] Code clean up - Clean up Authentication logic classes -
Fix failing tests
---
.../smartregister/account/AccountAuthenticator.java | 9 +++++----
.../smartregister/login/task/RemoteLoginTask.java | 12 +++++++-----
.../service/ImageUploadSyncService.java | 3 ++-
.../sync/helper/SyncSettingsServiceHelper.java | 9 +++++++--
.../sync/helper/SyncSettingsServiceHelperTest.java | 6 +++++-
.../view/fragment/BaseRegisterFragmentTest.java | 2 --
6 files changed, 26 insertions(+), 15 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java b/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
index 92d3a9389..2b9887f16 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
@@ -50,17 +50,18 @@ public Bundle getAuthToken(AccountAuthenticatorResponse response, Account accoun
AccountManager accountManager = CoreLibrary.getInstance().getAccountManager();
String authToken = accountManager.peekAuthToken(account, authTokenType);
- String refreshToken = "";
+ String refreshToken;
Timber.d("peekAuthToken " + authToken);
if (TextUtils.isEmpty(authToken)) {
- final String password = accountManager.getPassword(account);
- if (password != null) {
+ refreshToken = accountManager.getPassword(account);
+
+ if (refreshToken != null) {
try {
Timber.d("Authenticate with saved credentials");
- AccountResponse accountResponse = CoreLibrary.getInstance().context().getHttpAgent().oauth2authenticateRefreshToken(password);
+ AccountResponse accountResponse = CoreLibrary.getInstance().context().getHttpAgent().oauth2authenticateRefreshToken(refreshToken);
if (accountResponse.getStatus() == HttpStatus.SC_OK) {
authToken = accountResponse.getAccessToken();
refreshToken = accountResponse.getRefreshToken();
diff --git a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
index 005f7f817..d68a56ebe 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
@@ -63,12 +63,12 @@ protected LoginResponse doInBackground(Void... params) {
LoginResponse loginResponse;
try {
- AccountConfiguration accountConfiguration = CoreLibrary.getInstance().context().getHttpAgent().fetchOAuthConfiguration();
+ AccountConfiguration accountConfiguration = getOpenSRPContext().getHttpAgent().fetchOAuthConfiguration();
boolean isKeycloakConfigured = accountConfiguration != null;
//Persist config resources
- SharedPreferences.Editor sharedPrefEditor = CoreLibrary.getInstance().context().allSharedPreferences().getPreferences().edit();
+ SharedPreferences.Editor sharedPrefEditor = getOpenSRPContext().allSharedPreferences().getPreferences().edit();
sharedPrefEditor.putBoolean(AccountHelper.CONFIGURATION_CONSTANTS.IS_KEYCLOAK_CONFIGURED, isKeycloakConfigured);
sharedPrefEditor.apply();
@@ -76,7 +76,7 @@ protected LoginResponse doInBackground(Void... params) {
if (!isKeycloakConfigured) {
accountConfiguration = new AccountConfiguration();
accountConfiguration.setGrantTypesSupported(Arrays.asList(AccountHelper.OAUTH.GRANT_TYPE.PASSWORD));
- accountConfiguration.setTokenEndpoint(CoreLibrary.getInstance().context().configuration().dristhiBaseURL() + AccountHelper.OAUTH.TOKEN_ENDPOINT);
+ accountConfiguration.setTokenEndpoint(getOpenSRPContext().configuration().dristhiBaseURL() + AccountHelper.OAUTH.TOKEN_ENDPOINT);
accountConfiguration.setAuthorizationEndpoint("");
accountConfiguration.setIssuerEndpoint("");
}
@@ -92,7 +92,7 @@ protected LoginResponse doInBackground(Void... params) {
sharedPrefEditor.putString(AccountHelper.CONFIGURATION_CONSTANTS.USERINFO_ENDPOINT_URL, accountConfiguration.getUserinfoEndpoint());
sharedPrefEditor.apply();
- AccountResponse response = CoreLibrary.getInstance().context().getHttpAgent().oauth2authenticate(mUsername, mPassword, AccountHelper.OAUTH.GRANT_TYPE.PASSWORD, accountConfiguration.getTokenEndpoint());
+ AccountResponse response = getOpenSRPContext().getHttpAgent().oauth2authenticate(mUsername, mPassword, AccountHelper.OAUTH.GRANT_TYPE.PASSWORD, accountConfiguration.getTokenEndpoint());
AccountManager mAccountManager = CoreLibrary.getInstance().getAccountManager();
@@ -142,7 +142,9 @@ protected LoginResponse doInBackground(Void... params) {
} catch (Exception e) {
- loginResponse = CUSTOM_SERVER_RESPONSE.withMessage(e.getMessage());
+ loginResponse = CUSTOM_SERVER_RESPONSE.withMessage(getOpenSRPContext().applicationContext().getResources().getString(R.string.unknown_response));
+
+ Timber.e(e);
}
return loginResponse;
diff --git a/opensrp-app/src/main/java/org/smartregister/service/ImageUploadSyncService.java b/opensrp-app/src/main/java/org/smartregister/service/ImageUploadSyncService.java
index 691146844..95b7b8a18 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/ImageUploadSyncService.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/ImageUploadSyncService.java
@@ -43,7 +43,8 @@ protected void onHandleIntent(Intent intent) {
if (response.contains("success")) {
imageRepo.close(profileImages.get(i).getImageid());
} else {
- Timber.e(new StringBuilder("Image Upload: could NOT upload image ID: ").append(profileImages.get(i).getImageid()).append(" PATH: ").append(profileImages.get(i).getFilepath()).toString());
+ Timber.e("Image Upload: could NOT upload image ID: %s %s %s ", profileImages.get(i).getImageid(), " PATH: ", profileImages.get(i).getFilepath());
+
}
}
} catch (Exception e) {
diff --git a/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java b/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java
index e802bcb75..d5554b7a8 100644
--- a/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/sync/helper/SyncSettingsServiceHelper.java
@@ -70,8 +70,7 @@ public int processIntent() throws JSONException {
private JSONArray getSettings() throws JSONException {
- AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
- String authToken = AccountHelper.getCachedOAuthToken(sharedPreferences.fetchRegisteredANM(), authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
+ String authToken = getAccessToken();
JSONArray settings = pullSettingsFromServer(getInstance().getSyncConfiguration().getSettingsSyncFilterValue(), authToken);
getGlobalSettings(settings, authToken);
@@ -79,6 +78,12 @@ private JSONArray getSettings() throws JSONException {
return settings;
}
+ @VisibleForTesting
+ protected String getAccessToken() {
+ AccountAuthenticatorXml authenticatorXml = CoreLibrary.getInstance().getAccountAuthenticatorXml();
+ return AccountHelper.getCachedOAuthToken(sharedPreferences.fetchRegisteredANM(), authenticatorXml.getAccountType(), AccountHelper.TOKEN_TYPE.PROVIDER);
+ }
+
@VisibleForTesting
protected CoreLibrary getInstance() {
return CoreLibrary.getInstance();
diff --git a/opensrp-app/src/test/java/org/smartregister/sync/helper/SyncSettingsServiceHelperTest.java b/opensrp-app/src/test/java/org/smartregister/sync/helper/SyncSettingsServiceHelperTest.java
index 97b3d3959..5009a3d45 100644
--- a/opensrp-app/src/test/java/org/smartregister/sync/helper/SyncSettingsServiceHelperTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/sync/helper/SyncSettingsServiceHelperTest.java
@@ -48,6 +48,7 @@ public class SyncSettingsServiceHelperTest extends BaseRobolectricUnitTest {
@Mock
private SQLiteDatabase sqLiteDatabase;
private String settingsResponse = "[{\"identifier\":\"site_characteristics\",\"settings\":[{\"settingMetadataId\":\"4\",\"serverVersion\":1594125118616,\"description\":\"Is the HIV prevalence consistently > 1% in pregnant women attending antenatal clinics at your facility?\",\"label\":\"Generalized HIV epidemic\",\"type\":\"Setting\",\"value\":\"true\",\"uuid\":\"e42f3e1f-e8b9-4694-8efa-f021e66b5691\",\"key\":\"site_anc_hiv\",\"settingIdentifier\":\"site_characteristics\"},{\"settingMetadataId\":\"1\",\"serverVersion\":1594125118616,\"description\":\"\\\"Are all of the following in place at your facility: \\r\\n1. A protocol or standard operating procedure for Intimate Partner Violence (IPV); \\r\\n2. A health worker trained on how to ask about IPV and how to provide the minimum response or beyond;\\r\\n3. A private setting; \\r\\n4. A way to ensure confidentiality; \\r\\n5. Time to allow for appropriate disclosure; and\\r\\n6. A system for referral in place. \\\"\",\"label\":\"Minimum requirements for IPV assessment\",\"type\":\"Setting\",\"value\":\"true\",\"uuid\":\"fb2ca30f-3de5-4bfc-a2d2-987e9c383cd7\",\"key\":\"site_ipv_assess\",\"settingIdentifier\":\"site_characteristics\"}],\"serverVersion\":1593791975015,\"providerId\":\"demo\",\"locationId\":\"44de66fb-e6c6-4bae-92bb-386dfe626eba\",\"teamId\":\"6c8d2b9b-2246-47c2-949b-4fe29e888cc8\",\"_rev\":\"v1\",\"team\":\"Bukesa\",\"_id\":\"9918b87c-a71f-462d-b1c9-33a2d50e4c15\",\"type\":\"Setting\"}]";
+ private static final String SAMPLE_TEST_TOKEN = "Sample_TOKEN";
@Before
public void setUp() {
@@ -81,10 +82,13 @@ public void testProcessIntent() throws JSONException {
List params = new ArrayList<>();
params.add("locationId=location-uuid");
- Mockito.doReturn(params).when(syncConfiguration).getExtraSettingsParameters();
+ Mockito.doReturn(params).when(syncConfiguration).getExtraSettingsParameters();
Mockito.doReturn(new Response<>(ResponseStatus.success, settingsResponse)).when(syncSettingsServiceHelper).getResponse(ArgumentMatchers.anyString(), ArgumentMatchers.anyString());
+ Mockito.doReturn(SAMPLE_TEST_TOKEN).when(syncSettingsServiceHelper).getAccessToken();
+
int size = syncSettingsServiceHelper.processIntent();
+
Assert.assertEquals(3, size);
}
}
diff --git a/opensrp-app/src/test/java/org/smartregister/view/fragment/BaseRegisterFragmentTest.java b/opensrp-app/src/test/java/org/smartregister/view/fragment/BaseRegisterFragmentTest.java
index d4daea9bc..4be8cfb77 100644
--- a/opensrp-app/src/test/java/org/smartregister/view/fragment/BaseRegisterFragmentTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/view/fragment/BaseRegisterFragmentTest.java
@@ -18,7 +18,6 @@
import org.junit.Assert;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
@@ -270,7 +269,6 @@ public void assertOnQRCodeSucessfullyScannedInvokesFilterWithCorrectParams() {
}
@Test
- @Ignore
public void assertOnQRCodeSucessfullyScannedInvokessetUniqueIDWithCorrectParams() {
String OPENSRP_ID = "8232-372-8L";
From ef043839aa298e481a3430cd6b31ece8ff81edab Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Sat, 18 Jul 2020 17:21:11 +0300
Subject: [PATCH 52/70] Fix local authentication bug - Fix issue local login
not working for team and location auth config types - Refactor to use bytes
for all password refs
---
opensrp-app/build.gradle | 1 +
.../smartregister/util/FakeUserService.java | 2 +-
.../account/AccountAuthenticator.java | 5 +
.../smartregister/account/AccountHelper.java | 4 +-
.../login/interactor/BaseLoginInteractor.java | 121 +++++++-------
.../login/task/RemoteLoginTask.java | 10 +-
.../smartregister/repository/Repository.java | 12 +-
.../helper/OpenSRPDatabaseErrorHandler.java | 35 ++++
.../security/SecurityHelper.java | 18 +--
.../smartregister/service/UserService.java | 150 +++++++++---------
.../java/org/smartregister/util/Session.java | 1 +
.../view/activity/DrishtiApplication.java | 8 +-
.../service/UserServiceTest.java | 2 +-
.../view/activity/DrishtiApplicationTest.java | 2 +-
14 files changed, 194 insertions(+), 177 deletions(-)
create mode 100644 opensrp-app/src/main/java/org/smartregister/repository/helper/OpenSRPDatabaseErrorHandler.java
diff --git a/opensrp-app/build.gradle b/opensrp-app/build.gradle
index 82eb74095..0709af9eb 100644
--- a/opensrp-app/build.gradle
+++ b/opensrp-app/build.gradle
@@ -93,6 +93,7 @@ android {
versionName project.VERSION_NAME
testInstrumentationRunner "android.test.InstrumentationTestRunner"
buildConfigField "long", "BUILD_TIMESTAMP", System.currentTimeMillis() + "L"
+ buildConfigField "int", "ENCRYPTION_VERSION", '1'
}
sourceSets {
diff --git a/opensrp-app/src/androidTest/java/org/smartregister/util/FakeUserService.java b/opensrp-app/src/androidTest/java/org/smartregister/util/FakeUserService.java
index 7a229ea35..49bb3dc1a 100644
--- a/opensrp-app/src/androidTest/java/org/smartregister/util/FakeUserService.java
+++ b/opensrp-app/src/androidTest/java/org/smartregister/util/FakeUserService.java
@@ -22,7 +22,7 @@ public FakeUserService() {
}
@Override
- public boolean isValidLocalLogin(String userName, char[] password) {
+ public boolean isValidLocalLogin(String userName, byte[] password) {
assertExpectedCredentials(userName, password);
actualCalls.add("local");
return shouldSucceedLocalLogin;
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java b/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
index 2b9887f16..0a3dcbb98 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountAuthenticator.java
@@ -7,6 +7,7 @@
import android.accounts.NetworkErrorException;
import android.content.Context;
import android.content.Intent;
+import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
@@ -68,6 +69,10 @@ public Bundle getAuthToken(AccountAuthenticatorResponse response, Account accoun
accountManager.setPassword(account, refreshToken);
accountManager.setAuthToken(account, authTokenType, authToken);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ accountManager.notifyAccountAuthenticated(account);
+ }
}
} catch (Exception e) {
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
index 71ec29ea6..853191dcf 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
@@ -46,8 +46,8 @@ public static final class INTENT_KEY {
public final static String AUTH_TYPE = "AUTH_TYPE";
public final static String ACCOUNT_NAME = "ACCOUNT_NAME";
public final static String IS_NEW_ACCOUNT = "IS_NEW_ACCOUNT";
- public final static String ACCOUNT_GROUP_ID = "ACCOUNT_GROUP_ID";
- public final static String ACCOUNT_PASSWORD = "ACCOUNT_PASSWORD";
+ public final static String ACCOUNT_SECRET_KEY = "ACCOUNT_SECRET_KEY";
+ public final static String ACCOUNT_REFRESH_TOKEN = "ACCOUNT_REFRESH_TOKEN";
public final static String ACCOUNT_PASSWORD_SALT = "ACCOUNT_PASSWORD_SALT";
}
diff --git a/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java b/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java
index cc4c8ac7b..682eb99e9 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/interactor/BaseLoginInteractor.java
@@ -19,7 +19,6 @@
import org.smartregister.job.P2pServiceJob;
import org.smartregister.job.PullUniqueIdsServiceJob;
import org.smartregister.job.SyncSettingsServiceJob;
-import org.smartregister.listener.OnCompleteClearDataCallback;
import org.smartregister.login.task.LocalLoginTask;
import org.smartregister.login.task.RemoteLoginTask;
import org.smartregister.multitenant.ResetAppHelper;
@@ -93,7 +92,7 @@ private void localLogin(WeakReference view, String userN
} else if (isAuthenticated && (!AllConstants.TIME_CHECK || TimeStatus.OK.equals(getUserService().validateStoredServerTimeZone()))) {
- navigateToHomePage(userName, password);
+ navigateToHomePage(userName);
} else {
loginWithLocalFlag(view, false, userName, password);
@@ -104,24 +103,21 @@ private void localLogin(WeakReference view, String userN
}
- private void navigateToHomePage(String userName, char[] password) {
+ private void navigateToHomePage(String userName) {
- getUserService().localLogin(userName, password);
+ getUserService().localLoginWith(userName);
getLoginView().goToHome(false);
CoreLibrary.getInstance().initP2pLibrary(userName);
- new Thread(new Runnable() {
- @Override
- public void run() {
- Timber.i("Starting DrishtiSyncScheduler " + DateTime.now().toString());
+ new Thread(() -> {
+ Timber.i("Starting DrishtiSyncScheduler " + DateTime.now().toString());
- scheduleJobsImmediately();
+ scheduleJobsImmediately();
- Timber.i("Started DrishtiSyncScheduler " + DateTime.now().toString());
+ Timber.i("Started DrishtiSyncScheduler " + DateTime.now().toString());
- CoreLibrary.getInstance().context().getUniqueIdRepository().releaseReservedIds();
- }
+ CoreLibrary.getInstance().context().getUniqueIdRepository().releaseReservedIds();
}).start();
}
@@ -132,68 +128,59 @@ private void remoteLogin(final String userName, final char[] password, final Acc
getSharedPreferences().savePreference("DRISHTI_BASE_URL", getApplicationContext().getString(R.string.opensrp_url));
}
if (!getSharedPreferences().fetchBaseURL("").isEmpty()) {
- tryRemoteLogin(userName, password, accountAuthenticatorXml, new Listener() {
-
- public void onEvent(LoginResponse loginResponse) {
- getLoginView().enableLoginButton(true);
- if (loginResponse == LoginResponse.SUCCESS) {
- String username = loginResponse.payload() != null && loginResponse.payload().user != null && StringUtils.isNotBlank(loginResponse.payload().user.getUsername())
- ? loginResponse.payload().user.getUsername() : userName;
- if (getUserService().isUserInPioneerGroup(username)) {
- TimeStatus timeStatus = getUserService().validateDeviceTime(
- loginResponse.payload(), AllConstants.MAX_SERVER_TIME_DIFFERENCE
- );
- if (!AllConstants.TIME_CHECK || timeStatus.equals(TimeStatus.OK)) {
-
- remoteLoginWith(username, password, loginResponse);
-
- } else {
- if (timeStatus.equals(TimeStatus.TIMEZONE_MISMATCH)) {
- TimeZone serverTimeZone = UserService.getServerTimeZone(loginResponse.payload());
+ tryRemoteLogin(userName, password, accountAuthenticatorXml, loginResponse -> {
+ getLoginView().enableLoginButton(true);
+ if (loginResponse == LoginResponse.SUCCESS) {
+ String username = loginResponse.payload() != null && loginResponse.payload().user != null && StringUtils.isNotBlank(loginResponse.payload().user.getUsername())
+ ? loginResponse.payload().user.getUsername() : userName;
+ if (getUserService().isUserInPioneerGroup(username)) {
+ TimeStatus timeStatus = getUserService().validateDeviceTime(
+ loginResponse.payload(), AllConstants.MAX_SERVER_TIME_DIFFERENCE
+ );
+ if (!AllConstants.TIME_CHECK || timeStatus.equals(TimeStatus.OK)) {
+
+ remoteLoginWith(username, loginResponse);
- getLoginView().showErrorDialog(getApplicationContext().getString(timeStatus.getMessage(),
- serverTimeZone.getDisplayName()));
- } else {
- getLoginView().showErrorDialog(getApplicationContext().getString(timeStatus.getMessage()));
- }
- }
} else {
+ if (timeStatus.equals(TimeStatus.TIMEZONE_MISMATCH)) {
+ TimeZone serverTimeZone = UserService.getServerTimeZone(loginResponse.payload());
- if (CoreLibrary.getInstance().getSyncConfiguration().clearDataOnNewTeamLogin()) {
- getLoginView().showClearDataDialog(new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- if (which == DialogInterface.BUTTON_POSITIVE) {
- ResetAppHelper resetAppHelper = new ResetAppHelper(DrishtiApplication.getInstance());
- resetAppHelper.startResetProcess(getLoginView().getAppCompatActivity(), new OnCompleteClearDataCallback() {
- @Override
- public void onComplete() {
- login(new WeakReference<>(getLoginView()), userName, password);
- }
- });
- }
- }
- });
+ getLoginView().showErrorDialog(getApplicationContext().getString(timeStatus.getMessage(),
+ serverTimeZone.getDisplayName()));
} else {
- // Valid user from wrong group trying to log in
- getLoginView().showErrorDialog(getApplicationContext().getString(R.string.unauthorized_group));
+ getLoginView().showErrorDialog(getApplicationContext().getString(timeStatus.getMessage()));
}
+ }
+ } else {
+ if (CoreLibrary.getInstance().getSyncConfiguration().clearDataOnNewTeamLogin()) {
+ getLoginView().showClearDataDialog((dialog, which) -> {
+
+ dialog.dismiss();
+
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ ResetAppHelper resetAppHelper = new ResetAppHelper(DrishtiApplication.getInstance());
+ resetAppHelper.startResetProcess(getLoginView().getAppCompatActivity(), () -> login(new WeakReference<>(getLoginView()), userName, password));
+ }
+ });
+ } else {
+ // Valid user from wrong group trying to log in
+ getLoginView().showErrorDialog(getApplicationContext().getString(R.string.unauthorized_group));
}
+
+ }
+ } else {
+ if (loginResponse == null) {
+ getLoginView().showErrorDialog("Sorry, your loginWithLocalFlag failed. Please try again");
} else {
- if (loginResponse == null) {
- getLoginView().showErrorDialog("Sorry, your loginWithLocalFlag failed. Please try again");
+ if (loginResponse == NO_INTERNET_CONNECTIVITY) {
+ getLoginView().showErrorDialog(getApplicationContext().getResources().getString(R.string.no_internet_connectivity));
+ } else if (loginResponse == UNKNOWN_RESPONSE) {
+ getLoginView().showErrorDialog(getApplicationContext().getResources().getString(R.string.unknown_response));
+ } else if (loginResponse == UNAUTHORIZED) {
+ getLoginView().showErrorDialog(getApplicationContext().getResources().getString(R.string.unauthorized));
} else {
- if (loginResponse == NO_INTERNET_CONNECTIVITY) {
- getLoginView().showErrorDialog(getApplicationContext().getResources().getString(R.string.no_internet_connectivity));
- } else if (loginResponse == UNKNOWN_RESPONSE) {
- getLoginView().showErrorDialog(getApplicationContext().getResources().getString(R.string.unknown_response));
- } else if (loginResponse == UNAUTHORIZED) {
- getLoginView().showErrorDialog(getApplicationContext().getResources().getString(R.string.unauthorized));
- } else {
- getLoginView().showErrorDialog(loginResponse.message());
- }
+ getLoginView().showErrorDialog(loginResponse.message());
}
}
}
@@ -216,8 +203,8 @@ private void tryRemoteLogin(final String userName, final char[] password, final
remoteLoginTask.execute();
}
- private void remoteLoginWith(String userName, char[] password, LoginResponse loginResponse) {
- getUserService().processLoginResponseDataForUser(userName, password, loginResponse.payload());
+ private void remoteLoginWith(String userName, LoginResponse loginResponse) {
+ getUserService().processLoginResponseDataForUser(userName, loginResponse.payload());
processServerSettings(loginResponse);
scheduleJobsPeriodically();
diff --git a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
index d68a56ebe..261ebafc6 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
@@ -102,21 +102,21 @@ protected LoginResponse doInBackground(Void... params) {
if (loginResponse != null && loginResponse.equals(LoginResponse.SUCCESS)) {
- Bundle userData = getOpenSRPContext().userService().saveUserGroup(mUsername, mPassword, loginResponse.payload());
+ Bundle userData = getOpenSRPContext().userService().saveUserCredentials(mUsername, mPassword, loginResponse.payload());
mAccountManager.addAccountExplicitly(account, response.getRefreshToken(), userData);
mAccountManager.setAuthToken(account, mLoginView.getAuthTokenType(), response.getAccessToken());
mAccountManager.setPassword(account, response.getRefreshToken());
- mAccountManager.setUserData(account, AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, userData.getString(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID));
- mAccountManager.setUserData(account, AccountHelper.INTENT_KEY.ACCOUNT_NAME, userData.getString(AccountHelper.INTENT_KEY.ACCOUNT_NAME));
- mAccountManager.setUserData(account, AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD, userData.getString(AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD));
+ mAccountManager.setUserData(account, AccountHelper.INTENT_KEY.ACCOUNT_SECRET_KEY, userData.getString(AccountHelper.INTENT_KEY.ACCOUNT_SECRET_KEY));
mAccountManager.setUserData(account, AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD_SALT, userData.getString(AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD_SALT));
+ mAccountManager.setUserData(account, AccountHelper.INTENT_KEY.ACCOUNT_NAME, userData.getString(AccountHelper.INTENT_KEY.ACCOUNT_NAME));
+ mAccountManager.setUserData(account, AccountHelper.INTENT_KEY.ACCOUNT_REFRESH_TOKEN, response.getRefreshToken());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mAccountManager.notifyAccountAuthenticated(account);
}
- if (getOpenSRPContext().userService().getGroupId(mUsername) != null && CoreLibrary.getInstance().getSyncConfiguration().isSyncSettings()) {
+ if (getOpenSRPContext().userService().getAccountSecretKey(mUsername) != null && CoreLibrary.getInstance().getSyncConfiguration().isSyncSettings()) {
publishProgress(R.string.loading_client_settings);
diff --git a/opensrp-app/src/main/java/org/smartregister/repository/Repository.java b/opensrp-app/src/main/java/org/smartregister/repository/Repository.java
index 81c4d32b4..000e0d177 100755
--- a/opensrp-app/src/main/java/org/smartregister/repository/Repository.java
+++ b/opensrp-app/src/main/java/org/smartregister/repository/Repository.java
@@ -2,6 +2,7 @@
import android.content.Context;
+import net.sqlcipher.DefaultDatabaseErrorHandler;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteDatabaseHook;
import net.sqlcipher.database.SQLiteException;
@@ -12,6 +13,7 @@
import org.smartregister.CoreLibrary;
import org.smartregister.commonregistry.CommonFtsObject;
import org.smartregister.exception.DatabaseMigrationException;
+import org.smartregister.repository.helper.OpenSRPDatabaseErrorHandler;
import org.smartregister.util.DatabaseMigrationUtils;
import org.smartregister.util.Session;
import org.smartregister.view.activity.DrishtiApplication;
@@ -156,15 +158,17 @@ public SQLiteDatabase getWritableDatabase() {
return getWritableDatabase(password());
}
- private boolean isDatabaseWritable(char[] password) {
+ private boolean isDatabaseWritable(byte[] password) {
+
+
SQLiteDatabase database = SQLiteDatabase
.openDatabase(databasePath.getPath(), password, null,
- SQLiteDatabase.OPEN_READONLY, hook);
+ SQLiteDatabase.OPEN_READONLY, hook, new OpenSRPDatabaseErrorHandler());
database.close();
return true;
}
- public boolean canUseThisPassword(char[] password) {
+ public boolean canUseThisPassword(byte[] password) {
try {
return isDatabaseWritable(password);
} catch (SQLiteException e) {
@@ -190,7 +194,7 @@ public boolean canUseThisPassword(char[] password) {
}
}
- private char[] password() {
+ private byte[] password() {
return DrishtiApplication.getInstance().getPassword();
}
diff --git a/opensrp-app/src/main/java/org/smartregister/repository/helper/OpenSRPDatabaseErrorHandler.java b/opensrp-app/src/main/java/org/smartregister/repository/helper/OpenSRPDatabaseErrorHandler.java
new file mode 100644
index 000000000..69f69deef
--- /dev/null
+++ b/opensrp-app/src/main/java/org/smartregister/repository/helper/OpenSRPDatabaseErrorHandler.java
@@ -0,0 +1,35 @@
+package org.smartregister.repository.helper;
+
+import android.util.Log;
+
+import net.sqlcipher.DatabaseErrorHandler;
+import net.sqlcipher.database.SQLiteDatabase;
+
+/**
+ * Created by ndegwamartin on 18/07/2020.
+ */
+public class OpenSRPDatabaseErrorHandler implements DatabaseErrorHandler {
+
+ private final String TAG = getClass().getSimpleName();
+
+ /**
+ * defines the default method to be invoked when database corruption is detected.
+ *
+ * @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption
+ * is detected.
+ */
+ public void onCorruption(SQLiteDatabase dbObj) {
+ Log.e(TAG, "Corruption reported by sqlite on database, db file path: " + dbObj.getPath());
+
+ if (dbObj.isOpen()) {
+ Log.e(TAG, "Database object for corrupted database is already open, closing");
+
+ try {
+ dbObj.close();
+ } catch (Exception e) {
+ Log.e(TAG, "Exception closing Database object for corrupted database, ignored", e);
+ }
+ }
+
+ }
+}
diff --git a/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java b/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java
index 8ba95e78b..f49b6bee9 100644
--- a/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java
@@ -4,6 +4,8 @@
import android.text.Editable;
import android.util.Base64;
+import net.sqlcipher.database.SQLiteDatabase;
+
import org.apache.commons.codec.CharEncoding;
import org.apache.commons.lang3.StringUtils;
@@ -100,15 +102,8 @@ private static void clearStringBuffer(StringBuffer stringBuffer) {
* @return an array of bytes, a conversion from the chars array
*/
public static byte[] toBytes(char[] chars) {
- CharBuffer charBuffer = CharBuffer.wrap(chars);
-
- ByteBuffer byteBuffer = CHARSET.encode(charBuffer);
- byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
-
- clearArray(byteBuffer.array());
-
- return bytes;
+ return SQLiteDatabase.getBytes(chars);
}
@@ -120,12 +115,7 @@ public static byte[] toBytes(char[] chars) {
*/
public static char[] toChars(byte[] bytes) {
- char[] convertedChar = new char[bytes.length];
- for (int i = 0; i < bytes.length; i++) {
- convertedChar[i] = (char) bytes[i];
- }
-
- return convertedChar;
+ return SQLiteDatabase.getChars(bytes);
}
/**
diff --git a/opensrp-app/src/main/java/org/smartregister/service/UserService.java b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
index 2dfcf4edb..11b6c6f4c 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/UserService.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
@@ -209,7 +209,7 @@ public TimeStatus validateDeviceTime(LoginResponseData userInfo, long serverTime
return TimeStatus.ERROR;
}
- public boolean isValidLocalLogin(String userName, char[] password) {
+ public boolean isValidLocalLogin(String userName, byte[] password) {
return allSharedPreferences.fetchRegisteredANM().equals(userName) && DrishtiApplication.getInstance().getRepository()
.canUseThisPassword(password) && !allSharedPreferences.fetchForceRemoteLogin(userName);
}
@@ -217,26 +217,23 @@ public boolean isValidLocalLogin(String userName, char[] password) {
public boolean isUserInValidGroup(final String userName, final char[] password) {
// Check if everything OK for local login
if (keyStore != null && userName != null && password != null && !allSharedPreferences.fetchForceRemoteLogin(userName)) {
+
String username = userName.equalsIgnoreCase(allSharedPreferences.fetchRegisteredANM()) ? allSharedPreferences.fetchRegisteredANM() : userName;
+
byte[] storedHash = null;
byte[] passwordHash = null;
byte[] passwordSalt = null;
try {
- KeyStore.PrivateKeyEntry privateKeyEntry = getUserKeyPair(username);
- if (privateKeyEntry != null) {
- // Compare stored password hash with provided password hash
- storedHash = SecurityHelper.nullSafeBase64Decode(AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD, userName, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType()));
+ // Compare stored password hash with provided password hash
+ storedHash = getAccountSecretKey(username);
- passwordSalt = SecurityHelper.nullSafeBase64Decode(AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD_SALT, userName, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType()));
- passwordHash = SecurityHelper.hashPassword(password, passwordSalt);
+ passwordSalt = SecurityHelper.nullSafeBase64Decode(AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD_SALT, userName, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType()));
+ passwordHash = SecurityHelper.hashPassword(getEncryptionParamValue(username, password), passwordSalt);
- if (storedHash != null && Arrays.equals(storedHash, passwordHash)) {
- char[] groupId = getGroupId(username, privateKeyEntry);
- if (groupId != null) {
- return isValidGroupId(groupId);
- }
- }
+ if (storedHash != null && Arrays.equals(storedHash, passwordHash)) {
+
+ return isValidDBPassword(storedHash);
}
} catch (Exception e) {
Timber.e(e);
@@ -251,15 +248,28 @@ public boolean isUserInValidGroup(final String userName, final char[] password)
return false;
}
- private boolean isValidGroupId(char[] groupId) {
- return DrishtiApplication.getInstance().getRepository().canUseThisPassword(groupId);
+ private char[] getEncryptionParamValue(String username, char[] password) {
+
+ char[] encryptionParamValue = password;
+ SyncFilter syncFilter = CoreLibrary.getInstance().getSyncConfiguration().getEncryptionParam();
+
+ if (SyncFilter.TEAM.equals(syncFilter) || SyncFilter.TEAM_ID.equals(syncFilter)) {
+ encryptionParamValue = allSharedPreferences.fetchDefaultTeamId(username).toCharArray();
+ } else if (SyncFilter.LOCATION.equals(syncFilter) || SyncFilter.LOCATION_ID.equals(syncFilter)) {
+ encryptionParamValue = allSharedPreferences.fetchDefaultLocalityId(username).toCharArray();
+ }
+ return encryptionParamValue;
+ }
+
+ private boolean isValidDBPassword(byte[] password) {
+ return DrishtiApplication.getInstance().getRepository().canUseThisPassword(password);
}
- public char[] getGroupId(String userName) {
+ public byte[] getAccountSecretKey(String userName) {
if (keyStore != null && userName != null) {
try {
KeyStore.PrivateKeyEntry privateKeyEntry = getUserKeyPair(userName);
- return getGroupId(userName, privateKeyEntry);
+ return getAccountSecretKey(userName, privateKeyEntry);
} catch (Exception e) {
Timber.e(e);
}
@@ -267,14 +277,14 @@ public char[] getGroupId(String userName) {
return null;
}
- public char[] getGroupId(String userName, KeyStore.PrivateKeyEntry privateKeyEntry) {
+ private byte[] getAccountSecretKey(String userName, KeyStore.PrivateKeyEntry privateKeyEntry) {
if (privateKeyEntry != null) {
- String encryptedGroupId = AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, userName, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType());
+ String encryptedSecretKey = AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_SECRET_KEY, userName, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType());
- if (encryptedGroupId != null) {
+ if (encryptedSecretKey != null) {
try {
- return SecurityHelper.toChars(decryptString(privateKeyEntry, encryptedGroupId));
+ return decryptString(privateKeyEntry, encryptedSecretKey);
} catch (Exception e) {
Timber.e(e);
}
@@ -295,11 +305,11 @@ public boolean isUserInPioneerGroup(String userName) {
if (userName.equals(pioneerUser)) {
return true;
} else {
- char[] userGroupId = getGroupId(userName);
- char[] pioneerGroupId = getGroupId(pioneerUser);
+ byte[] currentUserSecretKey = getAccountSecretKey(userName);
+ byte[] pioneerUserSecretKey = getAccountSecretKey(pioneerUser);
- if (userGroupId != null && Arrays.equals(pioneerGroupId, userGroupId)) {
- return isValidGroupId(userGroupId);
+ if (currentUserSecretKey != null && Arrays.equals(pioneerUserSecretKey, currentUserSecretKey)) {
+ return isValidDBPassword(currentUserSecretKey);
}
}
@@ -324,7 +334,7 @@ public LoginResponse isValidRemoteLogin(String userName, char[] password) {
LoginResponse loginResponse = httpAgent.urlCanBeAccessWithGivenCredentials(requestURL, userName, password);
if (loginResponse != null && loginResponse.equals(LoginResponse.SUCCESS)) {
- saveUserGroup(userName, password, loginResponse.payload());
+ saveUserCredentials(userName, password, loginResponse.payload());
}
return loginResponse;
@@ -339,54 +349,32 @@ public Response getLocationInformation() {
return httpAgent.fetch(requestURL);
}
- private boolean loginWith(String userName, char[] password) {
+ public boolean localLoginWith(String userName) {
boolean loginSuccessful = true;
- if (usesGroupIdAsDBPassword(userName)) {
+ try {
- String encryptedGroupId = AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, userName, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType());
+ byte[] secretKey = getAccountSecretKey(userName);
+ setupContextForLogin(secretKey);
- try {
- KeyStore.PrivateKeyEntry privateKeyEntry = getUserKeyPair(userName);
- if (privateKeyEntry != null) {
- byte[] groupId = decryptString(privateKeyEntry, encryptedGroupId);
- setupContextForLogin(SecurityHelper.toChars(groupId));
- SecurityHelper.clearArray(groupId);
- }
- } catch (Exception e) {
- Timber.e(e);
- loginSuccessful = false;
+ if (!allSharedPreferences.fetchRegisteredANM().equalsIgnoreCase(userName)) {
+ allSharedPreferences.updateANMUserName(userName);
}
- } else {
- setupContextForLogin(password);
- }
- String username = userName;
- if (allSharedPreferences.fetchRegisteredANM().equalsIgnoreCase(userName))
- username = allSharedPreferences.fetchRegisteredANM();
- allSharedPreferences.updateANMUserName(username);
- DrishtiApplication.getInstance().getRepository().getReadableDatabase();
- allSettings.registerANM(username);
- return loginSuccessful;
- }
- /**
- * Checks whether to use the groupId for the current user to decrypt the database
- *
- * @param username the username
- * @return TRUE if the user decrypts the database using the groupId
- */
- private boolean usesGroupIdAsDBPassword(String username) {
- return AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, username, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType()) != null;
- }
+ DrishtiApplication.getInstance().getRepository().getReadableDatabase();
- public void localLogin(String userName, char[] password) {
- loginWith(userName, password);
+ } catch (Exception e) {
+ Timber.e(e);
+ loginSuccessful = false;
+ }
+
+ return loginSuccessful;
}
- public void processLoginResponseDataForUser(String userName, char[] password, LoginResponseData userInfo) {
+ public void processLoginResponseDataForUser(String userName, LoginResponseData userInfo) {
String username = userInfo.user != null && StringUtils.isNotBlank(userInfo.user.getUsername())
? userInfo.user.getUsername() : userName;
- boolean loginSuccessful = loginWith(username, password);
+ boolean loginSuccessful = localLoginWith(username);
saveAnmLocation(getUserLocation(userInfo));
saveAnmTeam(getUserTeam(userInfo));
saveUserInfo(getUserData(userInfo));
@@ -564,7 +552,7 @@ public void saveUserInfo(User user) {
* @param userInfo The user's info from the
* endpoint (should contain the {team}.{team}.{uuid} key)
*/
- public Bundle saveUserGroup(String userName, char[] password, LoginResponseData userInfo) {
+ public Bundle saveUserCredentials(String userName, char[] password, LoginResponseData userInfo) {
Bundle bundle = new Bundle();
String username = userInfo.user != null && StringUtils.isNotBlank(userInfo.user.getUsername()) ? userInfo.user.getUsername() : userName;
@@ -572,7 +560,7 @@ public Bundle saveUserGroup(String userName, char[] password, LoginResponseData
if (keyStore != null && username != null) {
- byte[] groupId = null;
+ char[] encryptionParamValue = null;
try {
@@ -582,30 +570,29 @@ public Bundle saveUserGroup(String userName, char[] password, LoginResponseData
return null;
}
- PasswordHash passwordHash = SecurityHelper.getPasswordHash(password);
SyncConfiguration syncConfiguration = CoreLibrary.getInstance().getSyncConfiguration();
if (syncConfiguration.getEncryptionParam() != null) {
SyncFilter syncFilter = syncConfiguration.getEncryptionParam();
if (SyncFilter.TEAM.equals(syncFilter) || SyncFilter.TEAM_ID.equals(syncFilter)) {
- groupId = SecurityHelper.toBytes(getUserDefaultTeamId(userInfo).toCharArray());
+ encryptionParamValue = getUserDefaultTeamId(userInfo).toCharArray();
} else if (SyncFilter.LOCATION.equals(syncFilter) || SyncFilter.LOCATION_ID.equals(syncFilter)) {
- groupId = SecurityHelper.toBytes(getUserLocationId(userInfo).toCharArray());
+ encryptionParamValue = getUserLocationId(userInfo).toCharArray();
} else if (SyncFilter.PROVIDER.equals(syncFilter)) {
- groupId = SecurityHelper.toBytes(new StringBuffer(username).append('-').append(passwordHash.getPassword()));
+ encryptionParamValue = password;
}
}
- if (groupId == null || groupId.length < 1) {
+ if (encryptionParamValue == null || encryptionParamValue.length < 1) {
return null;
}
if (privateKeyEntry != null) {
- // Then save the encrypted group
- String encryptedGroupId = encryptString(privateKeyEntry, groupId);
- bundle.putString(AccountHelper.INTENT_KEY.ACCOUNT_GROUP_ID, encryptedGroupId);
+ PasswordHash passwordHash = SecurityHelper.getPasswordHash(encryptionParamValue);
- bundle.putString(AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD, Base64.encodeToString(passwordHash.getPassword(), Base64.DEFAULT));
+ // Then save the encrypted secret key
+ String encryptedSecretKey = encryptString(privateKeyEntry, passwordHash.getPassword());
+ bundle.putString(AccountHelper.INTENT_KEY.ACCOUNT_SECRET_KEY, encryptedSecretKey);
bundle.putString(AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD_SALT, Base64.encodeToString(passwordHash.getSalt(), Base64.DEFAULT));
// Finally, save the pioneer user
@@ -618,7 +605,7 @@ public Bundle saveUserGroup(String userName, char[] password, LoginResponseData
} finally {
SecurityHelper.clearArray(password);
- SecurityHelper.clearArray(groupId);
+ SecurityHelper.clearArray(encryptionParamValue);
}
}
@@ -633,22 +620,29 @@ public void logout() {
logoutSession();
allSettings.registerANM("");
allSettings.savePreviousFetchIndex("0");
- DrishtiApplication.getInstance().getRepository().deleteRepository();
+ configuration.getDrishtiApplication().getRepository().deleteRepository();
}
public void logoutSession() {
session().expire();
+ clearApplicationPasswordReference();
ON_LOGOUT.notifyListeners(true);
}
+ private void clearApplicationPasswordReference() {
+
+ SecurityHelper.clearArray(configuration.getDrishtiApplication().getPassword());
+ configuration.getDrishtiApplication().setPassword(null);
+ }
+
public boolean hasSessionExpired() {
return session().hasExpired();
}
- protected void setupContextForLogin(char[] password) {
+ protected void setupContextForLogin(byte[] password) {
session().start(session().lengthInMilliseconds());
configuration.getDrishtiApplication().setPassword(password);
- session().setPassword(SecurityHelper.toBytes(password));
+ session().setPassword(password);
}
protected Session session() {
diff --git a/opensrp-app/src/main/java/org/smartregister/util/Session.java b/opensrp-app/src/main/java/org/smartregister/util/Session.java
index b315dc52f..063c8336d 100644
--- a/opensrp-app/src/main/java/org/smartregister/util/Session.java
+++ b/opensrp-app/src/main/java/org/smartregister/util/Session.java
@@ -53,6 +53,7 @@ public boolean hasExpired() {
public void expire() {
SecurityHelper.clearArray(this.password);
+ this.password = null;
setSessionExpiryTimeTo(new Date().getTime() - 1);
}
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java b/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java
index 51a969643..b91005bc0 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java
@@ -38,7 +38,7 @@ public abstract class DrishtiApplication extends Application {
protected Locale locale = null;
protected Context context;
protected Repository repository;
- private char[] password;
+ private byte[] password;
private String username;
public static synchronized X getInstance() {
@@ -117,17 +117,17 @@ public Repository getRepository() {
return repository;
}
- public char[] getPassword() {
+ public byte[] getPassword() {
if (password == null) {
String username = context.userService().getAllSharedPreferences().fetchRegisteredANM();
- password = context.userService().getGroupId(username);
+ password = context.userService().getAccountSecretKey(username);
}
return password;
}
- public void setPassword(char[] password) {
+ public void setPassword(byte[] password) {
this.password = password;
}
diff --git a/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java b/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java
index 4f6cf82aa..bcb81d73f 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java
@@ -209,7 +209,7 @@ public void logoutCurrentUser() {
when(allSharedPreferences.fetchRegisteredANM()).thenReturn("user X");
- userService.processLoginResponseDataForUser("user X", "password Y".toCharArray(), userInfo);
+ userService.processLoginResponseDataForUser("user X", userInfo);
verify(allSettings).registerANM("user X");
}
diff --git a/opensrp-app/src/test/java/org/smartregister/view/activity/DrishtiApplicationTest.java b/opensrp-app/src/test/java/org/smartregister/view/activity/DrishtiApplicationTest.java
index df7313f5a..2f651d613 100644
--- a/opensrp-app/src/test/java/org/smartregister/view/activity/DrishtiApplicationTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/view/activity/DrishtiApplicationTest.java
@@ -81,7 +81,7 @@ public void getPassword() {
AllSharedPreferences allSharedPreferences = Mockito.spy(drishtiApplication.getContext().userService().getAllSharedPreferences());
ReflectionHelpers.setField(drishtiApplication.getContext().userService(), "allSharedPreferences", allSharedPreferences);
Mockito.doReturn(username).when(allSharedPreferences).fetchRegisteredANM();
- Mockito.doReturn(password).when(userService).getGroupId(Mockito.eq(username));
+ Mockito.doReturn(password).when(userService).getAccountSecretKey(Mockito.eq(username));
Assert.assertEquals(password, drishtiApplication.getPassword());
}
From 326e5e3d460842c675ce0a76b0dfbc50ecc21526 Mon Sep 17 00:00:00 2001
From: Martin Ndegwa
Date: Mon, 20 Jul 2020 18:35:18 +0300
Subject: [PATCH 53/70] Refactor authentication logic - Separate local device
authentication from database encryption/authentication - Refactor all
password refs to byte array - Fix tests
Signed-off-by: Martin Ndegwa
---
.../smartregister/account/AccountHelper.java | 5 +-
.../domain/jsonmapping/User.java | 14 +--
.../login/task/RemoteLoginTask.java | 2 +-
.../repository/AllSharedPreferences.java | 11 ++
.../security/SecurityHelper.java | 8 ++
.../smartregister/service/UserService.java | 37 ++++--
.../view/activity/DrishtiApplication.java | 3 +-
.../org/smartregister/TestApplication.java | 4 -
.../repository/mock/RepositoryMock.java | 2 +-
.../service/UserServiceTest.java | 118 ++++++++----------
.../view/activity/DrishtiApplicationTest.java | 2 +-
11 files changed, 111 insertions(+), 95 deletions(-)
diff --git a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
index 853191dcf..4ea12ef3b 100644
--- a/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/account/AccountHelper.java
@@ -128,7 +128,8 @@ public static String getOAuthToken(String accountName, String accountType, Strin
* @param authToken token to invalidate
*/
public static void invalidateAuthToken(String accountType, String authToken) {
- accountManager.invalidateAuthToken(accountType, authToken);
+ if (authToken != null)
+ accountManager.invalidateAuthToken(accountType, authToken);
}
@@ -140,7 +141,7 @@ public static void invalidateAuthToken(String accountType, String authToken) {
*/
public static String getCachedOAuthToken(String accountName, String accountType, String authTokenType) {
Account account = getOauthAccountByNameAndType(accountName, accountType);
- return accountManager.peekAuthToken(account, authTokenType);
+ return account != null ? accountManager.peekAuthToken(account, authTokenType) : null;
}
/**
diff --git a/opensrp-app/src/main/java/org/smartregister/domain/jsonmapping/User.java b/opensrp-app/src/main/java/org/smartregister/domain/jsonmapping/User.java
index 1e2e65b59..5ece59bba 100644
--- a/opensrp-app/src/main/java/org/smartregister/domain/jsonmapping/User.java
+++ b/opensrp-app/src/main/java/org/smartregister/domain/jsonmapping/User.java
@@ -16,7 +16,7 @@ public class User extends BaseEntity {
private String username;
- private String password;
+ private char[] password;
private String salt;
@@ -36,14 +36,14 @@ public User(String baseEntityId) {
super(baseEntityId);
}
- public User(String baseEntityId, String username, String password, String salt) {
+ public User(String baseEntityId, String username, char[] password, String salt) {
super(baseEntityId);
this.username = username;
this.password = password;
this.salt = salt;
}
- public User(String baseEntityId, String username, String password, String salt, String status, List roles, List permissions) {
+ public User(String baseEntityId, String username, char[] password, String salt, String status, List roles, List permissions) {
super(baseEntityId);
this.username = username;
this.password = password;
@@ -53,7 +53,7 @@ public User(String baseEntityId, String username, String password, String salt,
this.permissions = permissions;
}
- public User(String baseEntityId, String username, String password, String preferredName, String salt, String status, List roles,
+ public User(String baseEntityId, String username, char[] password, String preferredName, String salt, String status, List roles,
List permissions) {
super(baseEntityId);
this.username = username;
@@ -73,11 +73,11 @@ public void setUsername(String username) {
this.username = username;
}
- public String getPassword() {
+ public char[] getPassword() {
return password;
}
- public void setPassword(String password) {
+ public void setPassword(char[] password) {
this.password = password;
}
@@ -172,7 +172,7 @@ public User withUsername(String username) {
return this;
}
- public User withPassword(String password) {
+ public User withPassword(char[] password) {
this.password = password;
return this;
}
diff --git a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
index 261ebafc6..a48fe8a24 100644
--- a/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
+++ b/opensrp-app/src/main/java/org/smartregister/login/task/RemoteLoginTask.java
@@ -116,7 +116,7 @@ protected LoginResponse doInBackground(Void... params) {
mAccountManager.notifyAccountAuthenticated(account);
}
- if (getOpenSRPContext().userService().getAccountSecretKey(mUsername) != null && CoreLibrary.getInstance().getSyncConfiguration().isSyncSettings()) {
+ if (getOpenSRPContext().userService().getDecryptedPreferenceValue(mUsername) != null && CoreLibrary.getInstance().getSyncConfiguration().isSyncSettings()) {
publishProgress(R.string.loading_client_settings);
diff --git a/opensrp-app/src/main/java/org/smartregister/repository/AllSharedPreferences.java b/opensrp-app/src/main/java/org/smartregister/repository/AllSharedPreferences.java
index 284d8e468..131f60857 100644
--- a/opensrp-app/src/main/java/org/smartregister/repository/AllSharedPreferences.java
+++ b/opensrp-app/src/main/java/org/smartregister/repository/AllSharedPreferences.java
@@ -43,6 +43,7 @@ public class AllSharedPreferences {
private static final String PEER_TO_PEER_SYNC_LAST_FOREIGN_PROCESSED_RECORD = "PEER_TO_PEER_SYNC_LAST_FOREIGN_PROCESSED_RECORD";
public static final String MANIFEST_VERSION = "MANIFEST_VERSION";
public static final String FORMS_VERSION = "FORMS_VERSION";
+ private static final String ENCRYPTED_PASSPHRASE_KEY = "ENCRYPTED_PASSPHRASE_KEY";
private SharedPreferences preferences;
public AllSharedPreferences(SharedPreferences preferences) {
@@ -348,5 +349,15 @@ public SharedPreferences getPreferences() {
return preferences;
}
+ public String getPassphrase(String username) {
+ return preferences.getString(ENCRYPTED_PASSPHRASE_KEY + username, null);
+ }
+
+ public void savePassphrase(String username, String passphrase) {
+ if (username != null) {
+ preferences.edit().putString(ENCRYPTED_PASSPHRASE_KEY + username, passphrase).commit();
+ }
+ }
+
}
diff --git a/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java b/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java
index f49b6bee9..81a7b7867 100644
--- a/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java
+++ b/opensrp-app/src/main/java/org/smartregister/security/SecurityHelper.java
@@ -7,6 +7,7 @@
import net.sqlcipher.database.SQLiteDatabase;
import org.apache.commons.codec.CharEncoding;
+import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import java.nio.ByteBuffer;
@@ -30,6 +31,7 @@ public class SecurityHelper {
private static final Charset CHARSET = Charset.forName(CharEncoding.UTF_8);
public static final int ITERATION_COUNT = 200048;
+ private static final int PASSPHRASE_SIZE = 32;
/**
* This method ensures that sensitive info can be collected for the edit text in a safer way
@@ -160,4 +162,10 @@ public static byte[] nullSafeBase64Decode(String base64EncodedValue) {
return null;
}
}
+
+ public static char[] generateRandomPassphrase() {
+
+ return RandomStringUtils.randomAlphanumeric(PASSPHRASE_SIZE).toCharArray();
+ }
+
}
diff --git a/opensrp-app/src/main/java/org/smartregister/service/UserService.java b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
index 11b6c6f4c..c016f56f1 100644
--- a/opensrp-app/src/main/java/org/smartregister/service/UserService.java
+++ b/opensrp-app/src/main/java/org/smartregister/service/UserService.java
@@ -226,14 +226,14 @@ public boolean isUserInValidGroup(final String userName, final char[] password)
try {
// Compare stored password hash with provided password hash
- storedHash = getAccountSecretKey(username);
+ storedHash = getDecryptedAccountValue(username, AccountHelper.INTENT_KEY.ACCOUNT_SECRET_KEY);
passwordSalt = SecurityHelper.nullSafeBase64Decode(AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD_SALT, userName, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType()));
passwordHash = SecurityHelper.hashPassword(getEncryptionParamValue(username, password), passwordSalt);
if (storedHash != null && Arrays.equals(storedHash, passwordHash)) {
- return isValidDBPassword(storedHash);
+ return isValidDBPassword(getDecryptedPreferenceValue(username));
}
} catch (Exception e) {
Timber.e(e);
@@ -265,11 +265,11 @@ private boolean isValidDBPassword(byte[] password) {
return DrishtiApplication.getInstance().getRepository().canUseThisPassword(password);
}
- public byte[] getAccountSecretKey(String userName) {
+ public byte[] getDecryptedAccountValue(String userName, String key) {
if (keyStore != null && userName != null) {
try {
KeyStore.PrivateKeyEntry privateKeyEntry = getUserKeyPair(userName);
- return getAccountSecretKey(userName, privateKeyEntry);
+ return getDecryptedAccountValue(userName, privateKeyEntry, key);
} catch (Exception e) {
Timber.e(e);
}
@@ -277,10 +277,10 @@ public byte[] getAccountSecretKey(String userName) {
return null;
}
- private byte[] getAccountSecretKey(String userName, KeyStore.PrivateKeyEntry privateKeyEntry) {
+ private byte[] getDecryptedAccountValue(String userName, KeyStore.PrivateKeyEntry privateKeyEntry, String key) {
if (privateKeyEntry != null) {
- String encryptedSecretKey = AccountHelper.getAccountManagerValue(AccountHelper.INTENT_KEY.ACCOUNT_SECRET_KEY, userName, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType());
+ String encryptedSecretKey = AccountHelper.getAccountManagerValue(key, userName, CoreLibrary.getInstance().getAccountAuthenticatorXml().getAccountType());
if (encryptedSecretKey != null) {
try {
@@ -293,6 +293,18 @@ private byte[] getAccountSecretKey(String userName, KeyStore.PrivateKeyEntry pri
return null;
}
+ public byte[] getDecryptedPreferenceValue(String userName) {
+ if (keyStore != null && userName != null) {
+ try {
+ KeyStore.PrivateKeyEntry privateKeyEntry = getUserKeyPair(userName);
+ return decryptString(privateKeyEntry, allSharedPreferences.getPassphrase(userName));
+ } catch (Exception e) {
+ Timber.e(e);
+ }
+ }
+ return null;
+ }
+
/**
* Checks whether the groupId for the provided user is the same as the first person to
* successfully login
@@ -305,8 +317,8 @@ public boolean isUserInPioneerGroup(String userName) {
if (userName.equals(pioneerUser)) {
return true;
} else {
- byte[] currentUserSecretKey = getAccountSecretKey(userName);
- byte[] pioneerUserSecretKey = getAccountSecretKey(pioneerUser);
+ byte[] currentUserSecretKey = getDecryptedPreferenceValue(userName);
+ byte[] pioneerUserSecretKey = getDecryptedPreferenceValue(pioneerUser);
if (currentUserSecretKey != null && Arrays.equals(pioneerUserSecretKey, currentUserSecretKey)) {
return isValidDBPassword(currentUserSecretKey);
@@ -354,7 +366,7 @@ public boolean localLoginWith(String userName) {
try {
- byte[] secretKey = getAccountSecretKey(userName);
+ byte[] secretKey = getDecryptedPreferenceValue(userName);
setupContextForLogin(secretKey);
if (!allSharedPreferences.fetchRegisteredANM().equalsIgnoreCase(userName)) {
@@ -595,8 +607,13 @@ public Bundle saveUserCredentials(String userName, char[] password, LoginRespons
bundle.putString(AccountHelper.INTENT_KEY.ACCOUNT_SECRET_KEY, encryptedSecretKey);
bundle.putString(AccountHelper.INTENT_KEY.ACCOUNT_PASSWORD_SALT, Base64.encodeToString(passwordHash.getSalt(), Base64.DEFAULT));
+ //Save encrypted passphrase
+ if (StringUtils.isBlank(allSharedPreferences.getPassphrase(userName))) {
+ allSharedPreferences.savePassphrase(username, encryptString(privateKeyEntry, SecurityHelper.toBytes(SecurityHelper.generateRandomPassphrase())));
+ }
+
// Finally, save the pioneer user
- if (allSharedPreferences.fetchPioneerUser() == null) {
+ if (StringUtils.isBlank(allSharedPreferences.fetchPioneerUser())) {
allSharedPreferences.savePioneerUser(username);
}
}
diff --git a/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java b/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java
index b91005bc0..a5b8bac5b 100644
--- a/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java
+++ b/opensrp-app/src/main/java/org/smartregister/view/activity/DrishtiApplication.java
@@ -14,6 +14,7 @@
import org.smartregister.Context;
import org.smartregister.CoreLibrary;
import org.smartregister.R;
+import org.smartregister.account.AccountHelper;
import org.smartregister.repository.DrishtiRepository;
import org.smartregister.repository.Repository;
import org.smartregister.sync.ClientProcessorForJava;
@@ -121,7 +122,7 @@ public byte[] getPassword() {
if (password == null) {
String username = context.userService().getAllSharedPreferences().fetchRegisteredANM();
- password = context.userService().getAccountSecretKey(username);
+ password = context.userService().getDecryptedPreferenceValue(username);
}
return password;
diff --git a/opensrp-app/src/test/java/org/smartregister/TestApplication.java b/opensrp-app/src/test/java/org/smartregister/TestApplication.java
index c2b4c43e2..77265acdd 100644
--- a/opensrp-app/src/test/java/org/smartregister/TestApplication.java
+++ b/opensrp-app/src/test/java/org/smartregister/TestApplication.java
@@ -40,8 +40,4 @@ public void setContext(Context context) {
this.context = context;
}
- @Override
- public char[] getPassword() {
- return new char[]{};
- }
}
diff --git a/opensrp-app/src/test/java/org/smartregister/repository/mock/RepositoryMock.java b/opensrp-app/src/test/java/org/smartregister/repository/mock/RepositoryMock.java
index 0afbd399f..eb6ba6699 100644
--- a/opensrp-app/src/test/java/org/smartregister/repository/mock/RepositoryMock.java
+++ b/opensrp-app/src/test/java/org/smartregister/repository/mock/RepositoryMock.java
@@ -50,7 +50,7 @@ public SQLiteDatabase getWritableDatabase() {
}
@Override
- public boolean canUseThisPassword(char[] password) {
+ public boolean canUseThisPassword(byte[] password) {
return super.canUseThisPassword(password);
}
diff --git a/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java b/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java
index bcb81d73f..2e105d44c 100644
--- a/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/service/UserServiceTest.java
@@ -1,8 +1,10 @@
package org.smartregister.service;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
@@ -24,6 +26,7 @@
import org.smartregister.repository.AllSettings;
import org.smartregister.repository.AllSharedPreferences;
import org.smartregister.repository.Repository;
+import org.smartregister.security.SecurityHelper;
import org.smartregister.sync.SaveANMLocationTask;
import org.smartregister.sync.SaveANMTeamTask;
import org.smartregister.sync.SaveUserInfoTask;
@@ -42,7 +45,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -51,6 +53,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
import static org.smartregister.AllConstants.ENGLISH_LOCALE;
import static org.smartregister.AllConstants.KANNADA_LOCALE;
@@ -92,14 +95,25 @@ public class UserServiceTest extends BaseUnitTest {
private LoginResponseData loginResponseData;
+ byte[] password = "Password Z".getBytes();
+
+ private String user = "johndoe";
+
+ @Mock
+ private DrishtiApplication drishtiApplication;
+
@Before
- public void setUp() throws Exception {
+ public void setUp() {
+ initMocks(this);
Whitebox.setInternalState(DrishtiApplication.getInstance(), "repository", repository);
- userService = new UserService(allSettings, allSharedPreferences, httpAgent, session, configuration, saveANMLocationTask, saveUserInfoTask, saveANMTeamTask);
+ when(configuration.getDrishtiApplication()).thenReturn(drishtiApplication);
+ doReturn(repository).when(drishtiApplication).getRepository();
+ userService = spy(new UserService(allSettings, allSharedPreferences, httpAgent, session, configuration, saveANMLocationTask, saveUserInfoTask, saveANMTeamTask));
userInfoJSON = "{\"locations\":{\"locationsHierarchy\":{\"map\":{\"cd4ed528-87cd-42ee-a175-5e7089521ebd\":{\"id\":\"cd4ed528-87cd-42ee-a175-5e7089521ebd\",\"label\":\"Pakistan\",\"node\":{\"locationId\":\"cd4ed528-87cd-42ee-a175-5e7089521ebd\",\"name\":\"Pakistan\",\"tags\":[\"Country\"],\"voided\":false},\"children\":{\"461f2be7-c95d-433c-b1d7-c68f272409d7\":{\"id\":\"461f2be7-c95d-433c-b1d7-c68f272409d7\",\"label\":\"Sindh\",\"node\":{\"locationId\":\"461f2be7-c95d-433c-b1d7-c68f272409d7\",\"name\":\"Sindh\",\"parentLocation\":{\"locationId\":\"cd4ed528-87cd-42ee-a175-5e7089521ebd\",\"name\":\"Pakistan\",\"voided\":false},\"tags\":[\"Province\"],\"voided\":false},\"children\":{\"a529e2fc-6f0d-4e60-a5df-789fe17cca48\":{\"id\":\"a529e2fc-6f0d-4e60-a5df-789fe17cca48\",\"label\":\"Karachi\",\"node\":{\"locationId\":\"a529e2fc-6f0d-4e60-a5df-789fe17cca48\",\"name\":\"Karachi\",\"parentLocation\":{\"locationId\":\"461f2be7-c95d-433c-b1d7-c68f272409d7\",\"name\":\"Sindh\",\"parentLocation\":{\"locationId\":\"cd4ed528-87cd-42ee-a175-5e7089521ebd\",\"name\":\"Pakistan\",\"voided\":false},\"voided\":false},\"tags\":[\"City\"],\"voided\":false},\"children\":{\"60c21502-fec1-40f5-b77d-6df3f92771ce\":{\"id\":\"60c21502-fec1-40f5-b77d-6df3f92771ce\",\"label\":\"Baldia\",\"node\":{\"locationId\":\"60c21502-fec1-40f5-b77d-6df3f92771ce\",\"name\":\"Baldia\",\"parentLocation\":{\"locationId\":\"a529e2fc-6f0d-4e60-a5df-789fe17cca48\",\"name\":\"Karachi\",\"parentLocation\":{\"locationId\":\"461f2be7-c95d-433c-b1d7-c68f272409d7\",\"name\":\"Sindh\",\"voided\":false},\"voided\":false},\"tags\":[\"Town\"],\"attributes\":{\"at1\":\"atttt1\"},\"voided\":false},\"parent\":\"a529e2fc-6f0d-4e60-a5df-789fe17cca48\"}},\"parent\":\"461f2be7-c95d-433c-b1d7-c68f272409d7\"}},\"parent\":\"cd4ed528-87cd-42ee-a175-5e7089521ebd\"}}}},\"parentChildren\":{\"cd4ed528-87cd-42ee-a175-5e7089521ebd\":[\"461f2be7-c95d-433c-b1d7-c68f272409d7\"],\"461f2be7-c95d-433c-b1d7-c68f272409d7\":[\"a529e2fc-6f0d-4e60-a5df-789fe17cca48\"],\"a529e2fc-6f0d-4e60-a5df-789fe17cca48\":[\"60c21502-fec1-40f5-b77d-6df3f92771ce\"]}}},\"user\":{\"username\":\"demotest\",\"roles\":[\"Provider\",\"Authenticated\",\"Thrive Member\"],\"permissions\":[\"Delete Reports\",\"Remove Allergies\",\"Edit Cohorts\",\"View Unpublished Forms\",\"Patient Dashboard - View Patient Summary\",\"Add Relationships\",\"Edit Problems\",\"Remove Problems\",\"Patient Dashboard - View Forms Section\",\"Manage Report Designs\",\"Add Patient Programs\",\"Delete Orders\",\"Manage Identifier Types\",\"Manage Person Attribute Types\",\"Add Patient Identifiers\",\"View Visit Types\",\"View Patients\",\"Delete Concept Proposals\",\"View Identifier Types\",\"Delete Encounters\",\"View Global Properties\",\"Edit Visits\",\"View Concept Map Types\",\"Add Users\",\"Delete Cohorts\",\"Manage Scheduled Report Tasks\",\"View Concepts\",\"Edit Concept Proposals\",\"Add Visits\",\"Edit Patient Programs\",\"Manage Concept Datatypes\",\"Manage Indicator Definitions\",\"View Concept Proposals\",\"Add Allergies\",\"Edit Allergies\",\"Delete Observations\",\"View Roles\",\"Manage Address Templates\",\"Configure Visits\",\"Manage Data Set Definitions\",\"View Concept Sources\",\"Patient Dashboard - View Regimen Section\",\"View Calculations\",\"Manage Encounter Roles\",\"Delete People\",\"Edit Report Objects\",\"View People\",\"Manage Concept Sources\",\"View Orders\",\"Manage Concept Map Types\",\"Delete Patient Programs\",\"Add Problems\",\"Edit People\",\"Manage Locations\",\"View Patient Programs\",\"View Field Types\",\"View Relationship Types\",\"Manage Visit Attribute Types\",\"Manage Order Types\",\"Manage TeamLocation Attribute Types\",\"Form Entry\",\"View Encounter Types\",\"View Encounter Roles\",\"Manage Programs\",\"Edit Reports\",\"View TeamLocation Attribute Types\",\"View Order Types\",\"Manage Relationship Types\",\"Manage Providers\",\"Manage Reports\",\"Manage Concept Classes\",\"Add Concept Proposals\",\"View Patient Identifiers\",\"View Privileges\",\"View Data Entry Statistics\",\"Patient Dashboard - View Graphs Section\",\"Manage Tokens\",\"Add Reports\",\"View Forms\",\"View Administration Functions\",\"Manage Relationships\",\"View Observations\",\"View Team\",\"Add Observations\",\"View Member\",\"View Report Objects\",\"Edit Relationships\",\"View Relationships\",\"Manage Scheduler\",\"View Allergies\",\"View Concept Reference Terms\",\"View Encounters\",\"Edit Patient Identifiers\",\"Edit Observations\",\"Delete Patients\",\"Delete Patient Identifiers\",\"View Person Attribute Types\",\"Add Encounters\",\"View Problems\",\"Manage FormEntry XSN\",\"View Visits\",\"Edit Team\",\"Manage Field Types\",\"Patient Dashboard - View Encounters Section\",\"Add Team\",\"Add Cohorts\",\"Add Patients\",\"Patient Dashboard - View Demographics Section\",\"Manage Concepts\",\"View Rule Definitions\",\"Add Orders\",\"Manage Visit Types\",\"Patient Dashboard - View Visits Section\",\"View Locations\",\"Manage Forms\",\"Edit Encounters\",\"Delete Relationships\",\"Manage Concept Reference Terms\",\"Add Report Objects\",\"Manage Alerts\",\"View Users\",\"Edit Patients\",\"Manage Concept Stop Words\",\"View Concept Classes\",\"View Patient Cohorts\",\"View Visit Attribute Types\",\"Manage TeamLocation Tags\",\"Manage Encounter Types\",\"View Concept Datatypes\",\"View Navigation Menu\",\"Delete Visits\",\"Add People\",\"Edit Orders\",\"Manage Concept Name tags\",\"Run Reports\",\"View Providers\",\"Patient Dashboard - View Overview Section\",\"Manage Cohort Definitions\",\"View Reports\",\"View Programs\",\"Delete Report Objects\",\"Manage Report Definitions\"],\"baseEntityId\":\"6637559e-ebf9-480a-9731-c47e16e95716\",\"preferredName\":\"Demo test User\",\"voided\":false},\"time\":{\"time\":\"2018-03-02 10:17:51\",\"timeZone\":\"Africa/Harare\"},\"team\":{\"identifier\":\"12345678\",\"person\":{\"gender\":\"F\",\"display\":\"MOH ZEIR Demo\",\"resourceVersion\":\"1.11\",\"dead\":false,\"uuid\":\"12481a02-9a78-4c45-9ead-ddf24d14b19d\",\"birthdateEstimated\":false,\"deathdateEstimated\":false,\"attributes\":[],\"voided\":false,\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/person/12481a02-9a78-4c45-9ead-ddf24d14b19d\"},{\"rel\":\"full\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/person/12481a02-9a78-4c45-9ead-ddf24d14b19d?v\\u003dfull\"}],\"preferredName\":{\"display\":\"MOH ZEIR Demo\",\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/person/12481a02-9a78-4c45-9ead-ddf24d14b19d/name/4ab4a8b9-3723-44a8-8733-815ee6d05ef7\"}],\"uuid\":\"4ab4a8b9-3723-44a8-8733-815ee6d05ef7\"}},\"teamMemberId\":1.0,\"patients\":[],\"resourceVersion\":\"1.8\",\"location\":[{\"parentLocation\":{\"display\":\"Fort Jameson\",\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/location/25089a50-0cf0-47e8-8bfe-fecabed92530\"}],\"uuid\":\"25089a50-0cf0-47e8-8bfe-fecabed92530\"},\"display\":\"Happy Kids Clinic\",\"resourceVersion\":\"1.9\",\"uuid\":\"42abc582-6658-488b-922e-7be500c070f3\",\"tags\":[{\"display\":\"Health Centre Urban\",\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/locationtag/86c5e41b-08f0-495d-9130-170556c05041\"}],\"uuid\":\"86c5e41b-08f0-495d-9130-170556c05041\"},{\"display\":\"Health Facility\",\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/locationtag/4d9fce9d-c83a-46aa-b1d9-121da6176758\"}],\"uuid\":\"4d9fce9d-c83a-46aa-b1d9-121da6176758\"}],\"name\":\"Happy Kids Clinic\",\"retired\":false,\"attributes\":[{\"display\":\"dhis_ou_id: k2SgIKwkSh1\",\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/location/42abc582-6658-488b-922e-7be500c070f3/attribute/01ec1f7c-e061-4f37-9d2c-ce1c7fe99c36\"}],\"uuid\":\"01ec1f7c-e061-4f37-9d2c-ce1c7fe99c36\"}],\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/location/42abc582-6658-488b-922e-7be500c070f3\"},{\"rel\":\"full\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/location/42abc582-6658-488b-922e-7be500c070f3?v\\u003dfull\"}],\"childLocations\":[{\"display\":\"Happy Kids Clinic: Zone 1\",\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/location/42b88545-7ebb-4e11-8d1a-3d3a924c8af4\"}],\"uuid\":\"42b88545-7ebb-4e11-8d1a-3d3a924c8af4\"},{\"display\":\"Happy Kids Clinic: Zone 2\",\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/location/8a40cd7e-b8d4-4c6e-b88f-a77272fec630\"}],\"uuid\":\"8a40cd7e-b8d4-4c6e-b88f-a77272fec630\"},{\"display\":\"Happy Kids Clinic: Zone 3\",\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/location/5e79ae00-5a69-4814-aace-30e4717f823a\"}],\"uuid\":\"5e79ae00-5a69-4814-aace-30e4717f823a\"},{\"display\":\"Happy Kids Clinic: Zone 4\",\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/location/e79ff5bc-b6ff-46bc-9bbf-0cedc7d6c4c7\"}],\"uuid\":\"e79ff5bc-b6ff-46bc-9bbf-0cedc7d6c4c7\"}]}],\"team\":{\"teamName\":\"Demo\",\"dateCreated\":\"2017-04-06T09:21:39.000+0200\",\"display\":\"Demo\",\"resourceVersion\":\"1.8\",\"location\":{\"parentLocation\":{\"display\":\"Fort Jameson\",\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/location/25089a50-0cf0-47e8-8bfe-fecabed92530\"}],\"uuid\":\"25089a50-0cf0-47e8-8bfe-fecabed92530\"},\"display\":\"Happy Kids Clinic\",\"resourceVersion\":\"1.9\",\"uuid\":\"42abc582-6658-488b-922e-7be500c070f3\",\"tags\":[{\"display\":\"Health Centre Urban\",\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/locationtag/86c5e41b-08f0-495d-9130-170556c05041\"}],\"uuid\":\"86c5e41b-08f0-495d-9130-170556c05041\"},{\"display\":\"Health Facility\",\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/locationtag/4d9fce9d-c83a-46aa-b1d9-121da6176758\"}],\"uuid\":\"4d9fce9d-c83a-46aa-b1d9-121da6176758\"}],\"name\":\"Happy Kids Clinic\",\"retired\":false,\"attributes\":[{\"display\":\"dhis_ou_id: k2SgIKwkSh1\",\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/location/42abc582-6658-488b-922e-7be500c070f3/attribute/01ec1f7c-e061-4f37-9d2c-ce1c7fe99c36\"}],\"uuid\":\"01ec1f7c-e061-4f37-9d2c-ce1c7fe99c36\"}],\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/location/42abc582-6658-488b-922e-7be500c070f3\"},{\"rel\":\"full\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/location/42abc582-6658-488b-922e-7be500c070f3?v\\u003dfull\"}],\"childLocations\":[{\"display\":\"Happy Kids Clinic: Zone 1\",\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/location/42b88545-7ebb-4e11-8d1a-3d3a924c8af4\"}],\"uuid\":\"42b88545-7ebb-4e11-8d1a-3d3a924c8af4\"},{\"display\":\"Happy Kids Clinic: Zone 2\",\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/location/8a40cd7e-b8d4-4c6e-b88f-a77272fec630\"}],\"uuid\":\"8a40cd7e-b8d4-4c6e-b88f-a77272fec630\"},{\"display\":\"Happy Kids Clinic: Zone 3\",\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/location/5e79ae00-5a69-4814-aace-30e4717f823a\"}],\"uuid\":\"5e79ae00-5a69-4814-aace-30e4717f823a\"},{\"display\":\"Happy Kids Clinic: Zone 4\",\"links\":[{\"rel\":\"self\",\"uri\":\"http://openmrs.zeir-stage.smartregister.org/openmrs/ws/rest/v1/location/e79ff5bc-b6ff-46bc-9bbf-0cedc7d6c4c7\"}],\"uuid\":\"e79ff5bc-b6ff-46bc-9bbf-0cedc7d6c4c7\"}]},\"teamIdentifier\":\"Demo\",\"uuid\":\"7bfb4bb3-2689-404c-a5d4-f5cbe1aea9c4\"},\"isTeamLead\":true,\"uuid\":\"6ea953fb-46a2-4415-ae53-299ce909894b\"}}";
loginResponseData = AssetHandler.jsonStringToJava(userInfoJSON, LoginResponseData.class);
}
+
@Test
public void shouldUseHttpAgentToDoRemoteLoginCheck() {
@@ -107,7 +121,7 @@ public void shouldUseHttpAgentToDoRemoteLoginCheck() {
User userObject = new User();
userObject.setUsername("user");
- userObject.setPassword("password Y");
+ userObject.setPassword(SecurityHelper.toChars(password));
userInfo.user = userObject;
LoginResponse loginResponse = LoginResponse.SUCCESS.withPayload(userInfo);
@@ -115,18 +129,18 @@ public void shouldUseHttpAgentToDoRemoteLoginCheck() {
when(configuration.dristhiBaseURL()).thenReturn("http://dristhi_base_url");
String httpAuthenticateUrl = "http://dristhi_base_url/security/authenticate";
String user = "user";
- String password = "password Y";
+ char[] password = "password Y".toCharArray();
when(httpAgent.urlCanBeAccessWithGivenCredentials(
httpAuthenticateUrl,
user,
- password.toCharArray())).thenReturn(loginResponse);
+ password)).thenReturn(loginResponse);
when(allSharedPreferences.fetchRegisteredANM()).thenReturn("user");
- userService.isValidRemoteLogin(user, password.toCharArray());
+ userService.isValidRemoteLogin(user, password);
- verify(httpAgent).urlCanBeAccessWithGivenCredentials(httpAuthenticateUrl, user, password.toCharArray());
+ verify(httpAgent).urlCanBeAccessWithGivenCredentials(httpAuthenticateUrl, user, password);
}
@Test
@@ -163,19 +177,19 @@ public void shouldSaveANMLocation() {
public void shouldConsiderALocalLoginValid() {
// When Username Matches Registered User And Password Matches The One In DB
when(allSharedPreferences.fetchRegisteredANM()).thenReturn("ANM X");
- when(repository.canUseThisPassword("password Z".toCharArray())).thenReturn(true);
+ when(repository.canUseThisPassword(password)).thenReturn(true);
- assertTrue(userService.isValidLocalLogin("ANM X", "password Z".toCharArray()));
+ assertTrue(userService.isValidLocalLogin("ANM X", password));
verify(allSharedPreferences).fetchRegisteredANM();
- verify(repository).canUseThisPassword("password Z".toCharArray());
+ verify(repository).canUseThisPassword(password);
}
@Test
public void shouldConsiderALocalLoginInvalidWhenRegisteredUserDoesNotMatch() {
when(allSharedPreferences.fetchRegisteredANM()).thenReturn("ANM X");
- assertFalse(userService.isValidLocalLogin("SOME OTHER ANM", "password".toCharArray()));
+ assertFalse(userService.isValidLocalLogin("SOME OTHER ANM", "password".getBytes()));
verify(allSharedPreferences).fetchRegisteredANM();
verifyZeroInteractions(repository);
@@ -184,34 +198,36 @@ public void shouldConsiderALocalLoginInvalidWhenRegisteredUserDoesNotMatch() {
@Test
public void shouldConsiderALocalLoginInvalidWhenRegisteredUserMatchesButNotThePassword() {
when(allSharedPreferences.fetchRegisteredANM()).thenReturn("ANM X");
- when(repository.canUseThisPassword("password Z".toCharArray())).thenReturn(false);
+ when(repository.canUseThisPassword(password)).thenReturn(false);
- assertFalse(userService.isValidLocalLogin("ANM X", "password Z".toCharArray()));
+ assertFalse(userService.isValidLocalLogin("ANM X", password));
verify(allSharedPreferences).fetchRegisteredANM();
- verify(repository).canUseThisPassword("password Z".toCharArray());
+ verify(repository).canUseThisPassword(password);
}
@Test
public void shouldRegisterANewUser() {
- when(configuration.getDrishtiApplication()).thenReturn(new DrishtiApplication() {
- @Override
- public void logoutCurrentUser() {
- // Nothing to cleanup
- }
- });
+ when(configuration.getDrishtiApplication()).thenReturn(drishtiApplication);
LoginResponseData userInfo = new LoginResponseData();
+ String newUsername = "user X";
+
User user = new User();
- user.setUsername("user X");
- user.setPassword("password Y");
+ user.setUsername(newUsername);
+ user.setPassword("password Y".toCharArray());
userInfo.user = user;
- when(allSharedPreferences.fetchRegisteredANM()).thenReturn("user X");
+ when(allSharedPreferences.fetchRegisteredANM()).thenReturn("user Z");
+ ArgumentCaptor usernameCaptor = ArgumentCaptor.forClass(String.class);
+
+ userService.processLoginResponseDataForUser(newUsername, userInfo);
- userService.processLoginResponseDataForUser("user X", userInfo);
+ verify(allSharedPreferences).updateANMUserName(usernameCaptor.capture());
+ String value = usernameCaptor.getValue();
+ Assert.assertNotNull(value);
+ Assert.assertEquals(newUsername, value);
- verify(allSettings).registerANM("user X");
}
@Test
@@ -316,7 +332,7 @@ public void testValidateDeviceTimeSameTimeTimeAndTimeZone() {
public void testValidateStoredServerTimeZoneForNullServerTimeZoneReturnsError() {
when(allSharedPreferences.fetchServerTimeZone()).thenReturn(null);
assertEquals(TimeStatus.ERROR, userService.validateStoredServerTimeZone());
- verify(allSharedPreferences).saveForceRemoteLogin(true);
+ verify(allSharedPreferences).saveForceRemoteLogin(true, user);
}
@Test
@@ -325,7 +341,7 @@ public void testValidateStoredServerTimeZoneForDifferentTimeZoneServerTimeZoneRe
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
assertEquals(TimeStatus.TIMEZONE_MISMATCH, userService.validateStoredServerTimeZone());
- verify(allSharedPreferences).saveForceRemoteLogin(true);
+ verify(allSharedPreferences).saveForceRemoteLogin(true, user);
}
@@ -334,7 +350,7 @@ public void testValidateStoredServerTimeZoneForSameTimeTimeAndTimeZone() {
when(allSharedPreferences.fetchServerTimeZone()).thenReturn("Africa/Nairobi");
TimeZone.setDefault(TimeZone.getTimeZone("Africa/Nairobi"));
assertEquals(TimeStatus.OK, userService.validateStoredServerTimeZone());
- verify(allSharedPreferences, never()).saveForceRemoteLogin(true);
+ verify(allSharedPreferences, never()).saveForceRemoteLogin(true, user);
}
@@ -348,19 +364,13 @@ public void testIsUserInValidGroupForValidUserAndPassword() throws Exception {
Whitebox.setInternalState(userService, "keyStore", keyStore);
Whitebox.setInternalState(keyStore, "initialized", true);
Whitebox.setInternalState(keyStore, "keyStoreSpi", keyStoreSpi);
- String user = "johndoe";
when(keyStore.containsAlias(user)).thenReturn(true);
KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class);
when(keyStore.getEntry(user, null)).thenReturn(privateKeyEntry);
- String password = UUID.randomUUID().toString();
- when(allSharedPreferences.fetchEncryptedPassword(user)).thenReturn(password);
- when(allSharedPreferences.fetchEncryptedGroupId(user)).thenReturn(password);
userService = spy(userService);
- doReturn(password).when(userService).decryptString(privateKeyEntry, password);
+ doReturn(password).when(userService).decryptString(privateKeyEntry, "RandomSECURE_TEXT");
when(repository.canUseThisPassword(password)).thenReturn(true);
- assertTrue(userService.isUserInValidGroup(user, password));
- verify(allSharedPreferences).fetchEncryptedPassword(user);
- verify(allSharedPreferences).fetchEncryptedGroupId(user);
+ assertTrue(userService.isUserInValidGroup(user, SecurityHelper.toChars(password)));
verify(repository).canUseThisPassword(password);
}
@@ -375,38 +385,11 @@ public void testIsUserInValidGroupShouldReturnFalseOnError() throws Exception {
KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class);
when(keyStore.getEntry(user, null)).thenReturn(privateKeyEntry);
String password = UUID.randomUUID().toString();
- when(allSharedPreferences.fetchEncryptedPassword(user)).thenReturn(password);
- when(allSharedPreferences.fetchEncryptedGroupId(user)).thenReturn(password);
- assertFalse(userService.isUserInValidGroup(user, password));
- verify(allSharedPreferences).fetchEncryptedPassword(user);
- verify(allSharedPreferences, never()).fetchEncryptedGroupId(user);
+ assertFalse(userService.isUserInValidGroup(user, password.toCharArray()));
+ // verify(allSharedPreferences, never()).fetchEncryptedGroupId(user);
verifyZeroInteractions(repository);
}
- @Test
- public void testGetGroupIdShouldReturnNullOnError() throws Exception {
- Whitebox.setInternalState(userService, "keyStore", keyStore);
- Whitebox.setInternalState(keyStore, "initialized", true);
- Whitebox.setInternalState(keyStore, "keyStoreSpi", keyStoreSpi);
- assertNull(userService.getGroupId("johndoe"));
- }
-
- @Test
- public void testGetGroupIdShouldReturnGroupId() throws Exception {
- userService = spy(userService);
- Whitebox.setInternalState(userService, "keyStore", keyStore);
- Whitebox.setInternalState(keyStore, "initialized", true);
- Whitebox.setInternalState(keyStore, "keyStoreSpi", keyStoreSpi);
- String password = UUID.randomUUID().toString();
- String user = "johndoe";
- when(keyStore.containsAlias(user)).thenReturn(true);
- KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class);
- when(keyStore.getEntry(user, null)).thenReturn(privateKeyEntry);
- when(allSharedPreferences.fetchEncryptedGroupId(user)).thenReturn(password);
- doReturn("pass123").when(userService).decryptString(privateKeyEntry, password);
- assertEquals("pass123", userService.getGroupId(user));
- }
-
@Test
public void testIsUserInPioneerGroupShouldReturnTrueForPioneerUser() throws Exception {
userService = spy(userService);
@@ -418,7 +401,6 @@ public void testIsUserInPioneerGroupShouldReturnTrueForPioneerUser() throws Exce
when(keyStore.containsAlias(user)).thenReturn(true);
KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class);
when(keyStore.getEntry(user, null)).thenReturn(privateKeyEntry);
- when(allSharedPreferences.fetchEncryptedGroupId(user)).thenReturn(password);
when(allSharedPreferences.fetchPioneerUser()).thenReturn(user);
assertTrue(userService.isUserInPioneerGroup(user));
}
@@ -430,4 +412,4 @@ public void testIsUserInPioneerGroupShouldReturnFalseForOthers() throws Exceptio
}
-}
+}
\ No newline at end of file
diff --git a/opensrp-app/src/test/java/org/smartregister/view/activity/DrishtiApplicationTest.java b/opensrp-app/src/test/java/org/smartregister/view/activity/DrishtiApplicationTest.java
index 2f651d613..736fe045e 100644
--- a/opensrp-app/src/test/java/org/smartregister/view/activity/DrishtiApplicationTest.java
+++ b/opensrp-app/src/test/java/org/smartregister/view/activity/DrishtiApplicationTest.java
@@ -81,7 +81,7 @@ public void getPassword() {
AllSharedPreferences allSharedPreferences = Mockito.spy(drishtiApplication.getContext().userService().getAllSharedPreferences());
ReflectionHelpers.setField(drishtiApplication.getContext().userService(), "allSharedPreferences", allSharedPreferences);
Mockito.doReturn(username).when(allSharedPreferences).fetchRegisteredANM();
- Mockito.doReturn(password).when(userService).getAccountSecretKey(Mockito.eq(username));
+ Mockito.doReturn(password).when(userService).getDecryptedPreferenceValue(Mockito.eq(username));
Assert.assertEquals(password, drishtiApplication.getPassword());
}
From 95cf026eff890437a2845935d96151f47c3aca6e Mon Sep 17 00:00:00 2001
From: Martin Ndegwa