Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ConnectID Initial Implementation #2660

Merged
merged 79 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
6153242
Initial work on ConnectId UI and some intro logic. Working toward POC.
OrangeAndGreen Mar 2, 2023
466026c
Got rid of locked state for Connect ID (only logged out or logged in).
OrangeAndGreen Mar 10, 2023
06f9936
Renamed some files for consistency.
OrangeAndGreen Mar 30, 2023
6d7b527
Changes to initial ConnectID create/recovery workflow.
OrangeAndGreen Apr 6, 2023
87c6d0f
A few look-and-feel changes
OrangeAndGreen Apr 12, 2023
1bfa868
Merge remote-tracking branch 'origin/master' into dv/connect_id
OrangeAndGreen Apr 12, 2023
d16af33
Disabling ConnectID functionality by default.
OrangeAndGreen Apr 12, 2023
a60a863
Changed min SDK to 23.
OrangeAndGreen Apr 24, 2023
e2cb6cb
Added activity for getting Connect recovery vs. new account decision.
OrangeAndGreen Apr 27, 2023
f07b4ed
Fixed recently introduced crash after server error on Connect registr…
OrangeAndGreen Apr 28, 2023
5bd6747
Remembering registration phase to resume after app interruption.
OrangeAndGreen May 2, 2023
0cf3b66
Icons instead of highlighted text on two pages.
OrangeAndGreen May 3, 2023
52b1bb7
ConnectID V1 development, including several new activities.
OrangeAndGreen Jun 8, 2023
e5590c6
Added ConnectID unlock functionality as a developer option.
OrangeAndGreen Jun 9, 2023
db457b4
Improved ConnectID DB encryption using Android KeyStore.
OrangeAndGreen Jun 9, 2023
23881d8
Quick fix: Changed a key received from the server.
OrangeAndGreen Jun 9, 2023
303cac5
Fixed bug where password sometimes wouldn't auto-fill when it should.
OrangeAndGreen Jun 14, 2023
74a21d6
Merge remote-tracking branch 'origin/master' into dv/connect_id
OrangeAndGreen Jun 14, 2023
ab4e140
Added two new externalizable classes to list for FormStorageTest (fix…
OrangeAndGreen Jun 15, 2023
64ca2d2
Added one more externalizable class to FormStorageTest list.
OrangeAndGreen Jun 15, 2023
4db49b5
Added phone number checking to recovery decision page before allowing…
OrangeAndGreen Jun 19, 2023
a0ce2ea
Moved a bunch of ConnectID-related strings from android_translatable_…
OrangeAndGreen Jun 19, 2023
2c35dfb
Added some (temporary) error reporting to API call that checks if pho…
OrangeAndGreen Jun 20, 2023
78bcb6a
Styling improvements.
OrangeAndGreen Jun 21, 2023
88e094a
Solved DB passphrase encryption for API 21 and lower.
OrangeAndGreen Jun 23, 2023
1cca84f
Showing ConnectID page titles in blue bar at top of screen.
OrangeAndGreen Jun 27, 2023
3fc9f5a
Added AppSelectActivity initial implementation (not using yet).
OrangeAndGreen Jul 11, 2023
46aa87f
Show sign in/up if user is logged out
shubham1g5 Jul 11, 2023
8069967
don't set parent activity null on forget user as it causes crash when…
shubham1g5 Jul 11, 2023
a1c37f4
Merge branch 'master' into dv/connect_id
shubham1g5 Jul 11, 2023
371d2d2
Showing ConnectID sign-in menu option when user resumes registration …
OrangeAndGreen Jul 11, 2023
6d7d1b8
Merge branch 'dv/connect_id' of https://github.com/dimagi/commcare-an…
OrangeAndGreen Jul 23, 2023
57a8178
2-minute countdown between OTP requests.
OrangeAndGreen Jul 24, 2023
7a436c2
Disabling biometric config buttons when already configured.
OrangeAndGreen Jul 25, 2023
16521f2
BROKEN: Attempting to use getRecordsForValues to query app_data records.
OrangeAndGreen Jul 26, 2023
e0c18e8
Fixed bug using getRecordsForValues
OrangeAndGreen Jul 26, 2023
44ec36a
fix null check for buildProfileID
shubham1g5 Jul 26, 2023
a3c66aa
In progress: SSO functionality
OrangeAndGreen Jul 31, 2023
457ef9e
Changed UI and some workflow for biometrics configuration.
OrangeAndGreen Aug 1, 2023
ced400a
Logs connect signins
shubham1g5 Aug 2, 2023
993b8e9
set ccc_enabled as a user property
shubham1g5 Aug 2, 2023
68747e0
logs connect id account recovery attempts
shubham1g5 Aug 2, 2023
7774ea7
Logs ccc sign out and normal login clicks
shubham1g5 Aug 3, 2023
8803140
Merge pull request #2687 from dimagi/connectAnalytics
OrangeAndGreen Aug 3, 2023
ad3aa57
Changed SSO token retrieval to a sync process, and calling it asynchr…
OrangeAndGreen Aug 3, 2023
154a5be
Merge branch 'dv/connect_id' of https://github.com/dimagi/commcare-an…
OrangeAndGreen Aug 3, 2023
682f3ee
Completed initial SSO workflow.
OrangeAndGreen Aug 3, 2023
138f746
make event params unique
shubham1g5 Aug 8, 2023
dfd8fdc
More attempted fixes for earlier Android versions using PIN for biome…
OrangeAndGreen Aug 8, 2023
9a46ac9
Addressed several comments from PR 2660.
OrangeAndGreen Aug 9, 2023
6345b66
Merge pull request #2688 from dimagi/analyticsUniqueParams
shubham1g5 Aug 10, 2023
566078e
Showing a toast when user presses Continue button twice while network…
OrangeAndGreen Aug 11, 2023
f80e233
Merge branch 'dv/connect_id' of https://github.com/dimagi/commcare-an…
OrangeAndGreen Aug 11, 2023
f57970a
Changes to address PR comments.
OrangeAndGreen Aug 14, 2023
6ffa2f6
Refactored encryption methods slightly for easier testing.
OrangeAndGreen Aug 15, 2023
63f234d
Added unit test for encryption/decryption using RSA key pair.
OrangeAndGreen Aug 15, 2023
c718bf7
Applied CommCare coding style to all new ConnectID code.
OrangeAndGreen Aug 15, 2023
d4bf1b7
Better singleton loading for ConnectIDNetworkHelper.
OrangeAndGreen Aug 16, 2023
525b332
Addressing comments for PR.
OrangeAndGreen Aug 21, 2023
d88d97d
Tying transformation string to existing key in Keystore (so app can s…
OrangeAndGreen Aug 21, 2023
de32648
Showing progress dialogs for ConnectID network calls.
OrangeAndGreen Aug 21, 2023
3ca59c3
Added some missing strings (from conflict).
OrangeAndGreen Aug 22, 2023
0661af5
Merge branch 'master' into dv/connect_id
OrangeAndGreen Aug 22, 2023
bf11aa2
Removed extra API check and suppressed code warning.
OrangeAndGreen Aug 23, 2023
c4eb7bf
Merge branch 'dv/connect_id' of https://github.com/dimagi/commcare-an…
OrangeAndGreen Aug 23, 2023
f8724f0
Reverted KeyAndTransform from record back to simple helper class (cau…
OrangeAndGreen Aug 23, 2023
be096bb
Merge branch 'master' of https://github.com/dimagi/commcare-android i…
OrangeAndGreen Aug 23, 2023
28c4610
Refactored encryption key code into EncryptionKeyProvider class and e…
OrangeAndGreen Aug 23, 2023
b3aa48e
Assigning default encryptionKeyProvider immediately (child can overri…
OrangeAndGreen Aug 23, 2023
5f53442
Creating MockEncryptionKeyProvider after super.onCreate for CommCareT…
OrangeAndGreen Aug 23, 2023
4bb727e
Addressing lint warnings.
OrangeAndGreen Aug 23, 2023
ee16434
Addressing more lint warnings, mostly code formatting and adding Java…
OrangeAndGreen Aug 23, 2023
f548a4a
Added javadoc descriptions
OrangeAndGreen Aug 23, 2023
b5b60e7
Broke some long lines into multiple
OrangeAndGreen Aug 23, 2023
d6719e3
Addressing remaining lint warnings in ConnectIDManager.
OrangeAndGreen Aug 24, 2023
8855e6b
Cleaning up more lint warnings.
OrangeAndGreen Aug 24, 2023
6409159
Addressing more lint warnings.
OrangeAndGreen Aug 24, 2023
b8d8997
Renamed all ConnectID* classes to ConnectId*
OrangeAndGreen Aug 24, 2023
98c0a6f
Fixed more lint warnings.
OrangeAndGreen Aug 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ configurations {
}

dependencies {
implementation 'androidx.security:security-crypto:1.0.0'
testImplementation 'junit:junit:4.12'
testImplementation('org.robolectric:robolectric:4.8.2') {
exclude(group: 'org.bouncycastle', module: 'bcprov-jdk15on')
Expand Down Expand Up @@ -249,7 +250,7 @@ android {
}

defaultConfig {
minSdkVersion 16
minSdkVersion 23
OrangeAndGreen marked this conversation as resolved.
Show resolved Hide resolved
targetSdkVersion 31
multiDexEnabled true
applicationId "org.commcare.dalvik"
Expand Down
109 changes: 87 additions & 22 deletions app/src/org/commcare/activities/ConnectIDManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,26 @@
import android.content.SharedPreferences;
import android.widget.Toast;

import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteException;

import org.commcare.CommCareApplication;
import org.commcare.android.database.connect.models.ConnectUserRecord;
import org.commcare.core.network.AuthInfo;
import org.commcare.models.database.AndroidDbHelper;
import org.commcare.models.database.MigrationException;
import org.commcare.models.database.SqlStorage;
import org.commcare.models.database.connect.DatabaseConnectOpenHelper;
import org.commcare.modern.database.Table;
import org.commcare.utils.EncryptionUtils;
import org.commcare.views.dialogs.DialogChoiceItem;
import org.commcare.views.dialogs.PaneledChoiceDialog;
import org.javarosa.core.services.locale.Localization;
import org.javarosa.core.services.storage.Persistable;

public class ConnectIDManager {
//ConnectID UI elements hidden from user when this is set to false
public static final boolean ENABLE_CONNECT_ID = false;
public static final boolean ENABLE_CONNECT_ID = true;

public enum ConnectIDStatus {
NotIntroduced,
Expand All @@ -41,15 +52,24 @@ public interface ConnectActivityCompleteListener {
private static final int CONNECT_PICTURES_ACTIVITY = 1005;
private static final int CONNECT_PHONE_VERIFY_ACTIVITY = 1006;

private static final int STATE_UNINSTALLED = 0;
private static final int STATE_READY = 2;
public static final int STATE_CORRUPTED = 4;
public static final int STATE_LEGACY_DETECTED = 8;
public static final int STATE_MIGRATION_FAILED = 16;
public static final int STATE_MIGRATION_QUESTIONABLE = 32;


private static ConnectIDManager manager = null;
private final Object connectDbHandleLock = new Object();
private SQLiteDatabase connectDatabase;
// private int dbState;
private ConnectIDStatus connectStatus = ConnectIDStatus.NotIntroduced;
private CommCareActivity<?> parentActivity;
private ConnectActivityCompleteListener loginListener;
private ConnectActivityCompleteListener registrationListener;
private RegistrationPhase phase = RegistrationPhase.Initial;

private ConnectIDUser user = null;

private ConnectIDManager() {
}

Expand All @@ -61,27 +81,71 @@ private static ConnectIDManager getInstance() {
return manager;
}

public static void loadUserFromPreferences() {
ConnectIDManager manager= getInstance();
SharedPreferences prefs = CommCareApplication.instance().getCurrentApp().getAppPreferences();
manager.user = ConnectIDUser.getUserFromPreferences(prefs);
if(manager.user != null && manager.connectStatus == ConnectIDStatus.NotIntroduced) {
public static void init(CommCareActivity<?> parent) {
ConnectIDManager manager = getInstance();
manager.parentActivity = parent;
manager.initConnectDb();

ConnectUserRecord user = getUser();
if(user != null && manager.connectStatus == ConnectIDStatus.NotIntroduced) {
manager.connectStatus = ConnectIDStatus.LoggedOut;
}
}

public static void storeUserInPreferences() {
SharedPreferences prefs = CommCareApplication.instance().getCurrentApp().getAppPreferences();
ConnectIDUser.storeUserInPreferences(getInstance().user, prefs);
private int initConnectDb() {
SQLiteDatabase database;
try {
database = new DatabaseConnectOpenHelper(parentActivity).getWritableDatabase(EncryptionUtils.GetConnectDbPassphrase(parentActivity));
database.close();
return STATE_READY;
} catch (SQLiteException e) {
// Only thrown if DB isn't there
return STATE_UNINSTALLED;
} catch (MigrationException e) {
if (e.isDefiniteFailure()) {
return STATE_MIGRATION_FAILED;
} else {
return STATE_MIGRATION_QUESTIONABLE;
}
}
}

public static void loadUserFromIntent(Intent intent) {
getInstance().user = ConnectIDUser.getUserFromIntent(intent);
storeUserInPreferences();
public <T extends Persistable> SqlStorage<T> getConnectStorage(Class<T> c) {
return getConnectStorage(c.getAnnotation(Table.class).value(), c);
}

public <T extends Persistable> SqlStorage<T> getConnectStorage(String table, Class<T> c) {
return new SqlStorage<>(table, c, new AndroidDbHelper(parentActivity.getApplicationContext()) {
@Override
public SQLiteDatabase getHandle() {
synchronized (connectDbHandleLock) {
if (connectDatabase == null || !connectDatabase.isOpen()) {
connectDatabase = new DatabaseConnectOpenHelper(this.c).getWritableDatabase(EncryptionUtils.GetConnectDbPassphrase(parentActivity));
}
return connectDatabase;
}
}
});
}

public static ConnectIDUser getUser() {
return getInstance().user;
public static ConnectUserRecord getUser() {
ConnectUserRecord user = null;
ConnectIDManager manager= getInstance();
for (ConnectUserRecord r : manager.getConnectStorage(ConnectUserRecord.class)) {
user = r;
break;
}

return user;
}

public static void storeUser(ConnectUserRecord user) {
getInstance().getConnectStorage(ConnectUserRecord.class).write(user);
}

public static void loadUserFromIntent(Intent intent) {
ConnectUserRecord user = ConnectUserRecord.getUserFromIntent(intent);
storeUser(user);
}

public static boolean isConnectIDIntroduced() {
Expand Down Expand Up @@ -174,9 +238,9 @@ public static void signOut() {
}

public static void forgetUser() {
getInstance().connectStatus = ConnectIDStatus.NotIntroduced;
getInstance().user = null;
storeUserInPreferences();
ConnectIDManager manager = getInstance();
manager.connectStatus = ConnectIDStatus.NotIntroduced;
manager.getConnectStorage(ConnectUserRecord.class).remove(getUser().getID());
}

public static void beginRegistrationWorkflow(CommCareActivity<?> activity, ConnectActivityCompleteListener listener) {
Expand Down Expand Up @@ -255,9 +319,10 @@ public static void handleFinishedActivity(int requestCode, int resultCode, Inten
if(nextActivity != null) {
Intent i = new Intent(manager.parentActivity, nextActivity);

if(manager.user != null) {
i.putExtra(ConnectIDPhoneVerificationActivity.USERNAME, manager.user.Username);
i.putExtra(ConnectIDPhoneVerificationActivity.PASSWORD, manager.user.Password);
ConnectUserRecord user = getUser();
if(user != null) {
i.putExtra(ConnectIDPhoneVerificationActivity.USERNAME, user.getUserID());
i.putExtra(ConnectIDPhoneVerificationActivity.PASSWORD, user.getPassword());
}

manager.parentActivity.startActivityForResult(i, nextRequestCode);
Expand Down
28 changes: 13 additions & 15 deletions app/src/org/commcare/activities/ConnectIDRegistrationActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.google.common.collect.ImmutableMultimap;
import com.google.gson.Gson;

import org.commcare.android.database.connect.models.ConnectUserRecord;
import org.commcare.core.network.AuthInfo;
import org.commcare.core.network.HTTPMethod;
import org.commcare.dalvik.R;
Expand Down Expand Up @@ -37,7 +38,7 @@ public class ConnectIDRegistrationActivity extends CommCareActivity<ConnectIDReg

private ConnectIDRegistrationActivityUIController uiController;

private ConnectIDUser user;
private ConnectUserRecord user;

@Override
protected void onCreate(Bundle savedInstanceState) {
Expand Down Expand Up @@ -85,28 +86,25 @@ private String generatePassword() {
public void createAccount() {
String url = getString(R.string.ConnectURL) + "/users/register";

user = new ConnectIDUser();
user.Username = uiController.getUserIdText();
user.Name = uiController.getNameText();
user.DOB = uiController.getDOBText();
user.Phone = uiController.getPhoneText();
user.AltPhone = uiController.getAltPhoneText();
user.Password = generatePassword();
user = new ConnectUserRecord(uiController.getUserIdText(),
generatePassword(), uiController.getNameText());
String dob = uiController.getDOBText();
String phone = uiController.getPhoneText();
String altPhone = uiController.getAltPhoneText();

HashMap<String, String> params = new HashMap<>();
//params.put("device_id", CommCareApplication.instance().getPhoneId());
params.put("username", user.Username);
params.put("password", user.Password);
params.put("name", user.Name);
params.put("dob", user.DOB);
params.put("phone_number", user.Phone);
//params.put("recovery_phone", user.AltPhone);
params.put("username", user.getUserID());
params.put("password", user.getPassword());
params.put("name", user.getName());
params.put("dob", dob);
params.put("phone_number", phone);
params.put("recovery_phone", altPhone);

Gson gson = new Gson();
String json = gson.toJson(params);

RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), json);
//RequestBody requestBody = ModernHttpRequester.getPostBody(params);
ModernHttpTask postTask =
new ModernHttpTask(this, url,
ImmutableMultimap.of(),
Expand Down
65 changes: 0 additions & 65 deletions app/src/org/commcare/activities/ConnectIDUser.java

This file was deleted.

3 changes: 1 addition & 2 deletions app/src/org/commcare/activities/LoginActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,6 @@ public class LoginActivity extends CommCareActivity<LoginActivity>
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

ConnectIDManager.loadUserFromPreferences();

checkManagedConfiguration();

if (shouldFinish()) {
Expand All @@ -122,6 +120,7 @@ protected void onCreate(Bundle savedInstanceState) {
uiController.setupUI();
formAndDataSyncer = new FormAndDataSyncer();

ConnectIDManager.init(this);
if(ConnectIDManager.ENABLE_CONNECT_ID && ConnectIDManager.isConnectIDIntroduced()) {
uiController.showConnectIDButton();
updateConnectButton();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.commcare.android.database.connect.models;

import org.commcare.android.storage.framework.Persisted;
import org.commcare.models.framework.Persisting;
import org.commcare.modern.database.Table;

@Table(org.commcare.android.database.connect.models.ConnectUserRecord.STORAGE_KEY)
public class ConnectLinkedAppRecord extends Persisted {
/**
* Name of database that stores Connect user records
*/
public static final String STORAGE_KEY = "app_info";

@Persisting(1)
private String appID;

@Persisting(2)
private String userID;

@Persisting(3)
private String password;
}
Loading