[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:
parent
17e832e689
commit
3b9fd5c03a
5 changed files with 148 additions and 24 deletions
|
|
@ -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};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
13
index.ts
13
index.ts
|
|
@ -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':
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
3
types/credentials/index.d.ts
vendored
3
types/credentials/index.d.ts
vendored
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue