Skip to content

Commit

Permalink
[MOB - 6865] - Keychain null crash fix
Browse files Browse the repository at this point in the history
- Handles condition where IterableKeychain creation returns null to Iterable API.
SDK will continue to process without crashing.
There will be no email fetched from sharedPreferences in this rare case. No email will be persisted and the app will have to setEmail/setUserId on every app session for it to have email in memory than having it persisted for consequent app sessions

- Fixed indentation on IterableKeychain file. Using cmd+l on Android Studio

- Added method description in IterableConfig class so developers have more context on what `setEncryptionEnforced` means

- getKeychain is now annotated @nullable for future kotlin migration purpose
  • Loading branch information
“Akshay committed Sep 21, 2023
1 parent d628203 commit 61943df
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 43 deletions.
30 changes: 17 additions & 13 deletions iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ IterableAuthManager getAuthManager() {
return authManager;
}

@NonNull
@Nullable
IterableKeychain getKeychain() {
if (keychain == null) {
try {
Expand Down Expand Up @@ -350,15 +350,25 @@ private String getDeviceId() {
}

private void storeAuthData() {
getKeychain().saveEmail(_email);
getKeychain().saveUserId(_userId);
getKeychain().saveAuthToken(_authToken);
IterableKeychain iterableKeychain = getKeychain();
if (iterableKeychain != null) {
iterableKeychain.saveEmail(_email);
iterableKeychain.saveUserId(_userId);
iterableKeychain.saveAuthToken(_authToken);
} else {
IterableLogger.e(TAG, "Shared preference creation failed. ");
}
}

private void retrieveEmailAndUserId() {
_email = getKeychain().getEmail();
_userId = getKeychain().getUserId();
_authToken = getKeychain().getAuthToken();
IterableKeychain iterableKeychain = getKeychain();
if (iterableKeychain != null) {
_email = iterableKeychain.getEmail();
_userId = iterableKeychain.getUserId();
_authToken = iterableKeychain.getAuthToken();
} else {
IterableLogger.e(TAG, "retrieveEmailAndUserId: Shared preference creation failed. Could not retrieve email/userId");
}

if (config.authHandler != null) {
if (_authToken != null) {
Expand All @@ -370,10 +380,6 @@ private void retrieveEmailAndUserId() {
}
}

private void updateSDKVersion() {

}

private class IterableApiAuthProvider implements IterableApiClient.AuthProvider {
@Nullable
@Override
Expand Down Expand Up @@ -507,8 +513,6 @@ public static void initialize(@NonNull Context context, @NonNull String apiKey,
sharedInstance.config = new IterableConfig.Builder().build();
}

sharedInstance.updateSDKVersion();

sharedInstance.retrieveEmailAndUserId();

IterableActivityMonitor.getInstance().registerLifecycleCallbacks(context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,12 @@ public Builder setAllowedProtocols(@NonNull String[] allowedProtocols) {
return this;
}

/**
* Set whether the SDK should enforce encryption. If set to `true`, the SDK will not use fallback mechanism
* of storing data in un-encrypted shared preferences if encrypted database is not available. Set this to `true`
* if PII confidentiality is a concern for your app.
* @param encryptionEnforced `true` will have the SDK enforce encryption.
*/
public Builder setEncryptionEnforced(boolean encryptionEnforced) {
this.encryptionEnforced = encryptionEnforced;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ class IterableKeychain {
encryptionEnabled = false
sharedPrefs = context.getSharedPreferences(
IterableConstants.SHARED_PREFS_FILE,
Context.MODE_PRIVATE)
Context.MODE_PRIVATE
)
IterableLogger.v(TAG, "SharedPreferences being used")
} else {
// See if EncryptedSharedPreferences can be created successfully
Expand All @@ -39,7 +40,8 @@ class IterableKeychain {
encryptedSharedPrefsFileName,
masterKeyAlias,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM)
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
encryptionEnabled = true
} catch (e: Exception) {
if (encryptionEnforced) {
Expand All @@ -49,35 +51,16 @@ class IterableKeychain {
} else {
sharedPrefs = context.getSharedPreferences(
IterableConstants.SHARED_PREFS_FILE,
Context.MODE_PRIVATE)
Context.MODE_PRIVATE
)
encryptionEnabled = false
}
}

//Try to migrate data from SharedPreferences to EncryptedSharedPreferences
if(encryptionEnabled) {
if (encryptionEnabled) {
migrateAuthDataFromSharedPrefsToKeychain(context)
}

}
val masterKeyAlias = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
sharedPrefs = try {
EncryptedSharedPreferences.create(
context,
encryptedSharedPrefsFileName,
masterKeyAlias,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
} catch (e: Exception) {
if (encryptionEnforced) {
IterableLogger.e(TAG, "Error creating EncryptedSharedPreferences", e)
throw e.fillInStackTrace()
} else {
context.getSharedPreferences(encryptedSharedPrefsFileName, Context.MODE_PRIVATE)
}
}
}

Expand Down Expand Up @@ -113,17 +96,20 @@ class IterableKeychain {

@RequiresApi(api = Build.VERSION_CODES.M)
private fun migrateAuthDataFromSharedPrefsToKeychain(context: Context) {
val oldPrefs: SharedPreferences = context.getSharedPreferences(
val oldPrefs: SharedPreferences = context.getSharedPreferences(
IterableConstants.SHARED_PREFS_FILE,
Context.MODE_PRIVATE)
Context.MODE_PRIVATE
)
val sharedPrefsEmail = oldPrefs.getString(IterableConstants.SHARED_PREFS_EMAIL_KEY, null)
val sharedPrefsUserId = oldPrefs.getString(IterableConstants.SHARED_PREFS_USERID_KEY, null)
val sharedPrefsAuthToken = oldPrefs.getString(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY, null)
val sharedPrefsAuthToken =
oldPrefs.getString(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY, null)
val editor: SharedPreferences.Editor = oldPrefs.edit()
if (getEmail() == null && sharedPrefsEmail != null) {
saveEmail(sharedPrefsEmail)
editor.remove(IterableConstants.SHARED_PREFS_EMAIL_KEY)
IterableLogger.v(TAG,
IterableLogger.v(
TAG,
"UPDATED: migrated email from SharedPreferences to IterableKeychain"
)
} else if (sharedPrefsEmail != null) {
Expand All @@ -132,7 +118,8 @@ class IterableKeychain {
if (getUserId() == null && sharedPrefsUserId != null) {
saveUserId(sharedPrefsUserId)
editor.remove(IterableConstants.SHARED_PREFS_USERID_KEY)
IterableLogger.v(TAG,
IterableLogger.v(
TAG,
"UPDATED: migrated userId from SharedPreferences to IterableKeychain"
)
} else if (sharedPrefsUserId != null) {
Expand All @@ -141,7 +128,8 @@ class IterableKeychain {
if (getAuthToken() == null && sharedPrefsAuthToken != null) {
saveAuthToken(sharedPrefsAuthToken)
editor.remove(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY)
IterableLogger.v(TAG,
IterableLogger.v(
TAG,
"UPDATED: migrated authToken from SharedPreferences to IterableKeychain"
)
} else if (sharedPrefsAuthToken != null) {
Expand Down

0 comments on commit 61943df

Please sign in to comment.