From 29c52c844fb78ca48c7e98c5789b9ad0b2c5b4e5 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Mon, 26 May 2025 11:41:01 +0200 Subject: [PATCH] Add vaultstore generic instance for sharing main app and autofill component (#846) --- .../nativevaultmanager/NativeVaultManager.kt | 4 +- .../aliasvault/app/vaultstore/VaultStore.kt | 71 +++++++++++++++++-- .../app/(tabs)/settings/android-autofill.tsx | 4 +- 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/nativevaultmanager/NativeVaultManager.kt b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/nativevaultmanager/NativeVaultManager.kt index 654cb6538..250832a6d 100644 --- a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/nativevaultmanager/NativeVaultManager.kt +++ b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/nativevaultmanager/NativeVaultManager.kt @@ -22,9 +22,9 @@ class NativeVaultManager(reactContext: ReactApplicationContext) : const val NAME = "NativeVaultManager" } - private val vaultStore = VaultStore( - AndroidStorageProvider(reactContext), + private val vaultStore = VaultStore.getInstance( AndroidKeystoreProvider(reactContext) { getFragmentActivity() }, + AndroidStorageProvider(reactContext) ) init { diff --git a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/VaultStore.kt b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/VaultStore.kt index 70527ba70..422629ae0 100644 --- a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/VaultStore.kt +++ b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/VaultStore.kt @@ -44,6 +44,11 @@ class VaultStore( fun onError(e: Exception) } + interface CredentialOperationCallback { + fun onSuccess(result: List) + fun onError(e: Exception) + } + fun storeEncryptionKey(base64EncryptionKey: String) { this.encryptionKey = Base64.decode(base64EncryptionKey, Base64.NO_WRAP) @@ -388,6 +393,35 @@ class VaultStore( storageProvider.clearStorage() } + /** + * Attempts to get all credentials using only the cached encryption key. + * Returns false if the key isn't in memory, which signals the caller to authenticate. + */ + fun tryGetAllCredentials(callback: CredentialOperationCallback): Boolean { + // Check if the encryption key is already in memory + if (encryptionKey == null) { + Log.d(TAG, "Encryption key not in memory, authentication required") + return false + } + + try { + Log.d(TAG, "Unlocking vault and retrieving all credentials") + + // Unlock vault if it's locked + if (!isVaultUnlocked()) { + unlockVault() + } + + // Return all credentials + callback.onSuccess(getAllCredentials()) + return true + } catch (e: Exception) { + Log.e(TAG, "Error retrieving credentials", e) + callback.onError(e) + return false + } + } + private fun decryptData(encryptedData: String): String { var decryptedResult: String? = null var error: Exception? = null @@ -680,14 +714,26 @@ class VaultStore( return null } - return try { + val formats = listOf( SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US).apply { timeZone = TimeZone.getTimeZone("UTC") - }.parse(dateString) - } catch (e: Exception) { - Log.e(TAG, "Error parsing date: $dateString", e) - null + }, + SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US).apply { + timeZone = TimeZone.getTimeZone("UTC") + } + ) + + for (format in formats) { + try { + return format.parse(dateString) + } catch (e: Exception) { + // Continue to next format if this one fails + continue + } } + + Log.e(TAG, "Error parsing date: $dateString") + return null } fun onAppBackgrounded() { @@ -722,5 +768,20 @@ class VaultStore( set(Calendar.SECOND, 0) set(Calendar.MILLISECOND, 0) }.time + + @Volatile + private var instance: VaultStore? = null + + @JvmStatic + fun getInstance(keystoreProvider: KeystoreProvider, storageProvider: StorageProvider): VaultStore { + return instance ?: synchronized(this) { + instance ?: VaultStore(storageProvider, keystoreProvider).also { instance = it } + } + } + + @JvmStatic + fun getExistingInstance(): VaultStore? { + return instance + } } } diff --git a/apps/mobile-app/app/(tabs)/settings/android-autofill.tsx b/apps/mobile-app/app/(tabs)/settings/android-autofill.tsx index 32a85921b..7f2b1c900 100644 --- a/apps/mobile-app/app/(tabs)/settings/android-autofill.tsx +++ b/apps/mobile-app/app/(tabs)/settings/android-autofill.tsx @@ -118,10 +118,10 @@ export default function AndroidAutofillScreen() : React.ReactNode { - 2. Navigate to "Passwords, passkeys & accounts" in the settings menu + 2. Navigate to the "Passwords and autofill" section in the settings menu. Depending on your device, this option may be under "General management" or "System Settings". - 3. Change the "prefered service" to "AliasVault" + 3. Change the "autofill preferred service" to "AliasVault" Note: You'll need to authenticate with your device security method when using autofill.