[Gekidou] Get all existing server credentials on init (#5468)

* feat: get all existing server credentials on init

* fix: react-native-keychain patch

* fix: await in for loop

* fix: no need to normalize server URL
This commit is contained in:
Miguel Alatzar 2021-06-21 14:08:06 -07:00 committed by GitHub
parent 17e832e689
commit 3b9fd5c03a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 148 additions and 24 deletions

View file

@ -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<ServerCredential[]> => {
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<ServerCredentials|null> => {
export const getServerCredentials = async (serverUrl: string): Promise<ServerCredential|null> => {
try {
const credentials = await KeyChain.getInternetCredentials(serverUrl);
@ -94,7 +115,7 @@ export const getServerCredentials = async (serverUrl: string): Promise<ServerCre
// TODO: Create client and set token / CSRF
return {userId, token};
return {serverUrl, userId, token};
}
}

View file

@ -6,6 +6,7 @@ import 'react-native-gesture-handler';
import {ComponentDidAppearEvent, ComponentDidDisappearEvent, Navigation} from 'react-native-navigation';
import {Navigation as NavigationConstants, Screens} from './app/constants';
import {getAllServerCredentials} from './app/init/credentials';
import GlobalEventHandler from './app/init/global_event_handler';
import './app/init/fetch';
import {initialLaunch} from './app/init/launch';
@ -42,11 +43,19 @@ if (Platform.OS === 'android') {
AppRegistry.registerComponent('MattermostShare', () => 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':

View file

@ -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

View file

@ -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<NSString*>*)getAllServersForInternetPasswords
+{
+ NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ (__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnAttributes,
+ (__bridge id)kSecMatchLimitAll, (__bridge id)kSecMatchLimit,
+ nil];
+ NSMutableArray<NSString*> *servers = [NSMutableArray<NSString*> 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<NSString*>*)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<boolean> {
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<string[]> {
+ 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<string[]>;
+ function getAllInternetPasswordServers(): Promise<string[]>;
+
function hasInternetCredentials(server: string): Promise<false | Result>;
function setInternetCredentials(

View file

@ -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;
};