forked from Ivasoft/mattermost-mobile
* Servers logout and websocket state * addNewServer uility and rename file * add LaunchType for add new server * added time to LaunchProps type * Remove unnecessary props for launchToHome * Fix local action updateLastPostAt * Batch fetchProfilesPerChannels requests in chunks of 50 * WS handleUserAddedToChannelEvent to return early if no channelId is set * WS handleNewPostEvent to batch update last_post_at * add common actions to sync other servers * Entry actions to sync other servers data * Do not attempt to fetch notification data if payload does not contain a channelId * Set database as default at the end of the login flow * Handle logout when other servers remain * Handle Server options * Show alert when logging out from the account screen * Add workaround to have Lottie animate the loading component * Fix badge position in ServerIcon component * Server screen to support adding new server * Fix login screen to display error when credentials do not match * add localization strings * fix DatabaseProvider to update on server switch * Fix home icon and server icon subscriptions and badge display * Add dependencies to onLogout callback * feedback * Only updateLastPostAt if needed
242 lines
7.8 KiB
TypeScript
242 lines
7.8 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import {DeviceEventEmitter} from 'react-native';
|
|
|
|
import {autoUpdateTimezone, getDeviceTimezone, isTimezoneEnabled} from '@actions/local/timezone';
|
|
import {Database, Events} from '@constants';
|
|
import {SYSTEM_IDENTIFIERS} from '@constants/database';
|
|
import DatabaseManager from '@database/manager';
|
|
import {getServerCredentials} from '@init/credentials';
|
|
import NetworkManager from '@init/network_manager';
|
|
import WebsocketManager from '@init/websocket_manager';
|
|
import {queryDeviceToken} from '@queries/app/global';
|
|
import {queryCurrentUserId, queryCommonSystemValues} from '@queries/servers/system';
|
|
import {getCSRFFromCookie} from '@utils/security';
|
|
|
|
import {loginEntry} from './entry';
|
|
import {logError} from './error';
|
|
import {fetchDataRetentionPolicy} from './systems';
|
|
|
|
import type ClientError from '@client/rest/error';
|
|
import type {LoginArgs} from '@typings/database/database';
|
|
|
|
const HTTP_UNAUTHORIZED = 401;
|
|
|
|
export const completeLogin = async (serverUrl: string, user: UserProfile) => {
|
|
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
|
if (!operator) {
|
|
return {error: `${serverUrl} database not found`};
|
|
}
|
|
|
|
const {database} = operator;
|
|
const {config, license}: { config: Partial<ClientConfig>; license: Partial<ClientLicense> } = await queryCommonSystemValues(database);
|
|
|
|
if (!Object.keys(config)?.length || !Object.keys(license)?.length) {
|
|
return null;
|
|
}
|
|
|
|
// Set timezone
|
|
if (isTimezoneEnabled(config)) {
|
|
const timezone = getDeviceTimezone();
|
|
await autoUpdateTimezone(serverUrl, {deviceTimezone: timezone, userId: user.id});
|
|
}
|
|
|
|
// Data retention
|
|
if (config?.DataRetentionEnableMessageDeletion === 'true' && license?.IsLicensed === 'true' && license?.DataRetention === 'true') {
|
|
fetchDataRetentionPolicy(serverUrl);
|
|
}
|
|
|
|
await DatabaseManager.setActiveServerDatabase(serverUrl);
|
|
|
|
// Start websocket
|
|
const credentials = await getServerCredentials(serverUrl);
|
|
if (credentials?.token) {
|
|
WebsocketManager.createClient(serverUrl, credentials.token);
|
|
return operator.handleSystem({systems: [{
|
|
id: SYSTEM_IDENTIFIERS.WEBSOCKET,
|
|
value: 0,
|
|
}],
|
|
prepareRecordsOnly: false});
|
|
}
|
|
return null;
|
|
};
|
|
|
|
export const forceLogoutIfNecessary = async (serverUrl: string, err: ClientErrorProps) => {
|
|
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
|
|
if (!database) {
|
|
return {error: `${serverUrl} database not found`};
|
|
}
|
|
|
|
const currentUserId = await queryCurrentUserId(database);
|
|
|
|
if ('status_code' in err && err.status_code === HTTP_UNAUTHORIZED && err?.url?.indexOf('/login') === -1 && currentUserId) {
|
|
await logout(serverUrl);
|
|
}
|
|
|
|
return {error: null};
|
|
};
|
|
|
|
export const getSessions = async (serverUrl: string, currentUserId: string) => {
|
|
let client;
|
|
try {
|
|
client = NetworkManager.getClient(serverUrl);
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
|
|
try {
|
|
return await client.getSessions(currentUserId);
|
|
} catch (e) {
|
|
logError(e);
|
|
await forceLogoutIfNecessary(serverUrl, e as ClientError);
|
|
}
|
|
|
|
return undefined;
|
|
};
|
|
|
|
export const login = async (serverUrl: string, {ldapOnly = false, loginId, mfaToken, password, config, serverDisplayName}: LoginArgs): Promise<LoginActionResponse> => {
|
|
let deviceToken;
|
|
let user: UserProfile;
|
|
|
|
const appDatabase = DatabaseManager.appDatabase?.database;
|
|
if (!appDatabase) {
|
|
return {error: 'App database not found.', failed: true};
|
|
}
|
|
|
|
let client;
|
|
try {
|
|
client = NetworkManager.getClient(serverUrl);
|
|
} catch (error) {
|
|
return {error: error as Error, failed: true};
|
|
}
|
|
|
|
try {
|
|
deviceToken = await queryDeviceToken(appDatabase);
|
|
user = await client.login(
|
|
loginId,
|
|
password,
|
|
mfaToken,
|
|
deviceToken,
|
|
ldapOnly,
|
|
);
|
|
|
|
const server = await DatabaseManager.createServerDatabase({
|
|
config: {
|
|
dbName: serverUrl,
|
|
serverUrl,
|
|
identifier: config.DiagnosticId,
|
|
displayName: serverDisplayName,
|
|
},
|
|
});
|
|
|
|
await server?.operator.handleUsers({users: [user], prepareRecordsOnly: false});
|
|
await server?.operator.handleSystem({
|
|
systems: [{
|
|
id: Database.SYSTEM_IDENTIFIERS.CURRENT_USER_ID,
|
|
value: user.id,
|
|
}],
|
|
prepareRecordsOnly: false,
|
|
});
|
|
const csrfToken = await getCSRFFromCookie(serverUrl);
|
|
client.setCSRFToken(csrfToken);
|
|
} catch (error) {
|
|
return {error: error as Error, failed: true};
|
|
}
|
|
|
|
try {
|
|
const {error, hasTeams, time} = await loginEntry({serverUrl, user});
|
|
completeLogin(serverUrl, user);
|
|
return {error: error as ClientError, failed: false, hasTeams, time};
|
|
} catch (error) {
|
|
return {error: error as ClientError, failed: false, time: 0};
|
|
}
|
|
};
|
|
|
|
export const logout = async (serverUrl: string, skipServerLogout = false) => {
|
|
if (!skipServerLogout) {
|
|
try {
|
|
const client = NetworkManager.getClient(serverUrl);
|
|
await client.logout();
|
|
} catch (error) {
|
|
// We want to log the user even if logging out from the server failed
|
|
// eslint-disable-next-line no-console
|
|
console.warn('An error ocurred loging out from the server', serverUrl, error);
|
|
}
|
|
}
|
|
|
|
DeviceEventEmitter.emit(Events.SERVER_LOGOUT, serverUrl);
|
|
};
|
|
|
|
export const sendPasswordResetEmail = async (serverUrl: string, email: string) => {
|
|
let client;
|
|
try {
|
|
client = NetworkManager.getClient(serverUrl);
|
|
} catch (error) {
|
|
return {error};
|
|
}
|
|
|
|
let response;
|
|
try {
|
|
response = await client.sendPasswordResetEmail(email);
|
|
} catch (error) {
|
|
return {error};
|
|
}
|
|
return {
|
|
data: response.data,
|
|
error: undefined,
|
|
};
|
|
};
|
|
|
|
export const ssoLogin = async (serverUrl: string, serverDisplayName: string, serverIdentifier: string, bearerToken: string, csrfToken: string): Promise<LoginActionResponse> => {
|
|
let deviceToken;
|
|
let user;
|
|
|
|
const database = DatabaseManager.appDatabase?.database;
|
|
if (!database) {
|
|
return {error: 'App database not found', failed: true};
|
|
}
|
|
|
|
let client;
|
|
try {
|
|
client = NetworkManager.getClient(serverUrl);
|
|
} catch (error) {
|
|
return {error: error as Error, failed: true};
|
|
}
|
|
|
|
client.setBearerToken(bearerToken);
|
|
client.setCSRFToken(csrfToken);
|
|
|
|
// Setting up active database for this SSO login flow
|
|
try {
|
|
const server = await DatabaseManager.createServerDatabase({
|
|
config: {
|
|
dbName: serverUrl,
|
|
serverUrl,
|
|
identifier: serverIdentifier,
|
|
displayName: serverDisplayName,
|
|
},
|
|
});
|
|
deviceToken = await queryDeviceToken(database);
|
|
user = await client.getMe();
|
|
await server?.operator.handleUsers({users: [user], prepareRecordsOnly: false});
|
|
await server?.operator.handleSystem({
|
|
systems: [{
|
|
id: Database.SYSTEM_IDENTIFIERS.CURRENT_USER_ID,
|
|
value: user.id,
|
|
}],
|
|
prepareRecordsOnly: false,
|
|
});
|
|
} catch (e) {
|
|
return {error: e as ClientError, failed: true};
|
|
}
|
|
|
|
try {
|
|
const {error, hasTeams, time} = await loginEntry({serverUrl, user, deviceToken});
|
|
completeLogin(serverUrl, user);
|
|
return {error: error as ClientError, failed: false, hasTeams, time};
|
|
} catch (error) {
|
|
return {error: error as ClientError, failed: false, time: 0};
|
|
}
|
|
};
|