@polkadot/extension-base 0.59.2 → 0.60.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/background/handlers/Extension.js +34 -34
- package/background/handlers/State.js +87 -69
- package/background/handlers/Tabs.js +21 -21
- package/cjs/background/handlers/Extension.js +34 -34
- package/cjs/background/handlers/State.js +87 -69
- package/cjs/background/handlers/Tabs.js +21 -21
- package/cjs/packageInfo.js +1 -1
- package/cjs/page/PostMessageProvider.js +15 -15
- package/cjs/stores/Base.js +7 -7
- package/package.json +14 -14
- package/packageInfo.js +1 -1
- package/page/PostMessageProvider.js +15 -15
- package/stores/Base.js +7 -7
|
@@ -19,16 +19,16 @@ function isJsonPayload(value) {
|
|
|
19
19
|
return value.genesisHash !== undefined;
|
|
20
20
|
}
|
|
21
21
|
export default class Extension {
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
#cachedUnlocks;
|
|
23
|
+
#state;
|
|
24
24
|
constructor(state) {
|
|
25
|
-
this
|
|
26
|
-
this
|
|
25
|
+
this.#cachedUnlocks = {};
|
|
26
|
+
this.#state = state;
|
|
27
27
|
}
|
|
28
28
|
transformAccounts(accounts) {
|
|
29
29
|
return Object.values(accounts).map(({ json: { address, meta }, type }) => ({
|
|
30
30
|
address,
|
|
31
|
-
isDefaultAuthSelected: this.
|
|
31
|
+
isDefaultAuthSelected: this.#state.defaultAuthAccountSelection.includes(address),
|
|
32
32
|
...meta,
|
|
33
33
|
type
|
|
34
34
|
}));
|
|
@@ -77,26 +77,26 @@ export default class Extension {
|
|
|
77
77
|
async accountsForget({ address }) {
|
|
78
78
|
const authorizedAccountsDiff = [];
|
|
79
79
|
// cycle through authUrls and prepare the array of diff
|
|
80
|
-
Object.entries(this.
|
|
80
|
+
Object.entries(this.#state.authUrls).forEach(([url, urlInfo]) => {
|
|
81
81
|
// Note that urlInfo.authorizedAccounts may be undefined if this website entry
|
|
82
82
|
// was created before the "account authorization per website" functionality was introduced
|
|
83
83
|
if (urlInfo.authorizedAccounts?.includes(address)) {
|
|
84
84
|
authorizedAccountsDiff.push([url, urlInfo.authorizedAccounts.filter((previousAddress) => previousAddress !== address)]);
|
|
85
85
|
}
|
|
86
86
|
});
|
|
87
|
-
await this.
|
|
87
|
+
await this.#state.updateAuthorizedAccounts(authorizedAccountsDiff);
|
|
88
88
|
// cycle through default account selection for auth and remove any occurrence of the account
|
|
89
|
-
const newDefaultAuthAccounts = this.
|
|
90
|
-
await this.
|
|
89
|
+
const newDefaultAuthAccounts = this.#state.defaultAuthAccountSelection.filter((defaultSelectionAddress) => defaultSelectionAddress !== address);
|
|
90
|
+
await this.#state.updateDefaultAuthAccounts(newDefaultAuthAccounts);
|
|
91
91
|
keyring.forgetAccount(address);
|
|
92
92
|
return true;
|
|
93
93
|
}
|
|
94
94
|
refreshAccountPasswordCache(pair) {
|
|
95
95
|
const { address } = pair;
|
|
96
|
-
const savedExpiry = this
|
|
96
|
+
const savedExpiry = this.#cachedUnlocks[address] || 0;
|
|
97
97
|
const remainingTime = savedExpiry - Date.now();
|
|
98
98
|
if (remainingTime < 0) {
|
|
99
|
-
this
|
|
99
|
+
this.#cachedUnlocks[address] = 0;
|
|
100
100
|
pair.lock();
|
|
101
101
|
return 0;
|
|
102
102
|
}
|
|
@@ -133,22 +133,22 @@ export default class Extension {
|
|
|
133
133
|
return true;
|
|
134
134
|
}
|
|
135
135
|
authorizeApprove({ authorizedAccounts, id }) {
|
|
136
|
-
const queued = this.
|
|
136
|
+
const queued = this.#state.getAuthRequest(id);
|
|
137
137
|
assert(queued, 'Unable to find request');
|
|
138
138
|
const { resolve } = queued;
|
|
139
139
|
resolve({ authorizedAccounts, result: true });
|
|
140
140
|
return true;
|
|
141
141
|
}
|
|
142
142
|
async authorizeUpdate({ authorizedAccounts, url }) {
|
|
143
|
-
return await this.
|
|
143
|
+
return await this.#state.updateAuthorizedAccounts([[url, authorizedAccounts]]);
|
|
144
144
|
}
|
|
145
145
|
getAuthList() {
|
|
146
|
-
return { list: this.
|
|
146
|
+
return { list: this.#state.authUrls };
|
|
147
147
|
}
|
|
148
148
|
// FIXME This looks very much like what we have in accounts
|
|
149
149
|
authorizeSubscribe(id, port) {
|
|
150
150
|
const cb = createSubscription(id, port);
|
|
151
|
-
const subscription = this.
|
|
151
|
+
const subscription = this.#state.authSubject.subscribe((requests) => cb(requests));
|
|
152
152
|
port.onDisconnect.addListener(() => {
|
|
153
153
|
unsubscribe(id);
|
|
154
154
|
subscription.unsubscribe();
|
|
@@ -156,21 +156,21 @@ export default class Extension {
|
|
|
156
156
|
return true;
|
|
157
157
|
}
|
|
158
158
|
async metadataApprove({ id }) {
|
|
159
|
-
const queued = this.
|
|
159
|
+
const queued = this.#state.getMetaRequest(id);
|
|
160
160
|
assert(queued, 'Unable to find request');
|
|
161
161
|
const { request, resolve } = queued;
|
|
162
|
-
await this.
|
|
162
|
+
await this.#state.saveMetadata(request);
|
|
163
163
|
resolve(true);
|
|
164
164
|
return true;
|
|
165
165
|
}
|
|
166
166
|
metadataGet(genesisHash) {
|
|
167
|
-
return this.
|
|
167
|
+
return this.#state.knownMetadata.find((result) => result.genesisHash === genesisHash) || null;
|
|
168
168
|
}
|
|
169
169
|
metadataList() {
|
|
170
|
-
return this.
|
|
170
|
+
return this.#state.knownMetadata;
|
|
171
171
|
}
|
|
172
172
|
metadataReject({ id }) {
|
|
173
|
-
const queued = this.
|
|
173
|
+
const queued = this.#state.getMetaRequest(id);
|
|
174
174
|
assert(queued, 'Unable to find request');
|
|
175
175
|
const { reject } = queued;
|
|
176
176
|
reject(new Error('Rejected'));
|
|
@@ -178,7 +178,7 @@ export default class Extension {
|
|
|
178
178
|
}
|
|
179
179
|
metadataSubscribe(id, port) {
|
|
180
180
|
const cb = createSubscription(id, port);
|
|
181
|
-
const subscription = this.
|
|
181
|
+
const subscription = this.#state.metaSubject.subscribe((requests) => cb(requests));
|
|
182
182
|
port.onDisconnect.addListener(() => {
|
|
183
183
|
unsubscribe(id);
|
|
184
184
|
subscription.unsubscribe();
|
|
@@ -239,7 +239,7 @@ export default class Extension {
|
|
|
239
239
|
};
|
|
240
240
|
}
|
|
241
241
|
signingApprovePassword({ id, password, savePass }) {
|
|
242
|
-
const queued = this.
|
|
242
|
+
const queued = this.#state.getSignRequest(id);
|
|
243
243
|
assert(queued, 'Unable to find request');
|
|
244
244
|
const { reject, request, resolve } = queued;
|
|
245
245
|
const pair = keyring.getPair(queued.account.address);
|
|
@@ -260,7 +260,7 @@ export default class Extension {
|
|
|
260
260
|
const { payload } = request;
|
|
261
261
|
if (isJsonPayload(payload)) {
|
|
262
262
|
// Get the metadata for the genesisHash
|
|
263
|
-
const metadata = this.
|
|
263
|
+
const metadata = this.#state.knownMetadata.find(({ genesisHash }) => genesisHash === payload.genesisHash);
|
|
264
264
|
if (metadata) {
|
|
265
265
|
// we have metadata, expand it and extract the info/registry
|
|
266
266
|
const expanded = metadataExpand(metadata, false);
|
|
@@ -282,7 +282,7 @@ export default class Extension {
|
|
|
282
282
|
// unlike queued.account.address the following
|
|
283
283
|
// address is encoded with the default prefix
|
|
284
284
|
// which what is used for password caching mapping
|
|
285
|
-
this
|
|
285
|
+
this.#cachedUnlocks[pair.address] = Date.now() + PASSWORD_EXPIRY_MS;
|
|
286
286
|
}
|
|
287
287
|
else {
|
|
288
288
|
pair.lock();
|
|
@@ -294,21 +294,21 @@ export default class Extension {
|
|
|
294
294
|
return true;
|
|
295
295
|
}
|
|
296
296
|
signingApproveSignature({ id, signature, signedTransaction }) {
|
|
297
|
-
const queued = this.
|
|
297
|
+
const queued = this.#state.getSignRequest(id);
|
|
298
298
|
assert(queued, 'Unable to find request');
|
|
299
299
|
const { resolve } = queued;
|
|
300
300
|
resolve({ id, signature, signedTransaction });
|
|
301
301
|
return true;
|
|
302
302
|
}
|
|
303
303
|
signingCancel({ id }) {
|
|
304
|
-
const queued = this.
|
|
304
|
+
const queued = this.#state.getSignRequest(id);
|
|
305
305
|
assert(queued, 'Unable to find request');
|
|
306
306
|
const { reject } = queued;
|
|
307
307
|
reject(new Error('Cancelled'));
|
|
308
308
|
return true;
|
|
309
309
|
}
|
|
310
310
|
signingIsLocked({ id }) {
|
|
311
|
-
const queued = this.
|
|
311
|
+
const queued = this.#state.getSignRequest(id);
|
|
312
312
|
assert(queued, 'Unable to find request');
|
|
313
313
|
const address = queued.request.payload.address;
|
|
314
314
|
const pair = keyring.getPair(address);
|
|
@@ -322,7 +322,7 @@ export default class Extension {
|
|
|
322
322
|
// FIXME This looks very much like what we have in authorization
|
|
323
323
|
signingSubscribe(id, port) {
|
|
324
324
|
const cb = createSubscription(id, port);
|
|
325
|
-
const subscription = this.
|
|
325
|
+
const subscription = this.#state.signSubject.subscribe((requests) => cb(requests));
|
|
326
326
|
port.onDisconnect.addListener(() => {
|
|
327
327
|
unsubscribe(id);
|
|
328
328
|
subscription.unsubscribe();
|
|
@@ -371,13 +371,13 @@ export default class Extension {
|
|
|
371
371
|
return true;
|
|
372
372
|
}
|
|
373
373
|
async removeAuthorization(url) {
|
|
374
|
-
const remAuth = await this.
|
|
374
|
+
const remAuth = await this.#state.removeAuthorization(url);
|
|
375
375
|
return { list: remAuth };
|
|
376
376
|
}
|
|
377
377
|
// Reject the authorization request and add the URL to the authorized list with no keys.
|
|
378
378
|
// The site will not prompt for re-authorization on future visits.
|
|
379
379
|
rejectAuthRequest(id) {
|
|
380
|
-
const queued = this.
|
|
380
|
+
const queued = this.#state.getAuthRequest(id);
|
|
381
381
|
assert(queued, 'Unable to find request');
|
|
382
382
|
const { reject } = queued;
|
|
383
383
|
reject(new Error('Rejected'));
|
|
@@ -385,16 +385,16 @@ export default class Extension {
|
|
|
385
385
|
// Cancel the authorization request and do not add the URL to the authorized list.
|
|
386
386
|
// The site will prompt for authorization on future visits.
|
|
387
387
|
cancelAuthRequest(id) {
|
|
388
|
-
const queued = this.
|
|
388
|
+
const queued = this.#state.getAuthRequest(id);
|
|
389
389
|
assert(queued, 'Unable to find request');
|
|
390
390
|
const { reject } = queued;
|
|
391
391
|
reject(new Error('Cancelled'));
|
|
392
392
|
}
|
|
393
393
|
updateCurrentTabs({ urls }) {
|
|
394
|
-
this.
|
|
394
|
+
this.#state.updateCurrentTabsUrl(urls);
|
|
395
395
|
}
|
|
396
396
|
getConnectedTabsUrl() {
|
|
397
|
-
return this.
|
|
397
|
+
return this.#state.getConnectedTabsUrl();
|
|
398
398
|
}
|
|
399
399
|
// Weird thought, the eslint override is not needed in Tabs
|
|
400
400
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
@@ -469,7 +469,7 @@ export default class Extension {
|
|
|
469
469
|
case 'pri(seed.validate)':
|
|
470
470
|
return this.seedValidate(request);
|
|
471
471
|
case 'pri(settings.notification)':
|
|
472
|
-
return this.
|
|
472
|
+
return this.#state.setNotification(request);
|
|
473
473
|
case 'pri(signing.approve.password)':
|
|
474
474
|
return this.signingApprovePassword(request);
|
|
475
475
|
case 'pri(signing.approve.signature)':
|
|
@@ -63,35 +63,35 @@ async function extractMetadata(store) {
|
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
65
|
export default class State {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
#authUrls = new Map();
|
|
67
|
+
#authRequests = {};
|
|
68
|
+
#metaStore = new MetadataStore();
|
|
69
69
|
// Map of providers currently injected in tabs
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
#injectedProviders = new Map();
|
|
71
|
+
#metaRequests = {};
|
|
72
|
+
#notification = settings.notification;
|
|
73
73
|
// Map of all providers exposed by the extension, they are retrievable by key
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
#providers;
|
|
75
|
+
#signRequests = {};
|
|
76
|
+
#windows = [];
|
|
77
|
+
#connectedTabsUrl = [];
|
|
78
78
|
authSubject = new BehaviorSubject([]);
|
|
79
79
|
metaSubject = new BehaviorSubject([]);
|
|
80
80
|
signSubject = new BehaviorSubject([]);
|
|
81
81
|
authUrlSubjects = {};
|
|
82
82
|
defaultAuthAccountSelection = [];
|
|
83
83
|
constructor(providers = {}) {
|
|
84
|
-
this
|
|
84
|
+
this.#providers = providers;
|
|
85
85
|
}
|
|
86
86
|
async init() {
|
|
87
|
-
await extractMetadata(this
|
|
87
|
+
await extractMetadata(this.#metaStore);
|
|
88
88
|
// retrieve previously set authorizations
|
|
89
89
|
const storageAuthUrls = await chrome.storage.local.get(AUTH_URLS_KEY);
|
|
90
90
|
const authString = storageAuthUrls?.[AUTH_URLS_KEY] || '{}';
|
|
91
91
|
const previousAuth = JSON.parse(authString);
|
|
92
|
-
this
|
|
92
|
+
this.#authUrls = new Map(Object.entries(previousAuth));
|
|
93
93
|
// Initialize authUrlSubjects for each URL
|
|
94
|
-
|
|
94
|
+
this.#authUrls.forEach((authInfo, url) => {
|
|
95
95
|
this.authUrlSubjects[url] = new BehaviorSubject(authInfo);
|
|
96
96
|
});
|
|
97
97
|
// retrieve previously set default auth accounts
|
|
@@ -104,49 +104,49 @@ export default class State {
|
|
|
104
104
|
return knownMetadata();
|
|
105
105
|
}
|
|
106
106
|
get numAuthRequests() {
|
|
107
|
-
return Object.keys(this
|
|
107
|
+
return Object.keys(this.#authRequests).length;
|
|
108
108
|
}
|
|
109
109
|
get numMetaRequests() {
|
|
110
|
-
return Object.keys(this
|
|
110
|
+
return Object.keys(this.#metaRequests).length;
|
|
111
111
|
}
|
|
112
112
|
get numSignRequests() {
|
|
113
|
-
return Object.keys(this
|
|
113
|
+
return Object.keys(this.#signRequests).length;
|
|
114
114
|
}
|
|
115
115
|
get allAuthRequests() {
|
|
116
116
|
return Object
|
|
117
|
-
.values(this
|
|
117
|
+
.values(this.#authRequests)
|
|
118
118
|
.map(({ id, request, url }) => ({ id, request, url }));
|
|
119
119
|
}
|
|
120
120
|
get allMetaRequests() {
|
|
121
121
|
return Object
|
|
122
|
-
.values(this
|
|
122
|
+
.values(this.#metaRequests)
|
|
123
123
|
.map(({ id, request, url }) => ({ id, request, url }));
|
|
124
124
|
}
|
|
125
125
|
get allSignRequests() {
|
|
126
126
|
return Object
|
|
127
|
-
.values(this
|
|
127
|
+
.values(this.#signRequests)
|
|
128
128
|
.map(({ account, id, request, url }) => ({ account, id, request, url }));
|
|
129
129
|
}
|
|
130
130
|
get authUrls() {
|
|
131
|
-
return this
|
|
131
|
+
return Object.fromEntries(this.#authUrls);
|
|
132
132
|
}
|
|
133
133
|
popupClose() {
|
|
134
|
-
this.
|
|
135
|
-
this
|
|
134
|
+
this.#windows.forEach((id) => withErrorLog(() => chrome.windows.remove(id)));
|
|
135
|
+
this.#windows = [];
|
|
136
136
|
}
|
|
137
137
|
popupOpen() {
|
|
138
|
-
this
|
|
139
|
-
chrome.windows.create(this
|
|
138
|
+
this.#notification !== 'extension' &&
|
|
139
|
+
chrome.windows.create(this.#notification === 'window'
|
|
140
140
|
? NORMAL_WINDOW_OPTS
|
|
141
141
|
: POPUP_WINDOW_OPTS, (window) => {
|
|
142
142
|
if (window) {
|
|
143
|
-
this.
|
|
143
|
+
this.#windows.push(window.id || 0);
|
|
144
144
|
}
|
|
145
145
|
});
|
|
146
146
|
}
|
|
147
147
|
authComplete = (id, resolve, reject) => {
|
|
148
148
|
const complete = async (authorizedAccounts = []) => {
|
|
149
|
-
const { idStr, request: { origin }, url } = this
|
|
149
|
+
const { idStr, request: { origin }, url } = this.#authRequests[id];
|
|
150
150
|
const strippedUrl = this.stripUrl(url);
|
|
151
151
|
const authInfo = {
|
|
152
152
|
authorizedAccounts,
|
|
@@ -155,7 +155,7 @@ export default class State {
|
|
|
155
155
|
origin,
|
|
156
156
|
url
|
|
157
157
|
};
|
|
158
|
-
this.
|
|
158
|
+
this.#authUrls.set(strippedUrl, authInfo);
|
|
159
159
|
if (!this.authUrlSubjects[strippedUrl]) {
|
|
160
160
|
this.authUrlSubjects[strippedUrl] = new BehaviorSubject(authInfo);
|
|
161
161
|
}
|
|
@@ -164,14 +164,14 @@ export default class State {
|
|
|
164
164
|
}
|
|
165
165
|
await this.saveCurrentAuthList();
|
|
166
166
|
await this.updateDefaultAuthAccounts(authorizedAccounts);
|
|
167
|
-
delete this
|
|
167
|
+
delete this.#authRequests[id];
|
|
168
168
|
this.updateIconAuth(true);
|
|
169
169
|
};
|
|
170
170
|
return {
|
|
171
171
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
172
172
|
reject: async (error) => {
|
|
173
173
|
if (error.message === 'Cancelled') {
|
|
174
|
-
delete this
|
|
174
|
+
delete this.#authRequests[id];
|
|
175
175
|
this.updateIconAuth(true);
|
|
176
176
|
reject(new Error('Connection request was cancelled by the user.'));
|
|
177
177
|
}
|
|
@@ -209,17 +209,17 @@ export default class State {
|
|
|
209
209
|
: undefined;
|
|
210
210
|
})
|
|
211
211
|
.filter((value) => !!value);
|
|
212
|
-
this
|
|
212
|
+
this.#connectedTabsUrl = connectedTabs;
|
|
213
213
|
}
|
|
214
214
|
getConnectedTabsUrl() {
|
|
215
|
-
return this
|
|
215
|
+
return this.#connectedTabsUrl;
|
|
216
216
|
}
|
|
217
217
|
deleteAuthRequest(requestId) {
|
|
218
|
-
delete this
|
|
218
|
+
delete this.#authRequests[requestId];
|
|
219
219
|
this.updateIconAuth(true);
|
|
220
220
|
}
|
|
221
221
|
async saveCurrentAuthList() {
|
|
222
|
-
await chrome.storage.local.set({ [AUTH_URLS_KEY]: JSON.stringify(this
|
|
222
|
+
await chrome.storage.local.set({ [AUTH_URLS_KEY]: JSON.stringify(Object.fromEntries(this.#authUrls)) });
|
|
223
223
|
}
|
|
224
224
|
async saveDefaultAuthAccounts() {
|
|
225
225
|
await chrome.storage.local.set({ [DEFAULT_AUTH_ACCOUNTS]: JSON.stringify(this.defaultAuthAccountSelection) });
|
|
@@ -230,7 +230,7 @@ export default class State {
|
|
|
230
230
|
}
|
|
231
231
|
metaComplete = (id, resolve, reject) => {
|
|
232
232
|
const complete = () => {
|
|
233
|
-
delete this
|
|
233
|
+
delete this.#metaRequests[id];
|
|
234
234
|
this.updateIconMeta(true);
|
|
235
235
|
};
|
|
236
236
|
return {
|
|
@@ -246,7 +246,7 @@ export default class State {
|
|
|
246
246
|
};
|
|
247
247
|
signComplete = (id, resolve, reject) => {
|
|
248
248
|
const complete = () => {
|
|
249
|
-
delete this
|
|
249
|
+
delete this.#signRequests[id];
|
|
250
250
|
this.updateIconSign(true);
|
|
251
251
|
};
|
|
252
252
|
return {
|
|
@@ -261,9 +261,22 @@ export default class State {
|
|
|
261
261
|
};
|
|
262
262
|
};
|
|
263
263
|
stripUrl(url) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
264
|
+
try {
|
|
265
|
+
const parsedUrl = new URL(url);
|
|
266
|
+
if (!['http:', 'https:', 'ipfs:', 'ipns:'].includes(parsedUrl.protocol)) {
|
|
267
|
+
throw new Error(`Invalid protocol ${parsedUrl.protocol}`);
|
|
268
|
+
}
|
|
269
|
+
// For ipfs/ipns which don't have a standard origin, we handle it differently.
|
|
270
|
+
if (parsedUrl.protocol === 'ipfs:' || parsedUrl.protocol === 'ipns:') {
|
|
271
|
+
// ipfs://<hash> | ipns://<hash>
|
|
272
|
+
return `${parsedUrl.protocol}//${parsedUrl.hostname}`;
|
|
273
|
+
}
|
|
274
|
+
return parsedUrl.origin;
|
|
275
|
+
}
|
|
276
|
+
catch (e) {
|
|
277
|
+
console.error(e);
|
|
278
|
+
throw new Error('Invalid URL');
|
|
279
|
+
}
|
|
267
280
|
}
|
|
268
281
|
updateIcon(shouldClose) {
|
|
269
282
|
const authCount = this.numAuthRequests;
|
|
@@ -280,15 +293,15 @@ export default class State {
|
|
|
280
293
|
}
|
|
281
294
|
}
|
|
282
295
|
async removeAuthorization(url) {
|
|
283
|
-
const entry = this.
|
|
296
|
+
const entry = this.#authUrls.get(url);
|
|
284
297
|
assert(entry, `The source ${url} is not known`);
|
|
285
|
-
|
|
298
|
+
this.#authUrls.delete(url);
|
|
286
299
|
await this.saveCurrentAuthList();
|
|
287
300
|
if (this.authUrlSubjects[url]) {
|
|
288
301
|
entry.authorizedAccounts = [];
|
|
289
302
|
this.authUrlSubjects[url].next(entry);
|
|
290
303
|
}
|
|
291
|
-
return this.
|
|
304
|
+
return this.authUrls;
|
|
292
305
|
}
|
|
293
306
|
updateIconAuth(shouldClose) {
|
|
294
307
|
this.authSubject.next(this.allAuthRequests);
|
|
@@ -304,8 +317,12 @@ export default class State {
|
|
|
304
317
|
}
|
|
305
318
|
async updateAuthorizedAccounts(authorizedAccountsDiff) {
|
|
306
319
|
authorizedAccountsDiff.forEach(([url, authorizedAccountDiff]) => {
|
|
307
|
-
this.
|
|
308
|
-
|
|
320
|
+
const authInfo = this.#authUrls.get(url);
|
|
321
|
+
if (authInfo) {
|
|
322
|
+
authInfo.authorizedAccounts = authorizedAccountDiff;
|
|
323
|
+
this.#authUrls.set(url, authInfo);
|
|
324
|
+
this.authUrlSubjects[url].next(authInfo);
|
|
325
|
+
}
|
|
309
326
|
});
|
|
310
327
|
await this.saveCurrentAuthList();
|
|
311
328
|
}
|
|
@@ -313,12 +330,13 @@ export default class State {
|
|
|
313
330
|
const idStr = this.stripUrl(url);
|
|
314
331
|
// Do not enqueue duplicate authorization requests.
|
|
315
332
|
const isDuplicate = Object
|
|
316
|
-
.values(this
|
|
333
|
+
.values(this.#authRequests)
|
|
317
334
|
.some((request) => request.idStr === idStr);
|
|
318
335
|
assert(!isDuplicate, `The source ${url} has a pending authorization request`);
|
|
319
|
-
if (this.
|
|
336
|
+
if (this.#authUrls.has(idStr)) {
|
|
320
337
|
// this url was seen in the past
|
|
321
|
-
|
|
338
|
+
const authInfo = this.#authUrls.get(idStr);
|
|
339
|
+
assert(authInfo?.authorizedAccounts || authInfo?.isAllowed, `The source ${url} is not allowed to interact with this extension`);
|
|
322
340
|
return {
|
|
323
341
|
authorizedAccounts: [],
|
|
324
342
|
result: false
|
|
@@ -326,7 +344,7 @@ export default class State {
|
|
|
326
344
|
}
|
|
327
345
|
return new Promise((resolve, reject) => {
|
|
328
346
|
const id = getId();
|
|
329
|
-
this
|
|
347
|
+
this.#authRequests[id] = {
|
|
330
348
|
...this.authComplete(id, resolve, reject),
|
|
331
349
|
id,
|
|
332
350
|
idStr,
|
|
@@ -338,14 +356,14 @@ export default class State {
|
|
|
338
356
|
});
|
|
339
357
|
}
|
|
340
358
|
ensureUrlAuthorized(url) {
|
|
341
|
-
const entry = this.
|
|
359
|
+
const entry = this.#authUrls.get(this.stripUrl(url));
|
|
342
360
|
assert(entry, `The source ${url} has not been enabled yet`);
|
|
343
361
|
return true;
|
|
344
362
|
}
|
|
345
363
|
injectMetadata(url, request) {
|
|
346
364
|
return new Promise((resolve, reject) => {
|
|
347
365
|
const id = getId();
|
|
348
|
-
this
|
|
366
|
+
this.#metaRequests[id] = {
|
|
349
367
|
...this.metaComplete(id, resolve, reject),
|
|
350
368
|
id,
|
|
351
369
|
request,
|
|
@@ -356,73 +374,73 @@ export default class State {
|
|
|
356
374
|
});
|
|
357
375
|
}
|
|
358
376
|
getAuthRequest(id) {
|
|
359
|
-
return this
|
|
377
|
+
return this.#authRequests[id];
|
|
360
378
|
}
|
|
361
379
|
getMetaRequest(id) {
|
|
362
|
-
return this
|
|
380
|
+
return this.#metaRequests[id];
|
|
363
381
|
}
|
|
364
382
|
getSignRequest(id) {
|
|
365
|
-
return this
|
|
383
|
+
return this.#signRequests[id];
|
|
366
384
|
}
|
|
367
385
|
// List all providers the extension is exposing
|
|
368
386
|
rpcListProviders() {
|
|
369
|
-
return Promise.resolve(Object.keys(this
|
|
370
|
-
acc[key] = this
|
|
387
|
+
return Promise.resolve(Object.keys(this.#providers).reduce((acc, key) => {
|
|
388
|
+
acc[key] = this.#providers[key].meta;
|
|
371
389
|
return acc;
|
|
372
390
|
}, {}));
|
|
373
391
|
}
|
|
374
392
|
rpcSend(request, port) {
|
|
375
|
-
const provider = this.
|
|
393
|
+
const provider = this.#injectedProviders.get(port);
|
|
376
394
|
assert(provider, 'Cannot call pub(rpc.subscribe) before provider is set');
|
|
377
395
|
return provider.send(request.method, request.params);
|
|
378
396
|
}
|
|
379
397
|
// Start a provider, return its meta
|
|
380
398
|
rpcStartProvider(key, port) {
|
|
381
|
-
assert(Object.keys(this
|
|
382
|
-
if (this.
|
|
383
|
-
return Promise.resolve(this
|
|
399
|
+
assert(Object.keys(this.#providers).includes(key), `Provider ${key} is not exposed by extension`);
|
|
400
|
+
if (this.#injectedProviders.get(port)) {
|
|
401
|
+
return Promise.resolve(this.#providers[key].meta);
|
|
384
402
|
}
|
|
385
403
|
// Instantiate the provider
|
|
386
|
-
this.
|
|
404
|
+
this.#injectedProviders.set(port, this.#providers[key].start());
|
|
387
405
|
// Close provider connection when page is closed
|
|
388
406
|
port.onDisconnect.addListener(() => {
|
|
389
|
-
const provider = this.
|
|
407
|
+
const provider = this.#injectedProviders.get(port);
|
|
390
408
|
if (provider) {
|
|
391
409
|
withErrorLog(() => provider.disconnect());
|
|
392
410
|
}
|
|
393
|
-
this.
|
|
411
|
+
this.#injectedProviders.delete(port);
|
|
394
412
|
});
|
|
395
|
-
return Promise.resolve(this
|
|
413
|
+
return Promise.resolve(this.#providers[key].meta);
|
|
396
414
|
}
|
|
397
415
|
rpcSubscribe({ method, params, type }, cb, port) {
|
|
398
|
-
const provider = this.
|
|
416
|
+
const provider = this.#injectedProviders.get(port);
|
|
399
417
|
assert(provider, 'Cannot call pub(rpc.subscribe) before provider is set');
|
|
400
418
|
return provider.subscribe(type, method, params, cb);
|
|
401
419
|
}
|
|
402
420
|
rpcSubscribeConnected(_request, cb, port) {
|
|
403
|
-
const provider = this.
|
|
421
|
+
const provider = this.#injectedProviders.get(port);
|
|
404
422
|
assert(provider, 'Cannot call pub(rpc.subscribeConnected) before provider is set');
|
|
405
423
|
cb(null, provider.isConnected); // Immediately send back current isConnected
|
|
406
424
|
provider.on('connected', () => cb(null, true));
|
|
407
425
|
provider.on('disconnected', () => cb(null, false));
|
|
408
426
|
}
|
|
409
427
|
rpcUnsubscribe(request, port) {
|
|
410
|
-
const provider = this.
|
|
428
|
+
const provider = this.#injectedProviders.get(port);
|
|
411
429
|
assert(provider, 'Cannot call pub(rpc.unsubscribe) before provider is set');
|
|
412
430
|
return provider.unsubscribe(request.type, request.method, request.subscriptionId);
|
|
413
431
|
}
|
|
414
432
|
async saveMetadata(meta) {
|
|
415
|
-
await this.
|
|
433
|
+
await this.#metaStore.set(meta.genesisHash, meta);
|
|
416
434
|
addMetadata(meta);
|
|
417
435
|
}
|
|
418
436
|
setNotification(notification) {
|
|
419
|
-
this
|
|
437
|
+
this.#notification = notification;
|
|
420
438
|
return true;
|
|
421
439
|
}
|
|
422
440
|
sign(url, request, account) {
|
|
423
441
|
const id = getId();
|
|
424
442
|
return new Promise((resolve, reject) => {
|
|
425
|
-
this
|
|
443
|
+
this.#signRequests[id] = {
|
|
426
444
|
...this.signComplete(id, resolve, reject),
|
|
427
445
|
account,
|
|
428
446
|
id,
|