-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Expand file tree
/
Copy pathinit.js
More file actions
144 lines (130 loc) · 5.36 KB
/
Copy pathinit.js
File metadata and controls
144 lines (130 loc) · 5.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import { call, put, select, takeLatest } from 'redux-saga/effects';
import RNBootSplash from 'react-native-bootsplash';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { CURRENT_SERVER, TOKEN_KEY, getUserTokenKey } from '../lib/constants/keys';
import UserPreferences from '../lib/methods/userPreferences';
import { selectServerRequest } from '../actions/server';
import { setAllPreferences } from '../actions/sortPreferences';
import { APP } from '../actions/actionsTypes';
import log from '../lib/methods/helpers/log';
import database from '../lib/database';
import { localAuthenticate } from '../lib/methods/helpers/localAuthentication';
import { appReady, appStart } from '../actions/app';
import { RootEnum } from '../definitions';
import { getSortPreferences } from '../lib/methods/userPreferencesMethods';
import { deepLinkingClickCallPush } from '../actions/deepLinking';
import { getServerById } from '../lib/database/services/Server';
export const initLocalSettings = function* initLocalSettings() {
const sortPreferences = getSortPreferences();
yield put(setAllPreferences(sortPreferences));
};
const TOKEN_KEY_SERVER_SCOPED_MIGRATED = 'RC_TOKEN_KEY_SERVER_SCOPED_MIGRATED';
/**
* One-time migration: move auth tokens from the legacy non-server-scoped slot
* `${TOKEN_KEY}-${userId}` to the server-scoped slot `${TOKEN_KEY}-${server}-${userId}`.
* See `getUserTokenKey` for why the legacy scheme was ambiguous (token confusion).
*
* Only userIds referenced by a single server are migrated: the legacy slot can hold just one
* token, so when several servers share a userId we can't tell which server it belongs to.
* Migrating an ambiguous token would plant one server's token into another server's slot —
* exactly the confusion this change exists to prevent — so those slots are dropped instead,
* forcing a safe re-authentication.
*/
export const migrateTokenKeysToServerScoped = function* migrateTokenKeysToServerScoped() {
try {
if (UserPreferences.getBool(TOKEN_KEY_SERVER_SCOPED_MIGRATED)) {
return;
}
const serversDB = database.servers;
const servers = yield serversDB.get('servers').query().fetch();
// Map each server to its userId and count how many servers reference each userId.
const serverUserIds = [];
const serverCountByUserId = {};
for (let i = 0; i < servers.length; i += 1) {
const server = servers[i].id;
const userId = UserPreferences.getString(`${TOKEN_KEY}-${server}`);
if (!userId) {
continue;
}
serverUserIds.push({ server, userId });
serverCountByUserId[userId] = (serverCountByUserId[userId] || 0) + 1;
}
// Collected in a Set so an ambiguous userId shared by N servers is removed once.
const legacyKeys = new Set();
for (let i = 0; i < serverUserIds.length; i += 1) {
const { server, userId } = serverUserIds[i];
const legacyKey = `${TOKEN_KEY}-${userId}`;
// Ambiguous: don't migrate, just drop the legacy slot so the session re-authenticates.
if (serverCountByUserId[userId] > 1) {
legacyKeys.add(legacyKey);
continue;
}
const newKey = getUserTokenKey(server, userId);
if (!UserPreferences.getString(newKey)) {
const token = UserPreferences.getString(legacyKey);
if (token) {
UserPreferences.setString(newKey, token);
legacyKeys.add(legacyKey);
}
}
}
// Drop the legacy slots (migrated and ambiguous alike) now that the migration is done.
legacyKeys.forEach(key => UserPreferences.removeItem(key));
UserPreferences.setBool(TOKEN_KEY_SERVER_SCOPED_MIGRATED, true);
} catch (e) {
log(e);
}
};
const restore = function* restore() {
try {
yield call(migrateTokenKeysToServerScoped);
const server = UserPreferences.getString(CURRENT_SERVER);
let userId = UserPreferences.getString(`${TOKEN_KEY}-${server}`);
if (!server) {
yield put(appStart({ root: RootEnum.ROOT_OUTSIDE }));
} else if (!userId) {
const serversDB = database.servers;
const serversCollection = serversDB.get('servers');
const servers = yield serversCollection.query().fetch();
// Check if there're other logged in servers and picks first one
if (servers.length > 0) {
for (let i = 0; i < servers.length; i += 1) {
const newServer = servers[i].id;
userId = UserPreferences.getString(`${TOKEN_KEY}-${newServer}`);
if (userId) {
return yield put(selectServerRequest(newServer, newServer.version));
}
}
}
yield put(appStart({ root: RootEnum.ROOT_OUTSIDE }));
} else {
yield localAuthenticate(server);
const serverRecord = yield getServerById(server);
if (!serverRecord) {
return;
}
yield put(selectServerRequest(server, serverRecord.version));
}
yield put(appReady({}));
const pushNotification = yield call(AsyncStorage.getItem, 'pushNotification');
if (pushNotification) {
const pushNotification = yield call(AsyncStorage.removeItem, 'pushNotification');
yield call(deepLinkingClickCallPush, JSON.parse(pushNotification));
}
} catch (e) {
log(e);
yield put(appStart({ root: RootEnum.ROOT_OUTSIDE }));
}
};
const start = function* start() {
const currentRoot = yield select(state => state.app.root);
if (currentRoot !== RootEnum.ROOT_LOADING_SHARE_EXTENSION) {
yield RNBootSplash.hide({ fade: true });
}
};
const root = function* root() {
yield takeLatest(APP.INIT, restore);
yield takeLatest(APP.START, start);
yield takeLatest(APP.INIT_LOCAL_SETTINGS, initLocalSettings);
};
export default root;