Skip to content

Commit

Permalink
Merge pull request #469 from Iterable/jay/MOB-4549-keychain
Browse files Browse the repository at this point in the history
[MOB-4549] keychain
  • Loading branch information
roninopf authored Oct 24, 2022
2 parents 1cf2dd7 + eab0e9f commit 27e7df4
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 32 deletions.
12 changes: 12 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,25 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}

debug {
testCoverageEnabled true
}
}

testOptions.unitTests.includeAndroidResources = true

compileOptions {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}

kotlinOptions {
jvmTarget = "1.8"
}
Expand Down Expand Up @@ -69,9 +73,11 @@ dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.2.0'
androidTestImplementation 'br.com.concretesolutions:kappuccino:1.2.1'
}

tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
}

task jacocoDebugTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest']) {
group = "reporting"
description = "Generate unified Jacoco code coverage report"
Expand Down Expand Up @@ -99,12 +105,14 @@ task jacocoDebugTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest'])
'**/*$ModuleAdapter.class',
'**/*$ViewInjector*.class',
]

def debugTree = fileTree(dir: "${buildDir}/intermediates/javac/debug/classes", excludes: fileFilter) //we use "debug" build type for test coverage (can be other)
def sdkTree = fileTree(dir: "${buildDir}/../../iterableapi/build/intermediates/javac/debug/classes", excludes: fileFilter)
def sdkUiTree = fileTree(dir: "${buildDir}/../../iterableapi-ui/build/intermediates/javac/debug/classes", excludes: fileFilter)
def mainSrc = "${project.projectDir}/src/main/java"
def sdkSrc = "${project.projectDir}/../iterableapi/src/main/java"
def sdkUiSrc = "${project.projectDir}/../iterableapi-ui/src/main/java"

