diff --git a/app/init/credentials.ts b/app/init/credentials.ts index 4f889e49a..db309b57b 100644 --- a/app/init/credentials.ts +++ b/app/init/credentials.ts @@ -1,8 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Platform} from 'react-native'; import AsyncStorage from '@react-native-community/async-storage'; +import {Platform} from 'react-native'; import * as KeyChain from 'react-native-keychain'; import DatabaseManager from '@database/manager'; @@ -10,10 +10,31 @@ import * as analytics from '@init/analytics'; import {getIOSAppGroupDetails} from '@utils/mattermost_managed'; import {getCSRFFromCookie} from '@utils/security'; -import type {ServerCredentials} from '@typings/credentials'; +import type {ServerCredential} from '@typings/credentials'; const ASYNC_STORAGE_CURRENT_SERVER_KEY = '@currentServerUrl'; +export const getAllServerCredentials = async (): Promise => { + const serverCredentials: ServerCredential[] = []; + + let serverUrls: string[]; + if (Platform.OS === 'ios') { + serverUrls = await KeyChain.getAllInternetPasswordServers(); + } else { + serverUrls = await KeyChain.getAllGenericPasswordServices(); + } + + for await (const serverUrl of serverUrls) { + const serverCredential = await getServerCredentials(serverUrl); + + if (serverCredential) { + serverCredentials.push(serverCredential); + } + } + + return serverCredentials; +}; + // TODO: This function is only necessary to support pre-Gekidou // versions as the active server URL may be stored in AsyncStorage. // At some point we can remove this function and rely solely on @@ -71,7 +92,7 @@ export const removeActiveServerCredentials = async () => { } }; -export const getServerCredentials = async (serverUrl: string): Promise => { +export const getServerCredentials = async (serverUrl: string): Promise => { try { const credentials = await KeyChain.getInternetCredentials(serverUrl); @@ -94,7 +115,7 @@ export const getServerCredentials = async (serverUrl: string): Promise ShareExtension); } -Navigation.events().registerAppLaunchedListener(() => { +Navigation.events().registerAppLaunchedListener(async () => { GlobalEventHandler.init(); ManagedApp.init(); registerNavigationListeners(); registerScreens(); + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const serverCredentials = await getAllServerCredentials(); + + // TODO: + // DatabaseManager.init(serverCredentials); + // NetworkClientManager.init(serverCredentials); + initialLaunch(); }); @@ -60,7 +69,7 @@ function componentDidAppearListener({componentId}: ComponentDidAppearEvent) { switch (componentId) { case 'MainSidebar': - DeviceEventEmitter.emit(NavigationConstants.MAIN_SIDEBAR_DID_OPEN, this.handleSidebarDidOpen); + DeviceEventEmitter.emit(NavigationConstants.MAIN_SIDEBAR_DID_OPEN); DeviceEventEmitter.emit(NavigationConstants.BLUR_POST_DRAFT); break; case 'SettingsSidebar': diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 279ad7122..c21d4d19d 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -20,15 +20,15 @@ PODS: - glog (0.3.5) - jail-monkey (2.3.3): - React - - libwebp (1.1.0): - - libwebp/demux (= 1.1.0) - - libwebp/mux (= 1.1.0) - - libwebp/webp (= 1.1.0) - - libwebp/demux (1.1.0): + - libwebp (1.2.0): + - libwebp/demux (= 1.2.0) + - libwebp/mux (= 1.2.0) + - libwebp/webp (= 1.2.0) + - libwebp/demux (1.2.0): - libwebp/webp - - libwebp/mux (1.1.0): + - libwebp/mux (1.2.0): - libwebp/demux - - libwebp/webp (1.1.0) + - libwebp/webp (1.2.0) - Permission-Camera (3.0.2): - RNPermissions - Permission-PhotoLibrary (3.0.2): @@ -403,10 +403,10 @@ PODS: - React - RNVectorIcons (8.1.0): - React-Core - - Rudder (1.0.11) - - SDWebImage (5.9.4): - - SDWebImage/Core (= 5.9.4) - - SDWebImage/Core (5.9.4) + - Rudder (1.0.14) + - SDWebImage (5.11.1): + - SDWebImage/Core (= 5.11.1) + - SDWebImage/Core (5.11.1) - SDWebImageWebPCoder (0.6.1): - libwebp (~> 1.0) - SDWebImage/Core (~> 5.7) @@ -441,7 +441,7 @@ PODS: - React-jsi - XCDYouTubeKit (2.8.2) - Yoga (1.14.0) - - YoutubePlayer-in-WKWebView (0.3.4) + - YoutubePlayer-in-WKWebView (0.3.5) DEPENDENCIES: - BVLinearGradient (from `../node_modules/react-native-linear-gradient`) @@ -716,10 +716,10 @@ SPEC CHECKSUMS: EXConstants: c00cd53a17a65b2e53ddb3890e4e74d3418e406e EXFileSystem: 35769beb727d5341d1276fd222710f9704f7164e FBLazyVector: 49cbe4b43e445b06bf29199b6ad2057649e4c8f5 - FBReactNativeSpec: a804c9d6c798f94831713302354003ee54ea18cb + FBReactNativeSpec: 0eb0a77cec90f681f2a6e58baba2224a1597e3ed glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62 jail-monkey: 80c9e34da2cd54023e5ad08bf7051ec75bd43d5b - libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3 + libwebp: e90b9c01d99205d03b6bb8f2c8c415e5a4ef66f0 Permission-Camera: 4eb4a67be32698c5927cb12313c3c1f23ee5b7b2 Permission-PhotoLibrary: 5b198e177c0bc5c91c20e66ae7202d0183c78b87 RCT-Folly: ec7a233ccc97cc556cf7237f0db1ff65b986f27c @@ -779,8 +779,8 @@ SPEC CHECKSUMS: RNShare: 31fa0cedbd06c2744a78e0d2b7ba364778aa3506 RNSVG: 551acb6562324b1d52a4e0758f7ca0ec234e278f RNVectorIcons: 31cebfcf94e8cf8686eb5303ae0357da64d7a5a4 - Rudder: ef340f877a39653f19e69124dffae12fde3f881b - SDWebImage: b69257f4ab14e9b6a2ef53e910fdf914d8f757c1 + Rudder: 40f3a255fab3f8bbe120e496f90019de68c1aca1 + SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d SDWebImageWebPCoder: d0dac55073088d24b2ac1b191a71a8f8d0adac21 Sentry: 9d055e2de30a77685e86b219acf02e59b82091fc Swime: d7b2c277503b6cea317774aedc2dce05613f8b0b @@ -800,7 +800,7 @@ SPEC CHECKSUMS: WatermelonDB: e6706a0fac2221e2be4bdc93ff0e3990231d91bd XCDYouTubeKit: 79baadb0560673a67c771eba45f83e353fd12c1f Yoga: 8c8436d4171c87504c648ae23b1d81242bdf3bbf - YoutubePlayer-in-WKWebView: af2f5929fc78882d94bfdfeea999b661b78d9717 + YoutubePlayer-in-WKWebView: cfbf46da51d7370662a695a8f351e5fa1d3e1008 PODFILE CHECKSUM: 34335a8d329b001fdfa695c4fb3c8928db80f375 diff --git a/patches/react-native-keychain+7.0.0.patch b/patches/react-native-keychain+7.0.0.patch index 435b48cba..01e13b5e8 100644 --- a/patches/react-native-keychain+7.0.0.patch +++ b/patches/react-native-keychain+7.0.0.patch @@ -1,3 +1,57 @@ +diff --git a/node_modules/react-native-keychain/RNKeychainManager/RNKeychainManager.m b/node_modules/react-native-keychain/RNKeychainManager/RNKeychainManager.m +index 38ccdf3..bdc4713 100644 +--- a/node_modules/react-native-keychain/RNKeychainManager/RNKeychainManager.m ++++ b/node_modules/react-native-keychain/RNKeychainManager/RNKeychainManager.m +@@ -272,6 +272,34 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server + return SecItemDelete((__bridge CFDictionaryRef) query); + } + ++-(NSArray*)getAllServersForInternetPasswords ++{ ++ NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys: ++ (__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnAttributes, ++ (__bridge id)kSecMatchLimitAll, (__bridge id)kSecMatchLimit, ++ nil]; ++ NSMutableArray *servers = [NSMutableArray new]; ++ ++ [query setObject:(__bridge id)kSecClassInternetPassword forKey:(__bridge id)kSecClass]; ++ NSArray *result = nil; ++ CFTypeRef resultRef = NULL; ++ OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef*)&resultRef); ++ if (osStatus != noErr && osStatus != errSecItemNotFound) { ++ NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil]; ++ @throw error; ++ } else if (osStatus != errSecItemNotFound) { ++ result = (__bridge NSArray*)(resultRef); ++ if (result != NULL) { ++ for (id entry in result) { ++ NSString *server = [entry objectForKey:(__bridge NSString *)kSecAttrServer]; ++ [servers addObject:server]; ++ } ++ } ++ } ++ ++ return servers; ++} ++ + -(NSArray*)getAllServicesForSecurityClasses:(NSArray *)secItemClasses + { + NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys: +@@ -592,4 +620,14 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server + } + } + ++RCT_EXPORT_METHOD(getAllInternetPasswordServers:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) ++{ ++ @try { ++ NSArray *servers = [self getAllServersForInternetPasswords]; ++ return resolve(servers); ++ } @catch (NSError *nsError) { ++ return rejectWithError(reject, nsError); ++ } ++} ++ + @end diff --git a/node_modules/react-native-keychain/android/src/main/java/com/oblador/keychain/KeychainModule.java b/node_modules/react-native-keychain/android/src/main/java/com/oblador/keychain/KeychainModule.java index 3da433f..112cc74 100644 --- a/node_modules/react-native-keychain/android/src/main/java/com/oblador/keychain/KeychainModule.java @@ -57,3 +111,42 @@ index 3da433f..112cc74 100644 promise.resolve(credentials); } catch (KeyStoreAccessException e) { +diff --git a/node_modules/react-native-keychain/index.js b/node_modules/react-native-keychain/index.js +index b73cfb2..a754505 100644 +--- a/node_modules/react-native-keychain/index.js ++++ b/node_modules/react-native-keychain/index.js +@@ -348,6 +348,21 @@ export function canImplyAuthentication(options?: Options): Promise { + return RNKeychainManager.canCheckAuthentication(options); + } + ++/** ++ * Gets all `kSecAttrServer` values used in internet credentials for iOS. ++ * @return {Promise} Resolves to an array of strings ++ */ ++ export async function getAllInternetPasswordServers(): Promise { ++ if (Platform.OS !== 'ios') { ++ return Promise.reject( ++ new Error( ++ `getAllInternetPasswordServers() is not supported on ${Platform.OS}` ++ ) ++ ); ++ } ++ return RNKeychainManager.getAllInternetPasswordServers(); ++} ++ + //* ANDROID ONLY */ + + /** +diff --git a/node_modules/react-native-keychain/typings/react-native-keychain.d.ts b/node_modules/react-native-keychain/typings/react-native-keychain.d.ts +index af2eb56..038047b 100644 +--- a/node_modules/react-native-keychain/typings/react-native-keychain.d.ts ++++ b/node_modules/react-native-keychain/typings/react-native-keychain.d.ts +@@ -97,6 +97,8 @@ declare module 'react-native-keychain' { + + function getAllGenericPasswordServices(): Promise; + ++ function getAllInternetPasswordServers(): Promise; ++ + function hasInternetCredentials(server: string): Promise; + + function setInternetCredentials( diff --git a/types/credentials/index.d.ts b/types/credentials/index.d.ts index 7df45fa09..d64a2c013 100644 --- a/types/credentials/index.d.ts +++ b/types/credentials/index.d.ts @@ -1,7 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -export type ServerCredentials = { +export type ServerCredential = { + serverUrl: string; userId: string; token: string; };