-
Notifications
You must be signed in to change notification settings - Fork 1.4k
feat: migrate react-native-mmkv-storage to react-native-mmkv #6744
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
OtavioStasiak
wants to merge
148
commits into
develop
Choose a base branch
from
feat.migrate-react-native-mmkv-storage
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 117 commits
Commits
Show all changes
148 commits
Select commit
Hold shift + click to select a range
de22283
feat: installed react-native-mmkv
OtavioStasiak fb6afe8
feat: remove old library
OtavioStasiak 53b7c3d
fix: remove MMKV initialization
OtavioStasiak 8a88f38
fix: mocks
OtavioStasiak 92f79d1
fix: podfile
OtavioStasiak 963e124
feat(ios): standalone SecureStorage module
OtavioStasiak 96c7709
feat(ios): standalone SecureStorage module
OtavioStasiak 87d14e1
feat: SSLPinning use new MMKV
OtavioStasiak 99590a0
fix: disable modules to migrate
OtavioStasiak 593e337
fix: lint
OtavioStasiak 49a7173
feat: refactor userPreferences
OtavioStasiak c18acac
fix: lib name type
OtavioStasiak 665a35c
feat: add MMKVBridge wrapper
OtavioStasiak c3cdd8a
chore: xcode project cleanup
OtavioStasiak dc06dac
Merge branch 'develop' into feat.migrate-react-native-mmkv-storage
OtavioStasiak da2129e
test: migration
OtavioStasiak ce619e8
fix: migration mmkv
OtavioStasiak 8ca8642
feat: migrate data from react-native-mmkv-storage to react-native-mmkv
OtavioStasiak 109469c
fix: patch package
OtavioStasiak 124e45f
fix: patch package
OtavioStasiak dcb4b3c
fix: sslPinning
OtavioStasiak d7c2c11
feat: start android migration
OtavioStasiak 328434e
feat: start android migration
OtavioStasiak a3c635e
chore:remove logs
OtavioStasiak 7ea8b02
fix: android migration
OtavioStasiak 2146743
chore: code improvements
OtavioStasiak 52fb6eb
test: poc read old android data
OtavioStasiak b844781
feat: migrate from react-native-mmkv-storage data to react-native-mmkv
OtavioStasiak 5d9c9c4
fix: patch package
OtavioStasiak 097cdc8
fix: iOS build
OtavioStasiak 29fe1f1
Merge branch 'develop' into feat.migrate-react-native-mmkv-storage
OtavioStasiak 334f562
fix: ios build timeout
OtavioStasiak efcfcb3
fix: mmkvReader breaking on iOS
OtavioStasiak a4cbd8d
fix: build
OtavioStasiak 854e977
test: ios migration
OtavioStasiak 6a8b7d1
fix: use MMKVReader to migrate data on iOS
OtavioStasiak 8ef218b
Merge branch 'develop' into feat.migrate-react-native-mmkv-storage
OtavioStasiak 7c36d87
fix: iOS migration on native side
OtavioStasiak 5201d9c
test
OtavioStasiak 91067fb
Merge branch 'develop' into feat.migrate-react-native-mmkv-storage
OtavioStasiak 5fb2870
fix: migration timing
OtavioStasiak d2ed633
chore: format code and fix lint issues [skip ci]
OtavioStasiak b8abc6e
test
OtavioStasiak f9f4565
fix: unit test
OtavioStasiak ee4c809
fix: initialize mmkv storage on init
OtavioStasiak a899337
chore: format code and fix lint issues [skip ci]
OtavioStasiak faa6033
feat: improve logs
OtavioStasiak 100a077
chore: format code and fix lint issues [skip ci]
OtavioStasiak 30d1974
more logs
OtavioStasiak 9d329e8
Merge branch 'develop' into feat.migrate-react-native-mmkv-storage
OtavioStasiak 0667d5e
feat: improve debug on testflight
OtavioStasiak dee1164
chore: format code and fix lint issues [skip ci]
OtavioStasiak eb90469
error handling for debug
OtavioStasiak 133fb39
fix: encryption key diff
OtavioStasiak 8b324a9
fix: esport secureStorage key
OtavioStasiak 2c36c52
more logs
OtavioStasiak 7160e35
chore: format code and fix lint issues [skip ci]
OtavioStasiak 25466bc
test
OtavioStasiak 271c52d
chore: start cleanup
OtavioStasiak 5036e74
fix: remove logs on MMKVMigration
OtavioStasiak e6ff968
fix: remove MMKV logs on iOS
OtavioStasiak fd618e1
chore: format code and fix lint issues [skip ci]
OtavioStasiak 794dffe
fix: multiple servers not showing
OtavioStasiak e4c5de9
chore: format code and fix lint issues [skip ci]
OtavioStasiak b3d9e0a
fix: lint
OtavioStasiak 81e52da
fix: brinding header
OtavioStasiak 2abca4c
remove logs
OtavioStasiak 10db8cb
feat: increase timeout
OtavioStasiak 2715246
cleanup
OtavioStasiak acd1a51
cleanup
OtavioStasiak 6367507
testing to remove mmmkv reader
OtavioStasiak 01a4f6e
fix: lint
OtavioStasiak 5131262
chore: remove MMKVReader on iOS
OtavioStasiak febca2d
feat(android): enable setCertificate on webview
OtavioStasiak afe004a
chore: code improvements
OtavioStasiak 2070200
chore: code improvements
OtavioStasiak b309ff5
feat: code improvements
OtavioStasiak 6e51f84
feat: code improvements
OtavioStasiak 4b44b0d
chore: remove unused secureStorage methods on iOS
OtavioStasiak 44a7b95
feat: error handling MMKV
OtavioStasiak e032b8b
chore: remove unused methos on SecureKeystore.java
OtavioStasiak 32fcbf0
chore: remove logs on MMKVReader
OtavioStasiak a624eb7
chore: code improvements
OtavioStasiak a34f085
remove unused comments
OtavioStasiak f24ab27
remove unused methos on Storage.java
OtavioStasiak b9dea76
test: improve MMKV start logic on iOS
OtavioStasiak 4061c98
start mmkv on iOS
OtavioStasiak c7cca85
code improvements
OtavioStasiak 7357884
code improvements
OtavioStasiak c309a64
fix: lint
OtavioStasiak 6079c64
fix: add ARM ABI filters for Apple Silicon emulator compatibility
OtavioStasiak 136c0a3
e2e android build for all archictectures
OtavioStasiak ccd2fd6
fix: e2e build on android
OtavioStasiak 74b7866
fix: build
OtavioStasiak 229a823
fix: mmkv mock
OtavioStasiak 0125682
fix: initialize MMKV in MainApplication onCreate
OtavioStasiak ae37199
rollback e2e
OtavioStasiak b43a28a
fix: android build on e2e
OtavioStasiak 90f3f94
fix: android build on e2e
OtavioStasiak ef368e0
clear gradlew cache
OtavioStasiak 76501d5
clean cache build
OtavioStasiak f19cd64
fix: mock
OtavioStasiak 7f55e51
chore: format code and fix lint issues [skip ci]
OtavioStasiak 1ea269e
test: metroconfig
OtavioStasiak 0676560
metro fix
OtavioStasiak 7852730
rollback e2e build
OtavioStasiak 65bc60e
fix: e2e build
OtavioStasiak 9ff61d9
fix: metro config
OtavioStasiak f240ead
fix: build
OtavioStasiak 18560f7
fix: migrate old mmkv
OtavioStasiak 6207096
code improvements
OtavioStasiak 38fdd2c
code improvements
OtavioStasiak f299326
chore: avoid unecessary server validation
OtavioStasiak 434241d
code improvements
OtavioStasiak 5323218
improve comments
OtavioStasiak 3498353
rollback buid timeout
OtavioStasiak 5653229
fix: migration logic
OtavioStasiak 6312933
code improvements
OtavioStasiak 72d079c
fix: zero-pad bytes and handle signed values
OtavioStasiak 9a426e8
code improvements
OtavioStasiak fd47be8
fix: package.json and remove react-native-mmkv-storage of header sear…
OtavioStasiak 7b0ca79
code improvements
OtavioStasiak d6e8c59
fix: userPreferences
OtavioStasiak 3b72d61
code improvements
OtavioStasiak 32769fd
Merge branch 'develop' into feat.migrate-react-native-mmkv-storage
OtavioStasiak 4631acc
fix: mock
OtavioStasiak 545a0ed
chore: format code and fix lint issues [skip ci]
OtavioStasiak f521f76
Merge branch 'develop' into feat.migrate-react-native-mmkv-storage
OtavioStasiak 3b5bf88
chore: code improvements
OtavioStasiak 527195c
remove path on SSLPinning
OtavioStasiak 7305451
fix: lint
OtavioStasiak 1b9a4c6
fix: patch package
OtavioStasiak 57d83d3
Merge branch 'develop' into feat.migrate-react-native-mmkv-storage
diegolmello 242ce9b
feat(migration): Implement MMKV data migration from encrypted to unen…
diegolmello d7f18e3
refactor(migration): Remove legacy MMKV migration code and update com…
diegolmello 5899ca1
refactor(migration): Remove MMKVReader module and related files
diegolmello 71d3e4c
refactor(storage): Rename logging tags in SecureKeystore and Constants
diegolmello 3af7c56
fix mmkv write
diegolmello e16c64d
reKey android
diegolmello d66e5ff
refactor(migration): Simplify MMKV migration process and enhance logging
diegolmello 81d2f9e
Improve logging on iOS
diegolmello bd12cd1
Fix apple watch
diegolmello 5bc2139
Remove ensureServersInDatabase
diegolmello 3940c33
Move SecureStorage for consistency
diegolmello 02cba7a
Fix migration key iOS
diegolmello 4235ed5
Fix missing apple watch pieces
diegolmello d9a5c4d
cleanup
diegolmello 5355937
refactor(migration): Enhance MMKV encryption removal process and add …
diegolmello File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| // Mock for react-native-mmkv | ||
|
|
||
| // Shared storage between instances with the same id | ||
| const storageInstances = new Map(); | ||
|
|
||
| export const Mode = { | ||
| SINGLE_PROCESS: 1, | ||
| MULTI_PROCESS: 2 | ||
| }; | ||
|
|
||
| export class MMKV { | ||
| constructor(config = {}) { | ||
| const { id = 'default', mode, path } = config; | ||
| this.id = id; | ||
| this.mode = mode; | ||
| this.path = path; | ||
|
|
||
| // Share storage between instances with the same id | ||
| if (!storageInstances.has(this.id)) { | ||
| storageInstances.set(this.id, { | ||
| storage: new Map(), | ||
| listeners: [] | ||
| }); | ||
| } | ||
|
|
||
| const instance = storageInstances.get(this.id); | ||
| this.storage = instance.storage; | ||
| this.listeners = instance.listeners; | ||
| } | ||
|
|
||
| set(key, value) { | ||
| this.storage.set(key, value); | ||
| this.notifyListeners(key); | ||
| } | ||
|
|
||
| getString(key) { | ||
| const value = this.storage.get(key); | ||
| return typeof value === 'string' ? value : undefined; | ||
| } | ||
|
|
||
| getNumber(key) { | ||
| const value = this.storage.get(key); | ||
| return typeof value === 'number' ? value : undefined; | ||
| } | ||
|
|
||
| getBoolean(key) { | ||
| const value = this.storage.get(key); | ||
| return typeof value === 'boolean' ? value : undefined; | ||
| } | ||
|
|
||
| contains(key) { | ||
| return this.storage.has(key); | ||
| } | ||
|
|
||
| delete(key) { | ||
| const deleted = this.storage.delete(key); | ||
| if (deleted) { | ||
| this.notifyListeners(key); | ||
| } | ||
| return deleted; | ||
| } | ||
|
|
||
| getAllKeys() { | ||
| return Array.from(this.storage.keys()); | ||
| } | ||
|
|
||
| clearAll() { | ||
| this.storage.clear(); | ||
| // Notify about clear (pass undefined to indicate clear all) | ||
| this.notifyListeners(undefined); | ||
| } | ||
|
|
||
| addOnValueChangedListener(callback) { | ||
| this.listeners.push(callback); | ||
| return { | ||
| remove: () => { | ||
| const index = this.listeners.indexOf(callback); | ||
| if (index > -1) { | ||
| this.listeners.splice(index, 1); | ||
| } | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| notifyListeners(key) { | ||
| this.listeners.forEach((listener) => { | ||
| try { | ||
| listener(key); | ||
| } catch (error) { | ||
| console.error('Error in MMKV listener:', error); | ||
| } | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| // Export Configuration type for TypeScript | ||
| export const Configuration = {}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
163 changes: 163 additions & 0 deletions
163
android/app/src/main/java/chat/rocket/reactnative/SecureStorage.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,163 @@ | ||
| package chat.rocket.reactnative; | ||
|
|
||
| import android.content.Context; | ||
| import android.content.SharedPreferences; | ||
| import android.security.keystore.KeyGenParameterSpec; | ||
| import android.security.keystore.KeyProperties; | ||
| import android.util.Base64; | ||
| import android.util.Log; | ||
|
|
||
| import com.facebook.react.bridge.Promise; | ||
| import com.facebook.react.bridge.ReactApplicationContext; | ||
| import com.facebook.react.bridge.ReactContextBaseJavaModule; | ||
| import com.facebook.react.bridge.ReactMethod; | ||
|
|
||
| import java.nio.charset.StandardCharsets; | ||
| import java.security.KeyStore; | ||
| import java.util.UUID; | ||
|
|
||
| import javax.crypto.Cipher; | ||
| import javax.crypto.KeyGenerator; | ||
| import javax.crypto.SecretKey; | ||
| import javax.crypto.spec.GCMParameterSpec; | ||
|
|
||
| public class SecureStorage extends ReactContextBaseJavaModule { | ||
| private static final String TAG = "SecureStorage"; | ||
| private static final String KEYSTORE_PROVIDER = "AndroidKeyStore"; | ||
| private static final String PREFS_NAME = "SecureStoragePrefs"; | ||
| private static final String TRANSFORMATION = "AES/GCM/NoPadding"; | ||
| private static final int GCM_TAG_LENGTH = 128; | ||
|
|
||
| private final ReactApplicationContext reactContext; | ||
|
|
||
| public SecureStorage(ReactApplicationContext reactContext) { | ||
| super(reactContext); | ||
| this.reactContext = reactContext; | ||
| } | ||
|
|
||
| @Override | ||
| public String getName() { | ||
| return "SecureStorage"; | ||
| } | ||
|
|
||
| @ReactMethod | ||
| public void getSecureKey(String alias, Promise promise) { | ||
| try { | ||
| String value = getSecureKeyInternal(alias); | ||
| promise.resolve(value); | ||
| } catch (Exception e) { | ||
| Log.e(TAG, "Error getting secure key: " + alias, e); | ||
| promise.resolve(null); | ||
| } | ||
| } | ||
|
|
||
| @ReactMethod | ||
| public void setSecureKey(String alias, String value, Promise promise) { | ||
| try { | ||
| setSecureKeyInternal(alias, value); | ||
| promise.resolve(true); | ||
| } catch (Exception e) { | ||
| Log.e(TAG, "Error setting secure key: " + alias, e); | ||
| promise.reject("SET_SECURE_KEY_ERROR", e); | ||
| } | ||
| } | ||
|
|
||
| // Internal methods (can be called from Java) | ||
| public String getSecureKeyInternal(String alias) { | ||
| try { | ||
| SharedPreferences prefs = reactContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); | ||
| String encryptedValue = prefs.getString(alias, null); | ||
|
|
||
| if (encryptedValue == null) { | ||
| return null; | ||
| } | ||
|
|
||
| // Decrypt the value | ||
| KeyStore keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER); | ||
| keyStore.load(null); | ||
|
|
||
| if (!keyStore.containsAlias(alias)) { | ||
| return null; | ||
| } | ||
|
|
||
| SecretKey secretKey = (SecretKey) keyStore.getKey(alias, null); | ||
| Cipher cipher = Cipher.getInstance(TRANSFORMATION); | ||
|
|
||
| // Split IV and encrypted data | ||
| byte[] combined = Base64.decode(encryptedValue, Base64.DEFAULT); | ||
| byte[] iv = new byte[12]; | ||
| byte[] encrypted = new byte[combined.length - 12]; | ||
| System.arraycopy(combined, 0, iv, 0, 12); | ||
| System.arraycopy(combined, 12, encrypted, 0, encrypted.length); | ||
|
|
||
| GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv); | ||
| cipher.init(Cipher.DECRYPT_MODE, secretKey, spec); | ||
|
|
||
| byte[] decrypted = cipher.doFinal(encrypted); | ||
| return new String(decrypted, StandardCharsets.UTF_8); | ||
| } catch (Exception e) { | ||
| Log.e(TAG, "Error retrieving secure key", e); | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| public void setSecureKeyInternal(String alias, String value) throws Exception { | ||
| KeyStore keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER); | ||
| keyStore.load(null); | ||
|
|
||
| // Create key if it doesn't exist | ||
| if (!keyStore.containsAlias(alias)) { | ||
| KeyGenerator keyGenerator = KeyGenerator.getInstance( | ||
| KeyProperties.KEY_ALGORITHM_AES, | ||
| KEYSTORE_PROVIDER | ||
| ); | ||
| keyGenerator.init( | ||
| new KeyGenParameterSpec.Builder( | ||
| alias, | ||
| KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT | ||
| ) | ||
| .setBlockModes(KeyProperties.BLOCK_MODE_GCM) | ||
| .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) | ||
| .setRandomizedEncryptionRequired(true) | ||
| .build() | ||
| ); | ||
| keyGenerator.generateKey(); | ||
| } | ||
|
|
||
| // Encrypt the value | ||
| SecretKey secretKey = (SecretKey) keyStore.getKey(alias, null); | ||
| Cipher cipher = Cipher.getInstance(TRANSFORMATION); | ||
| cipher.init(Cipher.ENCRYPT_MODE, secretKey); | ||
|
|
||
| byte[] iv = cipher.getIV(); | ||
| byte[] encrypted = cipher.doFinal(value.getBytes(StandardCharsets.UTF_8)); | ||
|
|
||
| // Combine IV and encrypted data | ||
| byte[] combined = new byte[iv.length + encrypted.length]; | ||
| System.arraycopy(iv, 0, combined, 0, iv.length); | ||
| System.arraycopy(encrypted, 0, combined, iv.length, encrypted.length); | ||
|
|
||
| String encryptedValue = Base64.encodeToString(combined, Base64.DEFAULT); | ||
|
|
||
| // Store in SharedPreferences | ||
| SharedPreferences prefs = reactContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); | ||
| prefs.edit().putString(alias, encryptedValue).apply(); | ||
| } | ||
|
|
||
| // Generate a secure key if it doesn't exist | ||
| public String getOrCreateSecureKey(String alias) { | ||
| String key = getSecureKeyInternal(alias); | ||
| if (key == null) { | ||
| // Generate a new random key | ||
| key = UUID.randomUUID().toString(); | ||
| try { | ||
| setSecureKeyInternal(alias, key); | ||
| } catch (Exception e) { | ||
| Log.e(TAG, "Error creating secure key", e); | ||
| return null; | ||
| } | ||
| } | ||
| return key; | ||
| } | ||
| } | ||
|
|
25 changes: 25 additions & 0 deletions
25
android/app/src/main/java/chat/rocket/reactnative/SecureStoragePackage.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package chat.rocket.reactnative; | ||
|
|
||
| import com.facebook.react.ReactPackage; | ||
| import com.facebook.react.bridge.NativeModule; | ||
| import com.facebook.react.bridge.ReactApplicationContext; | ||
| import com.facebook.react.uimanager.ViewManager; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
|
|
||
| public class SecureStoragePackage implements ReactPackage { | ||
| @Override | ||
| public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { | ||
| List<NativeModule> modules = new ArrayList<>(); | ||
| modules.add(new SecureStorage(reactContext)); | ||
| return modules; | ||
| } | ||
|
|
||
| @Override | ||
| public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { | ||
| return Collections.emptyList(); | ||
| } | ||
| } | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.