From 282d69eeb6a28b0affd57e446dc9ab675049e4fd Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Thu, 1 Jan 2026 20:04:37 +0100 Subject: [PATCH] Cleanup unused NativeVaultManager methods and refactor (#1404) --- .../nativevaultmanager/NativeVaultManager.kt | 47 ---------- apps/mobile-app/context/DbContext.tsx | 11 ++- apps/mobile-app/hooks/useVaultMutate.ts | 9 +- .../RCTNativeVaultManager.mm | 12 --- .../ios/NativeVaultManager/VaultManager.swift | 27 ------ apps/mobile-app/specs/NativeVaultManager.ts | 93 ++++++------------- apps/mobile-app/utils/SqliteClient.ts | 19 ---- 7 files changed, 46 insertions(+), 172 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 ef400f178..1747de462 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 @@ -183,22 +183,6 @@ class NativeVaultManager(reactContext: ReactApplicationContext) : } } - /** - * Store the encrypted database. - * @param base64EncryptedDb The encrypted database as a base64 encoded string - * @param promise The promise to resolve - */ - @ReactMethod - override fun storeDatabase(base64EncryptedDb: String, promise: Promise) { - try { - vaultStore.storeEncryptedDatabase(base64EncryptedDb) - promise.resolve(null) - } catch (e: Exception) { - Log.e(TAG, "Error storing database", e) - promise.reject("ERR_STORE_DB", "Failed to store database: ${e.message}", e) - } - } - /** * Store the metadata. * @param metadata The metadata as a string @@ -340,37 +324,6 @@ class NativeVaultManager(reactContext: ReactApplicationContext) : } } - /** - * Get the current vault revision number. - * @param promise The promise to resolve - */ - @ReactMethod - override fun getCurrentVaultRevisionNumber(promise: Promise) { - try { - val revision = vaultStore.getVaultRevisionNumber() - promise.resolve(revision) - } catch (e: Exception) { - Log.e(TAG, "Error getting vault revision", e) - promise.reject("ERR_GET_REVISION", "Failed to get vault revision: ${e.message}", e) - } - } - - /** - * Set the current vault revision number. - * @param revisionNumber The revision number - * @param promise The promise to resolve - */ - @ReactMethod - override fun setCurrentVaultRevisionNumber(revisionNumber: Double, promise: Promise?) { - try { - vaultStore.setVaultRevisionNumber(revisionNumber.toInt()) - promise?.resolve(null) - } catch (e: Exception) { - Log.e(TAG, "Error setting vault revision", e) - promise?.reject("ERR_SET_REVISION", "Failed to set vault revision: ${e.message}", e) - } - } - /** * Execute a query on the vault. * @param query The query diff --git a/apps/mobile-app/context/DbContext.tsx b/apps/mobile-app/context/DbContext.tsx index 22ea04223..416b9291c 100644 --- a/apps/mobile-app/context/DbContext.tsx +++ b/apps/mobile-app/context/DbContext.tsx @@ -95,8 +95,15 @@ export const DbProvider: React.FC<{ children: React.ReactNode }> = ({ children } vaultRevisionNumber: vaultResponse.vault.currentRevisionNumber, }; - // Store the encrypted database and metadata (metadata is stored in plain text in UserDefaults) - await sqliteClient.storeEncryptedDatabase(vaultResponse.vault.blob); + // Store vault blob atomically with sync state (fresh from server, not dirty) + await NativeVaultManager.storeEncryptedVaultWithSyncState( + vaultResponse.vault.blob, + false, // markDirty = false (fresh from server) + vaultResponse.vault.currentRevisionNumber, // serverRevision + null // expectedMutationSeq (not checking race on initial login) + ); + + // Store metadata separately (email domains - not critical for race conditions) await sqliteClient.storeMetadata(JSON.stringify(metadata)); // Unlock the vault to make it available for queries diff --git a/apps/mobile-app/hooks/useVaultMutate.ts b/apps/mobile-app/hooks/useVaultMutate.ts index 5e958dcb8..d3da86bef 100644 --- a/apps/mobile-app/hooks/useVaultMutate.ts +++ b/apps/mobile-app/hooks/useVaultMutate.ts @@ -49,7 +49,8 @@ export function useVaultMutate() : { * Prepare vault for password change operation. */ const prepareVaultForPasswordChange = useCallback(async (): Promise => { - const currentRevision = await NativeVaultManager.getCurrentVaultRevisionNumber(); + const syncState = await NativeVaultManager.getSyncState(); + const currentRevision = syncState.serverRevision; const encryptedDb = await NativeVaultManager.getEncryptedDatabase(); if (!encryptedDb) { throw new Error(t('vault.errors.failedToGetEncryptedDatabase')); @@ -261,6 +262,9 @@ export function useVaultMutate() : { }; try { + // Capture mutation sequence before upload for atomic state update + const syncState = await NativeVaultManager.getSyncState(); + // Upload to server const response = await webApi.post('Vault/change-password', passwordChangeVault); @@ -273,7 +277,8 @@ export function useVaultMutate() : { // If we get here, it means we have a valid connection to the server. await NativeVaultManager.setOfflineMode(false); - await NativeVaultManager.setCurrentVaultRevisionNumber(newRevisionNumber); + // Update revision atomically with sync state (clears dirty flag if no mutations during upload) + await NativeVaultManager.markVaultClean(syncState.mutationSequence, newRevisionNumber); options.onSuccess?.(); } catch (error) { console.error('Error during password change operation:', error); diff --git a/apps/mobile-app/ios/NativeVaultManager/RCTNativeVaultManager.mm b/apps/mobile-app/ios/NativeVaultManager/RCTNativeVaultManager.mm index bfb88e62f..0ba01d1f5 100644 --- a/apps/mobile-app/ios/NativeVaultManager/RCTNativeVaultManager.mm +++ b/apps/mobile-app/ios/NativeVaultManager/RCTNativeVaultManager.mm @@ -93,10 +93,6 @@ [vaultManager clearClipboardAfterDelay:delayInSeconds resolver:resolve rejecter:reject]; } -- (void)storeDatabase:(NSString *)base64EncryptedDb resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { - [vaultManager storeDatabase:base64EncryptedDb resolver:resolve rejecter:reject]; -} - - (void)storeMetadata:(NSString *)metadata resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { [vaultManager storeMetadata:metadata resolver:resolve rejecter:reject]; } @@ -125,14 +121,6 @@ [vaultManager getEncryptedDatabase:resolve rejecter:reject]; } -- (void)getCurrentVaultRevisionNumber:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { - [vaultManager getCurrentVaultRevisionNumber:resolve rejecter:reject]; -} - -- (void)setCurrentVaultRevisionNumber:(double)revisionNumber resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { - [vaultManager setCurrentVaultRevisionNumber:revisionNumber resolver:resolve rejecter:reject]; -} - - (void)deriveKeyFromPassword:(NSString *)password salt:(NSString *)salt encryptionType:(NSString *)encryptionType encryptionSettings:(NSString *)encryptionSettings resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { [vaultManager deriveKeyFromPassword:password salt:salt encryptionType:encryptionType encryptionSettings:encryptionSettings resolver:resolve rejecter:reject]; } diff --git a/apps/mobile-app/ios/NativeVaultManager/VaultManager.swift b/apps/mobile-app/ios/NativeVaultManager/VaultManager.swift index e24d41dcb..0b06abd8e 100644 --- a/apps/mobile-app/ios/NativeVaultManager/VaultManager.swift +++ b/apps/mobile-app/ios/NativeVaultManager/VaultManager.swift @@ -23,18 +23,6 @@ public class VaultManager: NSObject { super.init() } - @objc - func storeDatabase(_ base64EncryptedDb: String, - resolver resolve: @escaping RCTPromiseResolveBlock, - rejecter reject: @escaping RCTPromiseRejectBlock) { - do { - try vaultStore.storeEncryptedDatabase(base64EncryptedDb) - resolve(nil) - } catch { - reject("DB_ERROR", "Failed to store database: \(error.localizedDescription)", error) - } - } - @objc func storeMetadata(_ metadata: String, resolver resolve: @escaping RCTPromiseResolveBlock, @@ -201,21 +189,6 @@ public class VaultManager: NSObject { } } - @objc - func getCurrentVaultRevisionNumber(_ resolve: @escaping RCTPromiseResolveBlock, - rejecter reject: @escaping RCTPromiseRejectBlock) { - let revisionNumber = vaultStore.getCurrentVaultRevisionNumber() - resolve(revisionNumber) - } - - @objc - func setCurrentVaultRevisionNumber(_ revisionNumber: Int, - resolver resolve: @escaping RCTPromiseResolveBlock, - rejecter reject: @escaping RCTPromiseRejectBlock) { - vaultStore.setCurrentVaultRevisionNumber(revisionNumber) - resolve(nil) - } - @objc func hasEncryptedDatabase(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) { diff --git a/apps/mobile-app/specs/NativeVaultManager.ts b/apps/mobile-app/specs/NativeVaultManager.ts index f29cb7841..800037ebc 100644 --- a/apps/mobile-app/specs/NativeVaultManager.ts +++ b/apps/mobile-app/specs/NativeVaultManager.ts @@ -4,19 +4,45 @@ import type { TurboModule } from 'react-native'; // eslint-disable-next-line @typescript-eslint/naming-convention export interface Spec extends TurboModule { - // Basic credential operations - clearVault(): Promise; + // WebAPI configuration and token management + setApiUrl(url: string): Promise; + getApiUrl(): Promise; + setAuthTokens(accessToken: string, refreshToken: string): Promise; + getAccessToken(): Promise; + clearAuthTokens(): Promise; + revokeTokens(): Promise; + + // WebAPI request execution + executeWebApiRequest(method: string, endpoint: string, body: string | null, headers: string, requiresAuth: boolean): Promise; // Vault state management isVaultUnlocked(): Promise; getVaultMetadata(): Promise; unlockVault(): Promise; + clearVault(): Promise; + + // Vault sync - single method handles all sync logic including merge + // Returns detailed result about what action was taken + syncVaultWithServer(): Promise<{ success: boolean; action: 'uploaded' | 'downloaded' | 'merged' | 'already_in_sync' | 'error'; newRevision: number; wasOffline: boolean; error: string | null }>; + + // Sync state management (kept for local mutation tracking) + getSyncState(): Promise<{isDirty: boolean; mutationSequence: number; serverRevision: number; isSyncing: boolean}>; + storeEncryptedVaultWithSyncState(encryptedVault: string, markDirty: boolean, serverRevision: number | null, expectedMutationSeq: number | null): Promise<{ success: boolean; mutationSequence: number }>; + markVaultClean(mutationSeqAtStart: number, newServerRevision: number): Promise; + uploadVault(): Promise<{ success: boolean; status: number; newRevisionNumber: number; mutationSeqAtStart: number; error: string | null }>; + + // Vault SQL operations + executeQuery(query: string, params: (string | number | null)[]): Promise; + executeUpdate(query: string, params:(string | number | null)[]): Promise; + executeRaw(query: string): Promise; + beginTransaction(): Promise; + commitTransaction(): Promise; + rollbackTransaction(): Promise; // Cryptography operations deriveKeyFromPassword(password: string, salt: string, encryptionType: string, encryptionSettings: string): Promise; - // Database operations - storeDatabase(base64EncryptedDb: string): Promise; + // Database/encryption key operations storeMetadata(metadata: string): Promise; setAuthMethods(authMethods: string[]): Promise; storeEncryptionKey(base64EncryptionKey: string): Promise; @@ -24,16 +50,6 @@ export interface Spec extends TurboModule { getEncryptionKeyDerivationParams(): Promise; hasEncryptedDatabase(): Promise; getEncryptedDatabase(): Promise; - getCurrentVaultRevisionNumber(): Promise; - setCurrentVaultRevisionNumber(revisionNumber: number): Promise; - - // SQL operations - executeQuery(query: string, params: (string | number | null)[]): Promise; - executeUpdate(query: string, params:(string | number | null)[]): Promise; - executeRaw(query: string): Promise; - beginTransaction(): Promise; - commitTransaction(): Promise; - rollbackTransaction(): Promise; // Auto-lock settings setAutoLockTimeout(timeout: number): Promise; @@ -59,23 +75,6 @@ export interface Spec extends TurboModule { registerCredentialIdentities(): Promise; removeCredentialIdentities(): Promise; - // WebAPI configuration and token management - setApiUrl(url: string): Promise; - getApiUrl(): Promise; - setAuthTokens(accessToken: string, refreshToken: string): Promise; - getAccessToken(): Promise; - clearAuthTokens(): Promise; - revokeTokens(): Promise; - - // WebAPI request execution - executeWebApiRequest( - method: string, - endpoint: string, - body: string | null, - headers: string, - requiresAuth: boolean - ): Promise; - // Username management setUsername(username: string): Promise; getUsername(): Promise; @@ -88,38 +87,6 @@ export interface Spec extends TurboModule { // Server version management isServerVersionGreaterThanOrEqualTo(targetVersion: string): Promise; - // Vault sync - single method handles all sync logic including merge - // Returns detailed result about what action was taken - syncVaultWithServer(): Promise<{ - success: boolean; - action: 'uploaded' | 'downloaded' | 'merged' | 'already_in_sync' | 'error'; - newRevision: number; - wasOffline: boolean; - error: string | null; - }>; - - // Sync state management (kept for local mutation tracking) - getSyncState(): Promise<{ - isDirty: boolean; - mutationSequence: number; - serverRevision: number; - isSyncing: boolean; - }>; - storeEncryptedVaultWithSyncState( - encryptedVault: string, - markDirty: boolean, - serverRevision: number | null, - expectedMutationSeq: number | null - ): Promise<{ success: boolean; mutationSequence: number }>; - markVaultClean(mutationSeqAtStart: number, newServerRevision: number): Promise; - uploadVault(): Promise<{ - success: boolean; - status: number; - newRevisionNumber: number; - mutationSeqAtStart: number; - error: string | null; - }>; - // PIN unlock methods isPinEnabled(): Promise; removeAndDisablePin(): Promise; diff --git a/apps/mobile-app/utils/SqliteClient.ts b/apps/mobile-app/utils/SqliteClient.ts index d40b21cca..ad33ec886 100644 --- a/apps/mobile-app/utils/SqliteClient.ts +++ b/apps/mobile-app/utils/SqliteClient.ts @@ -77,20 +77,6 @@ class SqliteClient implements IDatabaseClient { return this._logos; } - // ===== Core Database Operations ===== - - /** - * Store the encrypted database via the native code implementation. - */ - public async storeEncryptedDatabase(base64EncryptedDb: string): Promise { - try { - await NativeVaultManager.storeDatabase(base64EncryptedDb); - } catch (error) { - console.error('Error initializing SQLite database:', error); - throw error; - } - } - /** * Store the vault metadata via the native code implementation. * @@ -280,10 +266,6 @@ class SqliteClient implements IDatabaseClient { } } - // ============================================================================ - // Utility methods - // ============================================================================ - /** * Fetch all encryption keys. */ @@ -482,7 +464,6 @@ class SqliteClient implements IDatabaseClient { const allVersions = vaultSqlGenerator.getAllVersions(); return allVersions[allVersions.length - 1]; } - } export default SqliteClient; \ No newline at end of file