sourceDirectories.from = files([mainSrc])
classDirectories.from = files([debugTree])
additionalSourceDirs.from = files([sdkSrc, sdkUiSrc])
Expand All @@ -117,11 +125,13 @@ task jacocoDebugTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest'])
task jacocoDebugAndroidTestReport(type: JacocoReport, dependsOn: ['connectedCheck']) {
group = "reporting"
description = "Generate Jacoco code coverage report for instumentation tests"

reports {
xml.enabled = true
html.enabled = true
csv.enabled = false
}

def fileFilter = [
'**/*Test*.*',
'**/AutoValue_*.*',
Expand All @@ -141,12 +151,14 @@ task jacocoDebugAndroidTestReport(type: JacocoReport, dependsOn: ['connectedChec
'**/*$ModuleAdapter.class',
'**/*$ViewInjector*.class',
]

def debugTree = fileTree(dir: "${buildDir}/intermediates/javac/debug/classes", excludes: fileFilter) //we use "debug" build type for test coverage (can be other)
def sdkTree = fileTree(dir: "${buildDir}/../../iterableapi/build/intermediates/javac/debug/classes", excludes: fileFilter)
def sdkUiTree = fileTree(dir: "${buildDir}/../../iterableapi-ui/build/intermediates/javac/debug/classes", excludes: fileFilter)
def mainSrc = "${project.projectDir}/src/main/java"
def sdkSrc = "${project.projectDir}/../iterableapi/src/main/java"
def sdkUiSrc = "${project.projectDir}/../iterableapi-ui/src/main/java"

sourceDirectories.from = files([mainSrc])
classDirectories.from = files([debugTree])
additionalSourceDirs.from = files([sdkSrc, sdkUiSrc])
Expand Down
3 changes: 1 addition & 2 deletions app/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:tools="http://schemas.android.com/tools"
package="iterable.com.iterableapi">
<manifest xmlns:tools="http://schemas.android.com/tools" package="iterable.com.iterableapi">
<uses-sdk tools:overrideLibrary="br.com.concretesolutions.kappuccino,android_libs.ub_uiautomator"/>
</manifest>
8 changes: 2 additions & 6 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.iterable.iterableapi.testapp">

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.iterable.iterableapi.testapp">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
Expand All @@ -14,10 +12,8 @@
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
</manifest>
3 changes: 2 additions & 1 deletion iterableapi-ui/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ ext {
}

apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle'
if(hasProperty("mavenPublishEnabled")) {

if (hasProperty("mavenPublishEnabled")) {
apply from: '../maven-push.gradle'
}

Expand Down
8 changes: 7 additions & 1 deletion iterableapi/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ android {

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
minifyEnabled false
Expand All @@ -25,11 +26,13 @@ android {
multiDexEnabled true
}
}

testOptions.unitTests.all {
testLogging {
exceptionFormat "full"
}
}

testOptions.unitTests.includeAndroidResources = true
}

Expand All @@ -39,6 +42,7 @@ dependencies {
api 'androidx.appcompat:appcompat:1.0.0'
api 'androidx.annotation:annotation:1.0.0'
api 'com.google.firebase:firebase-messaging:20.3.0'
implementation "androidx.security:security-crypto:1.0.0"

testImplementation 'junit:junit:4.12'
testImplementation 'androidx.test:runner:1.3.0'
Expand Down Expand Up @@ -83,12 +87,14 @@ ext {
}

apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle'
if(hasProperty("mavenPublishEnabled")) {

if (hasProperty("mavenPublishEnabled")) {
apply from: '../maven-push.gradle'
}

task javadoc(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
excludes = ['**/*.kt']
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}

Expand Down
6 changes: 2 additions & 4 deletions iterableapi/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
package="iterable.com.iterableapi">
<manifest xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" package="iterable.com.iterableapi">
<uses-sdk tools:overrideLibrary="android_libs.ub_uiautomator"/>
<application
android:label="IterableAPI"
android:supportsRtl="true"/>
android:supportsRtl="true" />
</manifest>
5 changes: 4 additions & 1 deletion iterableapi/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.iterable.iterableapi">

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application>

<application>
<!--FCM-->
<service
android:name="com.iterable.iterableapi.IterableFirebaseMessagingService"
Expand All @@ -27,6 +28,8 @@
android:exported="false"
android:launchMode="singleTop"
android:theme="@style/TrampolineActivity.Transparent"/>

<uses-library android:name="androidx.security" android:required="false" />
</application>

<queries>
Expand Down
95 changes: 81 additions & 14 deletions iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;

import androidx.annotation.NonNull;
Expand Down Expand Up @@ -44,6 +45,7 @@ public class IterableApi {
private String inboxSessionId;
private IterableAuthManager authManager;
private HashMap<String, String> deviceAttributes = new HashMap<>();
private IterableKeychain keychain;

void fetchRemoteConfiguration() {
apiClient.getRemoteConfiguration(new IterableHelper.IterableActionHandler() {
Expand Down Expand Up @@ -130,6 +132,15 @@ IterableAuthManager getAuthManager() {
return authManager;
}

@NonNull
IterableKeychain getKeychain() {
if (keychain == null) {
keychain = new IterableKeychain(getMainActivityContext());
}

return keychain;
}

static void loadLastSavedConfiguration(Context context) {
SharedPreferences sharedPref = context.getSharedPreferences(IterableConstants.SHARED_PREFS_SAVED_CONFIGURATION, Context.MODE_PRIVATE);
boolean offlineMode = sharedPref.getBoolean(IterableConstants.SHARED_PREFS_OFFLINE_MODE_KEY, false);
Expand Down Expand Up @@ -330,31 +341,85 @@ private String getDeviceId() {
}

private void storeAuthData() {
try {
SharedPreferences.Editor editor = getPreferences().edit();
editor.putString(IterableConstants.SHARED_PREFS_EMAIL_KEY, _email);
editor.putString(IterableConstants.SHARED_PREFS_USERID_KEY, _userId);
editor.putString(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY, _authToken);
editor.commit();
} catch (Exception e) {
IterableLogger.e(TAG, "Error while persisting email/userId", e);
if (hasEncryptionDependency()) {
getKeychain().saveEmail(_email);
getKeychain().saveUserId(_userId);
getKeychain().saveAuthToken(_authToken);
} else {
try {
SharedPreferences.Editor editor = getPreferences().edit();
editor.putString(IterableConstants.SHARED_PREFS_EMAIL_KEY, _email);
editor.putString(IterableConstants.SHARED_PREFS_USERID_KEY, _userId);
editor.putString(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY, _authToken);
editor.commit();
} catch (Exception e) {
IterableLogger.e(TAG, "Error while persisting email/userId", e);
}
}
}

private void retrieveEmailAndUserId() {
try {
if (hasEncryptionDependency()) {
_email = getKeychain().getEmail();
_userId = getKeychain().getUserId();
_authToken = getKeychain().getAuthToken();
} else {
SharedPreferences prefs = getPreferences();
_email = prefs.getString(IterableConstants.SHARED_PREFS_EMAIL_KEY, null);
_userId = prefs.getString(IterableConstants.SHARED_PREFS_USERID_KEY, null);
_authToken = prefs.getString(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY, null);
if (_authToken != null) {
getAuthManager().queueExpirationRefresh(_authToken);
}
} catch (Exception e) {
IterableLogger.e(TAG, "Error while retrieving email/userId/authToken", e);
}

if (_authToken != null) {
getAuthManager().queueExpirationRefresh(_authToken);
}
}

private void updateSDKVersion() {
if (hasEncryptionDependency()) {
migrateAuthDataFromSharedPrefsToKeychain();
}
}

private void migrateAuthDataFromSharedPrefsToKeychain() {
SharedPreferences prefs = getPreferences();
String sharedPrefsEmail = prefs.getString(IterableConstants.SHARED_PREFS_EMAIL_KEY, null);
String sharedPrefsUserId = prefs.getString(IterableConstants.SHARED_PREFS_USERID_KEY, null);
String sharedPrefsAuthToken = prefs.getString(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY, null);

SharedPreferences.Editor editor = getPreferences().edit();

if (getKeychain().getEmail() == null && sharedPrefsEmail != null) {
getKeychain().saveEmail(sharedPrefsEmail);
editor.remove(IterableConstants.SHARED_PREFS_EMAIL_KEY);
IterableLogger.v(TAG, "UPDATED: migrated email from SharedPreferences to IterableKeychain");
} else if (sharedPrefsEmail != null) {
editor.remove(IterableConstants.SHARED_PREFS_EMAIL_KEY);
}

if (getKeychain().getUserId() == null && sharedPrefsUserId != null) {
getKeychain().saveUserId(sharedPrefsUserId);
editor.remove(IterableConstants.SHARED_PREFS_USERID_KEY);
IterableLogger.v(TAG, "UPDATED: migrated userId from SharedPreferences to IterableKeychain");
} else if (sharedPrefsUserId != null) {
editor.remove(IterableConstants.SHARED_PREFS_USERID_KEY);
}

if (getKeychain().getAuthToken() == null && sharedPrefsAuthToken != null) {
getKeychain().saveAuthToken(sharedPrefsAuthToken);
editor.remove(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY);
IterableLogger.v(TAG, "UPDATED: migrated authToken from SharedPreferences to IterableKeychain");
} else if (sharedPrefsAuthToken != null) {
editor.remove(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY);
}

editor.apply();
}

private boolean hasEncryptionDependency() {
return Build.VERSION.SDK_INT >= 23;
}

private class IterableApiAuthProvider implements IterableApiClient.AuthProvider {
@Nullable
@Override
Expand Down Expand Up @@ -488,6 +553,8 @@ 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
Loading

0 comments on commit 27e7df4

Please sign in to comment.