mattermost-mobile/app/actions/app/server.ts
Elias Nahum a5a1e53827
Biometric prompt, Jailbreak / Root detection and screenshot prevention (#8645)
* Handle biometric authentication

* jailbreak/root detection and biometric small fixes

* remove server from initializeSecurityManager and fix loginEntry

* Add screen capture prevention and other small fixes

* added unit tests to SecurityManager

* added shielded nativeID to protect views

* use MobilePreventScreenCapture instead of MobileAllowScreenshots in config type definition

* Apply Swizzle for screen capture on iOS

* Apply patch to bottom sheet to prevent screen captures

* fix ios sendReply

* Fix SDWebImage swizzle to use the correct session

* Fix potential crash on Android when using hardware keyboard

* rename patch for network library to remove warning

* add temp emm reference

* fix initializeSecurityManager tests

* fix translations

* use siteName for jailbreak detection when connecting to a new server

* fix i18n typo

* do not query the entire config from the db only the required fields

* migrate manage_apps to use defineMessages

* use TestHelper.wait in tests

* use defineMessages for security manager

* fix missing else statement for gm_to_channel

* created a TestHelper function to mockQuery and replace as jest.Mock with jest.mocked

* fix unit tests

* fix unit tests (again) and include setting the test environment to UTC

* Fix keyboard disappearing on iOS

* update react-native-emm
2025-03-13 14:07:41 -04:00

125 lines
4.4 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Navigation} from 'react-native-navigation';
import {doPing} from '@actions/remote/general';
import {fetchConfigAndLicense} from '@actions/remote/systems';
import {Screens} from '@constants';
import DatabaseManager from '@database/manager';
import SecurityManager from '@managers/security_manager';
import WebsocketManager from '@managers/websocket_manager';
import {getServer, getServerByIdentifier, queryAllActiveServers} from '@queries/app/servers';
import {getSecurityConfig} from '@queries/servers/system';
import {logError} from '@utils/log';
import {canReceiveNotifications} from '@utils/push_proxy';
import {alertServerAlreadyConnected, alertServerError, loginToServer} from '@utils/server';
import type {IntlShape} from 'react-intl';
export async function initializeSecurityManager() {
const servers = await queryAllActiveServers()?.fetch();
if (!servers?.length) {
return;
}
const promises: Array<Promise<[string, SecurityClientConfig | null]>> = [];
const results: Record<string, SecurityClientConfig> = {};
for (const server of servers) {
try {
const {database} = DatabaseManager.getServerDatabaseAndOperator(server.url);
const promise = getSecurityConfig(database).then((config) => [server.url, config] as [string, SecurityClientConfig | null]);
promises.push(promise);
} catch (error) {
logError('initializeSecurityManager', error);
continue;
}
}
const resolvedConfigs = await Promise.allSettled(promises);
for (const result of resolvedConfigs) {
if (result.status === 'fulfilled') {
const [url, config] = result.value;
if (config && Object.keys(config).length > 0) { // Ensure the object is not empty
results[url] = config;
}
}
}
const serverUrl = await DatabaseManager.getActiveServerUrl();
SecurityManager.init(results, serverUrl);
}
export async function switchToServer(serverUrl: string, theme: Theme, intl: IntlShape, callback?: () => void) {
const server = await getServer(serverUrl);
if (!server) {
logError(`Switch to Server with url ${serverUrl} not found`);
return;
}
if (server.lastActiveAt) {
const isJailbroken = await SecurityManager.isDeviceJailbroken(server.url);
if (isJailbroken) {
return;
}
const authenticated = await SecurityManager.authenticateWithBiometricsIfNeeded(server.url);
if (authenticated) {
Navigation.updateProps(Screens.HOME, {extra: undefined});
DatabaseManager.setActiveServerDatabase(server.url);
SecurityManager.setActiveServer(server.url);
WebsocketManager.initializeClient(server.url, 'Server Switch');
}
return;
}
switchToServerAndLogin(serverUrl, theme, intl, callback);
}
export async function switchToServerAndLogin(serverUrl: string, theme: Theme, intl: IntlShape, callback?: () => void) {
const server = await getServer(serverUrl);
if (!server) {
logError(`Switch to Server with url ${serverUrl} not found`);
return;
}
const result = await doPing(server.url, true);
if (result.error) {
alertServerError(intl, result.error);
callback?.();
return;
}
const data = await fetchConfigAndLicense(server.url, true);
if (data.error) {
alertServerError(intl, data.error);
callback?.();
return;
}
const existingServer = await getServerByIdentifier(data.config!.DiagnosticId);
if (existingServer && existingServer.lastActiveAt > 0) {
alertServerAlreadyConnected(intl);
callback?.();
return;
}
if (data.config?.MobileJailbreakProtection === 'true') {
const isJailbroken = await SecurityManager.isDeviceJailbroken(server.url);
if (isJailbroken) {
callback?.();
return;
}
}
let authenticated = true;
if (data.config?.MobileEnableBiometrics === 'true') {
authenticated = await SecurityManager.authenticateWithBiometrics(server.url, data.config?.SiteName);
}
if (authenticated) {
canReceiveNotifications(server.url, result.canReceiveNotifications as string, intl);
loginToServer(theme, server.url, server.displayName, data.config!, data.license!);
}
}