Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions .github/workflows/pr-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
java-version: '21'
cache: 'gradle'

- name: Build the app
Expand Down Expand Up @@ -93,7 +93,7 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
java-version: '21'
cache: 'gradle'

# - name: Run detekt
Expand All @@ -109,7 +109,7 @@ jobs:
target: ${{ matrix.target }}
arch: ${{ matrix.api-level > 27 && 'x86_64' || 'x86' }}
profile: pixel_3a
script: ./gradlew :android:connectedDebugAndroidTest --stacktrace
script: ./gradlew connectedCheck

- name: Upload Reports
uses: actions/upload-artifact@v4
Expand Down
1 change: 1 addition & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 6 additions & 9 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ android {

dependencies {
def lifecycle_version = '2.8.3'
def dagger_version = '2.56'
def dagger_version = '2.57.2'
def coroutines_version = "1.3.9"
def ktor_version = "3.1.1"

Expand All @@ -86,7 +86,7 @@ dependencies {

implementation 'io.rownd:telemetry:1.0.1'

implementation 'androidx.datastore:datastore-preferences:1.1.3'
implementation 'androidx.datastore:datastore-preferences:1.1.7'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.3.2'

Expand Down Expand Up @@ -131,25 +131,22 @@ dependencies {
implementation 'com.auth0.android:jwtdecode:2.0.2'
implementation 'com.lyft.kronos:kronos-android:0.0.1-alpha11'

implementation "com.goterl:lazysodium-android:5.0.2@aar"
implementation "net.java.dev.jna:jna:5.8.0@aar"
implementation "androidx.security:security-crypto:1.0.0"

// Passkeys & Google sign-in
implementation "androidx.credentials:credentials:1.5.0"
implementation "androidx.credentials:credentials-play-services-auth:1.5.0"
implementation 'com.google.android.libraries.identity.googleid:googleid:1.1.1'
implementation 'com.google.android.gms:play-services-auth:21.3.0'
implementation 'com.google.android.gms:play-services-auth:21.4.0'

// Dependency injection via Dagger
implementation "com.google.dagger:dagger:$dagger_version"
kapt "com.google.dagger:dagger-compiler:$dagger_version"

// For instrumentation tests
androidTestImplementation 'androidx.test.ext:junit-ktx:1.2.1'
androidTestImplementation 'androidx.test.ext:junit-ktx:1.3.0'
testImplementation("io.ktor:ktor-client-mock:$ktor_version")
androidTestImplementation("io.ktor:ktor-client-mock:$ktor_version")
androidTestImplementation 'com.nimbusds:nimbus-jose-jwt:7.8.1'
androidTestImplementation 'com.nimbusds:nimbus-jose-jwt:10.6'
androidTestImplementation 'com.google.crypto.tink:tink-android:1.19.0'

androidTestImplementation "com.google.dagger:dagger:$dagger_version"
kaptAndroidTest "com.google.dagger:dagger-compiler:$dagger_version"
Expand Down

This file was deleted.

4 changes: 0 additions & 4 deletions android/src/main/java/io/rownd/android/Rownd.kt
Original file line number Diff line number Diff line change
Expand Up @@ -370,10 +370,6 @@ class RowndClient(
}
}

fun isEncryptionPossible(): Boolean {
return userRepo.isEncryptionPossible()
}

// Internal stuff
internal fun displayHub(
targetPage: HubPageSelector,
Expand Down
28 changes: 1 addition & 27 deletions android/src/main/java/io/rownd/android/models/domain/User.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package io.rownd.android.models.domain

import android.util.Log
import io.rownd.android.models.repos.StateRepo
import io.rownd.android.models.repos.UserRepo
import io.rownd.android.util.AnyValueSerializer
import io.rownd.android.util.AuthLevel
import io.rownd.android.util.Encryption
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import io.rownd.android.models.network.User as NetworkUser
Expand All @@ -21,34 +19,10 @@ data class User(
) {
fun asNetworkModel(stateRepo: StateRepo, userRepo: UserRepo): NetworkUser {
return NetworkUser(
data = dataAsEncrypted(stateRepo, userRepo),
data = data,
redacted = redacted,
state = state,
authLevel = authLevel,
)
}

internal fun dataAsEncrypted(stateRepo: StateRepo, userRepo: UserRepo): Map<String, Any?> {
val encKeyId = userRepo.ensureEncryptionKey(this) ?: return data

val data = data.toMutableMap()

// Encrypt user fields
for (entry in data.entries) {
val (key, _) = entry
if (stateRepo.state.value.appConfig.schema[key]?.encryption?.state == AppSchemaEncryptionState.Enabled && entry.value is String) {
val value = entry.value as String
try {
val encrypted: String = Encryption.encrypt(value, encKeyId)
data[key] = encrypted
} catch (error: Exception) {
Log.d(
"RowndUserNetwork",
"Failed to encrypt user data value. Error: ${error.message}"
)
}
}
}
return data
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import io.rownd.android.models.domain.AuthState
import io.rownd.android.models.repos.StateAction
import io.rownd.android.models.repos.UserRepo
import io.rownd.android.util.AuthenticatedApiClient
import io.rownd.android.util.Encryption
import io.rownd.android.util.KtorApiClient
import io.rownd.android.util.RowndContext
import io.rownd.android.util.RowndEvent
Expand Down Expand Up @@ -79,10 +78,8 @@ class SignInLinkApi @Inject constructor() {
internal suspend fun signInWithLink(url: String) {
var signInUrl = url
val urlObj = url.toUri()
var encKey: String? = null

if (urlObj.fragment != null) {
encKey = urlObj.fragment
signInUrl = signInUrl.replace("#${urlObj.fragment}", "")
}

Expand All @@ -93,11 +90,6 @@ class SignInLinkApi @Inject constructor() {
try {
val authBody = authenticateWithSignInLink(signInUrl)

if (encKey != null) {
Encryption.deleteKey(authBody.appUserId)
Encryption.storeKey(encKey, authBody.appUserId)
}

Rownd.store.dispatch(
StateAction.SetAuth(
AuthState(
Expand Down
29 changes: 1 addition & 28 deletions android/src/main/java/io/rownd/android/models/network/User.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package io.rownd.android.models.network

import android.util.Log
import io.rownd.android.models.domain.AppSchemaEncryptionState
import io.rownd.android.models.repos.StateRepo
import io.rownd.android.models.repos.UserRepo
import io.rownd.android.util.AnyValueSerializer
import io.rownd.android.util.AuthLevel
import io.rownd.android.util.Encryption
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import io.rownd.android.models.domain.User as DomainUser
Expand All @@ -21,34 +18,10 @@ data class User(
) {
fun asDomainModel(stateRepo: StateRepo, userRepo: UserRepo): DomainUser {
return DomainUser(
data = dataAsDecrypted(stateRepo, userRepo),
data = data,
redacted = redacted.toMutableList(),
state = state,
authLevel = authLevel
)
}

internal fun dataAsDecrypted(stateRepo: StateRepo, userRepo: UserRepo): Map<String, Any?> {
val encKeyId = userRepo.ensureEncryptionKey(DomainUser(data = data)) ?: return data

val data = data.toMutableMap()

// Decrypt user fields
for (entry in data.entries) {
val (key, _) = entry
if (stateRepo.state.value.appConfig.schema[key]?.encryption?.state == AppSchemaEncryptionState.Enabled && entry.value is String) {
val value = entry.value as String
try {
val decrypted: String = Encryption.decrypt(value, encKeyId)
data[key] = decrypted
} catch (error: Exception) {
Log.d(
"RowndUserNetwork",
"Failed to decrypt user data value. Error: ${error.message} ${error.stackTraceToString()}"
)
}
}
}
return data
}
}
40 changes: 0 additions & 40 deletions android/src/main/java/io/rownd/android/models/repos/UserRepo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import io.ktor.client.request.setBody
import io.ktor.http.HttpStatusCode
import io.rownd.android.models.domain.User
import io.rownd.android.util.AuthenticatedApiClient
import io.rownd.android.util.Encryption
import io.rownd.android.util.EncryptionException
import io.rownd.android.util.RowndContext
import io.rownd.android.util.RowndException
import kotlinx.coroutines.CoroutineScope
Expand All @@ -19,7 +17,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.collections.set
import io.rownd.android.models.network.User as NetworkUser

@Singleton
Expand Down Expand Up @@ -122,41 +119,4 @@ class UserRepo @Inject constructor() {
return saveUserAsync(updatedUser)
}

fun getKeyId(user: User): String {
return get("user_id")
?: throw EncryptionException("An encryption key was requested, but the user has not been loaded yet. Are you signed in?")
}

internal fun ensureEncryptionKey(user: User): String? {
try {
val keyId = getKeyId(user)

var key = Encryption.loadKey(keyId)

if (key == null) {
key = Encryption.generateKey()
Encryption.storeKey(key, keyId)
return keyId
}

return keyId
} catch (error: Exception) {
Log.e(
"RowndUser",
"Failed to ensure that an encryption key exists: ${error.message}"
)
return null
}
}

fun isEncryptionPossible(): Boolean {
try {
val key = Encryption.loadKey(getKeyId(stateRepo.state.value.user)) ?: return false

return true
} catch (error: Exception) {
return false
}
}

}
Loading