@metamask/permission-controller 12.1.1 → 12.2.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/CHANGELOG.md +28 -1
- package/dist/Caveat.cjs.map +1 -1
- package/dist/Caveat.mjs.map +1 -1
- package/dist/PermissionController.cjs +406 -642
- package/dist/PermissionController.cjs.map +1 -1
- package/dist/PermissionController.d.cts +10 -270
- package/dist/PermissionController.d.cts.map +1 -1
- package/dist/PermissionController.d.mts +10 -270
- package/dist/PermissionController.d.mts.map +1 -1
- package/dist/PermissionController.mjs +406 -642
- package/dist/PermissionController.mjs.map +1 -1
- package/dist/SubjectMetadataController.cjs +42 -40
- package/dist/SubjectMetadataController.cjs.map +1 -1
- package/dist/SubjectMetadataController.d.cts +1 -17
- package/dist/SubjectMetadataController.d.cts.map +1 -1
- package/dist/SubjectMetadataController.d.mts +1 -17
- package/dist/SubjectMetadataController.d.mts.map +1 -1
- package/dist/SubjectMetadataController.mjs +42 -40
- package/dist/SubjectMetadataController.mjs.map +1 -1
- package/dist/errors.cjs +8 -7
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +4 -9
- package/dist/errors.d.cts.map +1 -1
- package/dist/errors.d.mts +4 -9
- package/dist/errors.d.mts.map +1 -1
- package/dist/errors.mjs +1 -0
- package/dist/errors.mjs.map +1 -1
- package/dist/permission-middleware.cjs +1 -1
- package/dist/permission-middleware.cjs.map +1 -1
- package/dist/permission-middleware.d.cts +4 -3
- package/dist/permission-middleware.d.cts.map +1 -1
- package/dist/permission-middleware.d.mts +4 -3
- package/dist/permission-middleware.d.mts.map +1 -1
- package/dist/permission-middleware.mjs +1 -1
- package/dist/permission-middleware.mjs.map +1 -1
- package/dist/rpc-methods/revokePermissions.cjs.map +1 -1
- package/dist/rpc-methods/revokePermissions.d.cts +1 -1
- package/dist/rpc-methods/revokePermissions.d.cts.map +1 -1
- package/dist/rpc-methods/revokePermissions.d.mts +1 -1
- package/dist/rpc-methods/revokePermissions.d.mts.map +1 -1
- package/dist/rpc-methods/revokePermissions.mjs.map +1 -1
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts.map +1 -1
- package/dist/utils.d.mts.map +1 -1
- package/dist/utils.mjs.map +1 -1
- package/package.json +9 -13
|
@@ -3,7 +3,13 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
3
3
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
4
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
5
|
};
|
|
6
|
-
var
|
|
6
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
7
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
10
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
|
+
};
|
|
12
|
+
var _PermissionController_instances, _PermissionController_caveatSpecifications, _PermissionController_permissionSpecifications, _PermissionController_unrestrictedMethods, _PermissionController_getPermissionSpecification, _PermissionController_getCaveatSpecification, _PermissionController_expectGetCaveatMerger, _PermissionController_validatePermissionSpecifications, _PermissionController_registerMessageHandlers, _PermissionController_getTypedPermissionSpecification, _PermissionController_deletePermission, _PermissionController_setCaveat, _PermissionController_deleteCaveat, _PermissionController_validateModifiedPermission, _PermissionController_targetExists, _PermissionController_applyGrantedPermissions, _PermissionController_validatePermission, _PermissionController_setValidatedPermissions, _PermissionController_constructCaveats, _PermissionController_validateCaveat, _PermissionController_validateRequestedPermissions, _PermissionController_mergeIncrementalPermissions, _PermissionController_mergePermission, _PermissionController_mergeCaveat, _PermissionController_requestUserApproval, _PermissionController_handleApprovedPermissions, _PermissionController_getSideEffects, _PermissionController_executeSideEffects, _PermissionController_validateApprovedPermissions, _PermissionController_hasApprovalRequest, _PermissionController_rejectPermissionsRequest, _PermissionController_executeRestrictedMethod;
|
|
7
13
|
function $importDefault(module) {
|
|
8
14
|
if (module?.__esModule) {
|
|
9
15
|
return module.default;
|
|
@@ -82,7 +88,7 @@ export class PermissionController extends BaseController {
|
|
|
82
88
|
* @returns The names of all unrestricted JSON-RPC methods
|
|
83
89
|
*/
|
|
84
90
|
get unrestrictedMethods() {
|
|
85
|
-
return this
|
|
91
|
+
return __classPrivateFieldGet(this, _PermissionController_unrestrictedMethods, "f");
|
|
86
92
|
}
|
|
87
93
|
/**
|
|
88
94
|
* Constructs the PermissionController.
|
|
@@ -113,96 +119,22 @@ export class PermissionController extends BaseController {
|
|
|
113
119
|
},
|
|
114
120
|
});
|
|
115
121
|
_PermissionController_instances.add(this);
|
|
116
|
-
this
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
this
|
|
122
|
+
_PermissionController_caveatSpecifications.set(this, void 0);
|
|
123
|
+
_PermissionController_permissionSpecifications.set(this, void 0);
|
|
124
|
+
_PermissionController_unrestrictedMethods.set(this, void 0);
|
|
125
|
+
__classPrivateFieldSet(this, _PermissionController_unrestrictedMethods, new Set(unrestrictedMethods), "f");
|
|
126
|
+
__classPrivateFieldSet(this, _PermissionController_caveatSpecifications, deepFreeze({ ...caveatSpecifications }), "f");
|
|
127
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_validatePermissionSpecifications).call(this, permissionSpecifications, __classPrivateFieldGet(this, _PermissionController_caveatSpecifications, "f"));
|
|
128
|
+
__classPrivateFieldSet(this, _PermissionController_permissionSpecifications, deepFreeze({
|
|
120
129
|
...permissionSpecifications,
|
|
121
|
-
});
|
|
122
|
-
this.
|
|
130
|
+
}), "f");
|
|
131
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_registerMessageHandlers).call(this);
|
|
123
132
|
this.createPermissionMiddleware = getPermissionMiddlewareFactory({
|
|
124
|
-
executeRestrictedMethod: this.
|
|
133
|
+
executeRestrictedMethod: __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_executeRestrictedMethod).bind(this),
|
|
125
134
|
getRestrictedMethod: this.getRestrictedMethod.bind(this),
|
|
126
135
|
isUnrestrictedMethod: this.unrestrictedMethods.has.bind(this.unrestrictedMethods),
|
|
127
136
|
});
|
|
128
137
|
}
|
|
129
|
-
/**
|
|
130
|
-
* Gets a permission specification.
|
|
131
|
-
*
|
|
132
|
-
* @param targetName - The name of the permission specification to get.
|
|
133
|
-
* @returns The permission specification with the specified target name.
|
|
134
|
-
*/
|
|
135
|
-
getPermissionSpecification(targetName) {
|
|
136
|
-
return this._permissionSpecifications[targetName];
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Gets a caveat specification.
|
|
140
|
-
*
|
|
141
|
-
* @param caveatType - The type of the caveat specification to get.
|
|
142
|
-
* @returns The caveat specification with the specified type.
|
|
143
|
-
*/
|
|
144
|
-
getCaveatSpecification(caveatType) {
|
|
145
|
-
return this._caveatSpecifications[caveatType];
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Constructor helper for validating permission specifications.
|
|
149
|
-
*
|
|
150
|
-
* Throws an error if validation fails.
|
|
151
|
-
*
|
|
152
|
-
* @param permissionSpecifications - The permission specifications passed to
|
|
153
|
-
* this controller's constructor.
|
|
154
|
-
* @param caveatSpecifications - The caveat specifications passed to this
|
|
155
|
-
* controller.
|
|
156
|
-
*/
|
|
157
|
-
validatePermissionSpecifications(permissionSpecifications, caveatSpecifications) {
|
|
158
|
-
Object.entries(permissionSpecifications).forEach(([targetName, { permissionType, targetName: innerTargetName, allowedCaveats },]) => {
|
|
159
|
-
if (!permissionType || !hasProperty(PermissionType, permissionType)) {
|
|
160
|
-
throw new Error(`Invalid permission type: "${permissionType}"`);
|
|
161
|
-
}
|
|
162
|
-
if (!targetName) {
|
|
163
|
-
throw new Error(`Invalid permission target name: "${targetName}"`);
|
|
164
|
-
}
|
|
165
|
-
if (targetName !== innerTargetName) {
|
|
166
|
-
throw new Error(`Invalid permission specification: target name "${targetName}" must match specification.targetName value "${innerTargetName}".`);
|
|
167
|
-
}
|
|
168
|
-
if (allowedCaveats) {
|
|
169
|
-
allowedCaveats.forEach((caveatType) => {
|
|
170
|
-
if (!hasProperty(caveatSpecifications, caveatType)) {
|
|
171
|
-
throw new UnrecognizedCaveatTypeError(caveatType);
|
|
172
|
-
}
|
|
173
|
-
const specification = caveatSpecifications[caveatType];
|
|
174
|
-
const isRestrictedMethodCaveat = isRestrictedMethodCaveatSpecification(specification);
|
|
175
|
-
if ((permissionType === PermissionType.RestrictedMethod &&
|
|
176
|
-
!isRestrictedMethodCaveat) ||
|
|
177
|
-
(permissionType === PermissionType.Endowment &&
|
|
178
|
-
isRestrictedMethodCaveat)) {
|
|
179
|
-
throw new CaveatSpecificationMismatchError(specification, permissionType);
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Constructor helper for registering the controller's messenger actions.
|
|
187
|
-
*/
|
|
188
|
-
registerMessageHandlers() {
|
|
189
|
-
this.messenger.registerActionHandler(`${controllerName}:clearPermissions`, () => this.clearState());
|
|
190
|
-
this.messenger.registerActionHandler(`${controllerName}:getEndowments`, (origin, targetName, requestData) => this.getEndowments(origin, targetName, requestData));
|
|
191
|
-
this.messenger.registerActionHandler(`${controllerName}:getSubjectNames`, () => this.getSubjectNames());
|
|
192
|
-
this.messenger.registerActionHandler(`${controllerName}:getPermissions`, (origin) => this.getPermissions(origin));
|
|
193
|
-
this.messenger.registerActionHandler(`${controllerName}:hasPermission`, (origin, targetName) => this.hasPermission(origin, targetName));
|
|
194
|
-
this.messenger.registerActionHandler(`${controllerName}:hasPermissions`, (origin) => this.hasPermissions(origin));
|
|
195
|
-
this.messenger.registerActionHandler(`${controllerName}:grantPermissions`, this.grantPermissions.bind(this));
|
|
196
|
-
this.messenger.registerActionHandler(`${controllerName}:grantPermissionsIncremental`, this.grantPermissionsIncremental.bind(this));
|
|
197
|
-
this.messenger.registerActionHandler(`${controllerName}:requestPermissions`, (subject, permissions) => this.requestPermissions(subject, permissions));
|
|
198
|
-
this.messenger.registerActionHandler(`${controllerName}:requestPermissionsIncremental`, (subject, permissions) => this.requestPermissionsIncremental(subject, permissions));
|
|
199
|
-
this.messenger.registerActionHandler(`${controllerName}:revokeAllPermissions`, (origin) => this.revokeAllPermissions(origin));
|
|
200
|
-
this.messenger.registerActionHandler(`${controllerName}:revokePermissionForAllSubjects`, (target) => this.revokePermissionForAllSubjects(target));
|
|
201
|
-
this.messenger.registerActionHandler(`${controllerName}:revokePermissions`, this.revokePermissions.bind(this));
|
|
202
|
-
this.messenger.registerActionHandler(`${controllerName}:updateCaveat`, (origin, target, caveatType, caveatValue) => {
|
|
203
|
-
this.updateCaveat(origin, target, caveatType, caveatValue);
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
138
|
/**
|
|
207
139
|
* Clears the state of the controller.
|
|
208
140
|
*/
|
|
@@ -213,33 +145,6 @@ export class PermissionController extends BaseController {
|
|
|
213
145
|
};
|
|
214
146
|
});
|
|
215
147
|
}
|
|
216
|
-
/**
|
|
217
|
-
* Gets the permission specification corresponding to the given permission
|
|
218
|
-
* type and target name. Throws an error if the target name does not
|
|
219
|
-
* correspond to a permission, or if the specification is not of the
|
|
220
|
-
* given permission type.
|
|
221
|
-
*
|
|
222
|
-
* @template Type - The type of the permission specification to get.
|
|
223
|
-
* @param permissionType - The type of the permission specification to get.
|
|
224
|
-
* @param targetName - The name of the permission whose specification to get.
|
|
225
|
-
* @param requestingOrigin - The origin of the requesting subject, if any.
|
|
226
|
-
* Will be added to any thrown errors.
|
|
227
|
-
* @returns The specification object corresponding to the given type and
|
|
228
|
-
* target name.
|
|
229
|
-
*/
|
|
230
|
-
getTypedPermissionSpecification(permissionType, targetName, requestingOrigin) {
|
|
231
|
-
const failureError = permissionType === PermissionType.RestrictedMethod
|
|
232
|
-
? methodNotFound(targetName, requestingOrigin ? { origin: requestingOrigin } : undefined)
|
|
233
|
-
: new EndowmentPermissionDoesNotExistError(targetName, requestingOrigin);
|
|
234
|
-
if (!this.targetExists(targetName)) {
|
|
235
|
-
throw failureError;
|
|
236
|
-
}
|
|
237
|
-
const specification = this.getPermissionSpecification(targetName);
|
|
238
|
-
if (!hasSpecificationType(specification, permissionType)) {
|
|
239
|
-
throw failureError;
|
|
240
|
-
}
|
|
241
|
-
return specification;
|
|
242
|
-
}
|
|
243
148
|
/**
|
|
244
149
|
* Gets the implementation of the specified restricted method.
|
|
245
150
|
*
|
|
@@ -253,7 +158,7 @@ export class PermissionController extends BaseController {
|
|
|
253
158
|
* @returns The restricted method implementation.
|
|
254
159
|
*/
|
|
255
160
|
getRestrictedMethod(method, origin) {
|
|
256
|
-
return this.
|
|
161
|
+
return __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_getTypedPermissionSpecification).call(this, PermissionType.RestrictedMethod, method, origin).methodImplementation;
|
|
257
162
|
}
|
|
258
163
|
/**
|
|
259
164
|
* Gets a list of all origins of subjects.
|
|
@@ -351,7 +256,7 @@ export class PermissionController extends BaseController {
|
|
|
351
256
|
if (!hasProperty(permissions, target)) {
|
|
352
257
|
throw new PermissionDoesNotExistError(origin, target);
|
|
353
258
|
}
|
|
354
|
-
this.
|
|
259
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_deletePermission).call(this, draftState.subjects, origin, target);
|
|
355
260
|
});
|
|
356
261
|
});
|
|
357
262
|
});
|
|
@@ -370,30 +275,11 @@ export class PermissionController extends BaseController {
|
|
|
370
275
|
Object.entries(draftState.subjects).forEach(([origin, subject]) => {
|
|
371
276
|
const { permissions } = subject;
|
|
372
277
|
if (hasProperty(permissions, target)) {
|
|
373
|
-
this.
|
|
278
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_deletePermission).call(this, draftState.subjects, origin, target);
|
|
374
279
|
}
|
|
375
280
|
});
|
|
376
281
|
});
|
|
377
282
|
}
|
|
378
|
-
/**
|
|
379
|
-
* Deletes the permission identified by the given origin and target. If the
|
|
380
|
-
* permission is the single remaining permission of its subject, the subject
|
|
381
|
-
* is also deleted.
|
|
382
|
-
*
|
|
383
|
-
* @param subjects - The draft permission controller subjects.
|
|
384
|
-
* @param origin - The origin of the subject associated with the permission
|
|
385
|
-
* to delete.
|
|
386
|
-
* @param target - The target name of the permission to delete.
|
|
387
|
-
*/
|
|
388
|
-
deletePermission(subjects, origin, target) {
|
|
389
|
-
const { permissions } = subjects[origin];
|
|
390
|
-
if (Object.keys(permissions).length > 1) {
|
|
391
|
-
delete permissions[target];
|
|
392
|
-
}
|
|
393
|
-
else {
|
|
394
|
-
delete subjects[origin];
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
283
|
/**
|
|
398
284
|
* Checks whether the permission of the subject corresponding to the given
|
|
399
285
|
* origin has a caveat of the specified type.
|
|
@@ -457,7 +343,7 @@ export class PermissionController extends BaseController {
|
|
|
457
343
|
if (this.hasCaveat(origin, target, caveatType)) {
|
|
458
344
|
throw new CaveatAlreadyExistsError(origin, target, caveatType);
|
|
459
345
|
}
|
|
460
|
-
this.
|
|
346
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_setCaveat).call(this, origin, target, caveatType, caveatValue);
|
|
461
347
|
}
|
|
462
348
|
/**
|
|
463
349
|
* Updates the value of the caveat of the specified type belonging to the
|
|
@@ -481,74 +367,7 @@ export class PermissionController extends BaseController {
|
|
|
481
367
|
if (!this.hasCaveat(origin, target, caveatType)) {
|
|
482
368
|
throw new CaveatDoesNotExistError(origin, target, caveatType);
|
|
483
369
|
}
|
|
484
|
-
this.
|
|
485
|
-
}
|
|
486
|
-
/**
|
|
487
|
-
* Sets the specified caveat on the specified permission. Overwrites existing
|
|
488
|
-
* caveats of the same type in-place (preserving array order), and adds the
|
|
489
|
-
* caveat to the end of the array otherwise.
|
|
490
|
-
*
|
|
491
|
-
* Throws an error if the permission does not exist or fails to validate after
|
|
492
|
-
* its caveats have been modified.
|
|
493
|
-
*
|
|
494
|
-
* @see {@link PermissionController.addCaveat}
|
|
495
|
-
* @see {@link PermissionController.updateCaveat}
|
|
496
|
-
* @template TargetName - The permission target name. Should be inferred.
|
|
497
|
-
* @template CaveatType - The valid caveat types for the permission. Should
|
|
498
|
-
* be inferred.
|
|
499
|
-
* @param origin - The origin of the subject.
|
|
500
|
-
* @param target - The target name of the permission.
|
|
501
|
-
* @param caveatType - The type of the caveat to set.
|
|
502
|
-
* @param caveatValue - The value of the caveat to set.
|
|
503
|
-
*/
|
|
504
|
-
setCaveat(origin, target, caveatType, caveatValue) {
|
|
505
|
-
this.update((draftState) => {
|
|
506
|
-
const subject = draftState.subjects[origin];
|
|
507
|
-
// Unreachable because `hasCaveat` is always called before this, and it
|
|
508
|
-
// throws if permissions are missing. TypeScript needs this, however.
|
|
509
|
-
/* istanbul ignore if */
|
|
510
|
-
if (!subject) {
|
|
511
|
-
throw new UnrecognizedSubjectError(origin);
|
|
512
|
-
}
|
|
513
|
-
const permission = subject.permissions[target];
|
|
514
|
-
/* istanbul ignore if: practically impossible, but TypeScript wants it */
|
|
515
|
-
if (!permission) {
|
|
516
|
-
throw new PermissionDoesNotExistError(origin, target);
|
|
517
|
-
}
|
|
518
|
-
const caveat = {
|
|
519
|
-
type: caveatType,
|
|
520
|
-
value: caveatValue,
|
|
521
|
-
};
|
|
522
|
-
this.validateCaveat(caveat, origin, target);
|
|
523
|
-
let addedCaveat = false;
|
|
524
|
-
if (permission.caveats) {
|
|
525
|
-
const caveatIndex = permission.caveats.findIndex((existingCaveat) => existingCaveat.type === caveat.type);
|
|
526
|
-
if (caveatIndex === -1) {
|
|
527
|
-
permission.caveats.push(caveat);
|
|
528
|
-
addedCaveat = true;
|
|
529
|
-
}
|
|
530
|
-
else {
|
|
531
|
-
permission.caveats.splice(caveatIndex, 1, caveat);
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
else {
|
|
535
|
-
// At this point, we don't know if the specific permission is allowed
|
|
536
|
-
// to have caveats, but it should be impossible to call this method
|
|
537
|
-
// for a permission that may not have any caveats. If all else fails,
|
|
538
|
-
// the permission validator is also called.
|
|
539
|
-
// @ts-expect-error See above comment
|
|
540
|
-
permission.caveats = [caveat];
|
|
541
|
-
addedCaveat = true;
|
|
542
|
-
}
|
|
543
|
-
// Mutating a caveat does not warrant permission validation, but mutating
|
|
544
|
-
// the caveat array does.
|
|
545
|
-
if (addedCaveat) {
|
|
546
|
-
this.validateModifiedPermission(permission, origin, {
|
|
547
|
-
invokePermissionValidator: true,
|
|
548
|
-
performCaveatValidation: false, // We just validated the caveat
|
|
549
|
-
});
|
|
550
|
-
}
|
|
551
|
-
});
|
|
370
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_setCaveat).call(this, origin, target, caveatType, caveatValue);
|
|
552
371
|
}
|
|
553
372
|
/**
|
|
554
373
|
* Updates all caveats with the specified type for all subjects and
|
|
@@ -600,13 +419,13 @@ export class PermissionController extends BaseController {
|
|
|
600
419
|
// results in an error.
|
|
601
420
|
targetCaveat.value =
|
|
602
421
|
mutatorResult.value;
|
|
603
|
-
this.
|
|
422
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_validateCaveat).call(this, targetCaveat, subject.origin, permission.parentCapability);
|
|
604
423
|
break;
|
|
605
424
|
case CaveatMutatorOperation.DeleteCaveat:
|
|
606
|
-
this.
|
|
425
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_deleteCaveat).call(this, permission, targetCaveatType, subject.origin);
|
|
607
426
|
break;
|
|
608
427
|
case CaveatMutatorOperation.RevokePermission:
|
|
609
|
-
this.
|
|
428
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_deletePermission).call(this, draftState.subjects, subject.origin, permission.parentCapability);
|
|
610
429
|
break;
|
|
611
430
|
default: {
|
|
612
431
|
// Overriding as `never` is the expected result of exhaustiveness checking,
|
|
@@ -641,70 +460,9 @@ export class PermissionController extends BaseController {
|
|
|
641
460
|
if (!permission.caveats) {
|
|
642
461
|
throw new CaveatDoesNotExistError(origin, target, caveatType);
|
|
643
462
|
}
|
|
644
|
-
this.
|
|
463
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_deleteCaveat).call(this, permission, caveatType, origin);
|
|
645
464
|
});
|
|
646
465
|
}
|
|
647
|
-
/**
|
|
648
|
-
* Deletes the specified caveat from the specified permission. If no caveats
|
|
649
|
-
* remain after deletion, the permission's caveat property is set to `null`.
|
|
650
|
-
* The permission is validated after being modified.
|
|
651
|
-
*
|
|
652
|
-
* Throws an error if the permission does not have a caveat with the specified
|
|
653
|
-
* type.
|
|
654
|
-
*
|
|
655
|
-
* @param permission - The permission whose caveat to delete.
|
|
656
|
-
* @param caveatType - The type of the caveat to delete.
|
|
657
|
-
* @param origin - The origin the permission subject.
|
|
658
|
-
*/
|
|
659
|
-
deleteCaveat(permission, caveatType, origin) {
|
|
660
|
-
/* istanbul ignore if: not possible in our usage */
|
|
661
|
-
if (!permission.caveats) {
|
|
662
|
-
throw new CaveatDoesNotExistError(origin, permission.parentCapability, caveatType);
|
|
663
|
-
}
|
|
664
|
-
const caveatIndex = permission.caveats.findIndex((existingCaveat) => existingCaveat.type === caveatType);
|
|
665
|
-
if (caveatIndex === -1) {
|
|
666
|
-
throw new CaveatDoesNotExistError(origin, permission.parentCapability, caveatType);
|
|
667
|
-
}
|
|
668
|
-
if (permission.caveats.length === 1) {
|
|
669
|
-
permission.caveats = null;
|
|
670
|
-
}
|
|
671
|
-
else {
|
|
672
|
-
permission.caveats.splice(caveatIndex, 1);
|
|
673
|
-
}
|
|
674
|
-
this.validateModifiedPermission(permission, origin, {
|
|
675
|
-
invokePermissionValidator: true,
|
|
676
|
-
performCaveatValidation: false, // No caveat object was mutated
|
|
677
|
-
});
|
|
678
|
-
}
|
|
679
|
-
/**
|
|
680
|
-
* Validates the specified modified permission. Should **always** be invoked
|
|
681
|
-
* on a permission when its caveat array has been mutated.
|
|
682
|
-
*
|
|
683
|
-
* Just like {@link PermissionController.validatePermission}, except that the
|
|
684
|
-
* corresponding target name and specification are retrieved first, and an
|
|
685
|
-
* error is thrown if the target name does not exist.
|
|
686
|
-
*
|
|
687
|
-
* @param permission - The modified permission to validate.
|
|
688
|
-
* @param origin - The origin associated with the permission.
|
|
689
|
-
* @param validationFlags - Validation flags. See {@link PermissionController.validatePermission}.
|
|
690
|
-
*/
|
|
691
|
-
validateModifiedPermission(permission, origin, validationFlags) {
|
|
692
|
-
/* istanbul ignore if: this should be impossible */
|
|
693
|
-
if (!this.targetExists(permission.parentCapability)) {
|
|
694
|
-
throw new Error(`Fatal: Existing permission target "${permission.parentCapability}" has no specification.`);
|
|
695
|
-
}
|
|
696
|
-
this.validatePermission(this.getPermissionSpecification(permission.parentCapability), permission, origin, validationFlags);
|
|
697
|
-
}
|
|
698
|
-
/**
|
|
699
|
-
* Verifies the existence the specified permission target, i.e. whether it has
|
|
700
|
-
* a specification.
|
|
701
|
-
*
|
|
702
|
-
* @param target - The requested permission target.
|
|
703
|
-
* @returns Whether the permission target exists.
|
|
704
|
-
*/
|
|
705
|
-
targetExists(target) {
|
|
706
|
-
return hasProperty(this._permissionSpecifications, target);
|
|
707
|
-
}
|
|
708
466
|
/**
|
|
709
467
|
* Grants _approved_ permissions to the specified subject. Every permission and
|
|
710
468
|
* caveat is stringently validated—including by calling their specification
|
|
@@ -761,139 +519,6 @@ export class PermissionController extends BaseController {
|
|
|
761
519
|
requestData,
|
|
762
520
|
});
|
|
763
521
|
}
|
|
764
|
-
/**
|
|
765
|
-
* Validates the specified permission by:
|
|
766
|
-
* - Ensuring that if `subjectTypes` is specified, the subject requesting the permission is of a type in the list.
|
|
767
|
-
* - Ensuring that its `caveats` property is either `null` or a non-empty array.
|
|
768
|
-
* - Ensuring that it only includes caveats allowed by its specification.
|
|
769
|
-
* - Ensuring that it includes no duplicate caveats (by caveat type).
|
|
770
|
-
* - Validating each caveat object, if `performCaveatValidation` is `true`.
|
|
771
|
-
* - Calling the validator of its specification, if one exists and `invokePermissionValidator` is `true`.
|
|
772
|
-
*
|
|
773
|
-
* An error is thrown if validation fails.
|
|
774
|
-
*
|
|
775
|
-
* @param specification - The specification of the permission.
|
|
776
|
-
* @param permission - The permission to validate.
|
|
777
|
-
* @param origin - The origin associated with the permission.
|
|
778
|
-
* @param validationOptions - Validation options.
|
|
779
|
-
* @param validationOptions.invokePermissionValidator - Whether to invoke the
|
|
780
|
-
* permission's consumer-specified validator function, if any.
|
|
781
|
-
* @param validationOptions.performCaveatValidation - Whether to invoke
|
|
782
|
-
* {@link PermissionController.validateCaveat} on each of the permission's
|
|
783
|
-
* caveats.
|
|
784
|
-
*/
|
|
785
|
-
validatePermission(specification, permission, origin, { invokePermissionValidator, performCaveatValidation, }) {
|
|
786
|
-
const { allowedCaveats, validator, targetName } = specification;
|
|
787
|
-
if (specification.subjectTypes?.length &&
|
|
788
|
-
specification.subjectTypes.length > 0) {
|
|
789
|
-
const metadata = this.messenger.call('SubjectMetadataController:getSubjectMetadata', origin);
|
|
790
|
-
if (!metadata ||
|
|
791
|
-
metadata.subjectType === null ||
|
|
792
|
-
!specification.subjectTypes.includes(metadata.subjectType)) {
|
|
793
|
-
throw specification.permissionType === PermissionType.RestrictedMethod
|
|
794
|
-
? methodNotFound(targetName, { origin })
|
|
795
|
-
: new EndowmentPermissionDoesNotExistError(targetName, origin);
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
if (hasProperty(permission, 'caveats')) {
|
|
799
|
-
const { caveats } = permission;
|
|
800
|
-
if (caveats !== null && !(Array.isArray(caveats) && caveats.length > 0)) {
|
|
801
|
-
throw new InvalidCaveatsPropertyError(origin, targetName, caveats);
|
|
802
|
-
}
|
|
803
|
-
const seenCaveatTypes = new Set();
|
|
804
|
-
caveats?.forEach((caveat) => {
|
|
805
|
-
if (performCaveatValidation) {
|
|
806
|
-
this.validateCaveat(caveat, origin, targetName);
|
|
807
|
-
}
|
|
808
|
-
if (!allowedCaveats?.includes(caveat.type)) {
|
|
809
|
-
throw new ForbiddenCaveatError(caveat.type, origin, targetName);
|
|
810
|
-
}
|
|
811
|
-
if (seenCaveatTypes.has(caveat.type)) {
|
|
812
|
-
throw new DuplicateCaveatError(caveat.type, origin, targetName);
|
|
813
|
-
}
|
|
814
|
-
seenCaveatTypes.add(caveat.type);
|
|
815
|
-
});
|
|
816
|
-
}
|
|
817
|
-
if (invokePermissionValidator && validator) {
|
|
818
|
-
validator(permission, origin, targetName);
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
/**
|
|
822
|
-
* Assigns the specified permissions to the subject with the given origin.
|
|
823
|
-
* Overwrites all existing permissions, and creates a subject entry if it
|
|
824
|
-
* doesn't already exist.
|
|
825
|
-
*
|
|
826
|
-
* ATTN: Assumes that the new permissions have been validated.
|
|
827
|
-
*
|
|
828
|
-
* @param origin - The origin of the grantee subject.
|
|
829
|
-
* @param permissions - The new permissions for the grantee subject.
|
|
830
|
-
*/
|
|
831
|
-
setValidatedPermissions(origin, permissions) {
|
|
832
|
-
this.update((draftState) => {
|
|
833
|
-
if (!draftState.subjects[origin]) {
|
|
834
|
-
draftState.subjects[origin] = { origin, permissions: {} };
|
|
835
|
-
}
|
|
836
|
-
draftState.subjects[origin].permissions = castDraft(permissions);
|
|
837
|
-
});
|
|
838
|
-
}
|
|
839
|
-
/**
|
|
840
|
-
* Validates the requested caveats for the permission of the specified
|
|
841
|
-
* subject origin and target name and returns the validated caveat array.
|
|
842
|
-
*
|
|
843
|
-
* Throws an error if validation fails.
|
|
844
|
-
*
|
|
845
|
-
* @param origin - The origin of the permission subject.
|
|
846
|
-
* @param target - The permission target name.
|
|
847
|
-
* @param requestedCaveats - The requested caveats to construct.
|
|
848
|
-
* @returns The constructed caveats.
|
|
849
|
-
*/
|
|
850
|
-
constructCaveats(origin, target, requestedCaveats) {
|
|
851
|
-
const caveatArray = requestedCaveats?.map((requestedCaveat) => {
|
|
852
|
-
this.validateCaveat(requestedCaveat, origin, target);
|
|
853
|
-
// Reassign so that we have a fresh object.
|
|
854
|
-
const { type, value } = requestedCaveat;
|
|
855
|
-
return { type, value };
|
|
856
|
-
});
|
|
857
|
-
return caveatArray && isNonEmptyArray(caveatArray)
|
|
858
|
-
? caveatArray
|
|
859
|
-
: undefined;
|
|
860
|
-
}
|
|
861
|
-
/**
|
|
862
|
-
* This methods validates that the specified caveat is an object with the
|
|
863
|
-
* expected properties and types. It also ensures that a caveat specification
|
|
864
|
-
* exists for the requested caveat type, and calls the specification
|
|
865
|
-
* validator, if it exists, on the caveat object.
|
|
866
|
-
*
|
|
867
|
-
* Throws an error if validation fails.
|
|
868
|
-
*
|
|
869
|
-
* @param caveat - The caveat object to validate.
|
|
870
|
-
* @param origin - The origin associated with the subject of the parent
|
|
871
|
-
* permission.
|
|
872
|
-
* @param target - The target name associated with the parent permission.
|
|
873
|
-
*/
|
|
874
|
-
validateCaveat(caveat, origin, target) {
|
|
875
|
-
if (!isPlainObject(caveat)) {
|
|
876
|
-
throw new InvalidCaveatError(caveat, origin, target);
|
|
877
|
-
}
|
|
878
|
-
if (Object.keys(caveat).length !== 2) {
|
|
879
|
-
throw new InvalidCaveatFieldsError(caveat, origin, target);
|
|
880
|
-
}
|
|
881
|
-
if (typeof caveat.type !== 'string') {
|
|
882
|
-
throw new InvalidCaveatTypeError(caveat, origin, target);
|
|
883
|
-
}
|
|
884
|
-
const specification = this.getCaveatSpecification(caveat.type);
|
|
885
|
-
if (!specification) {
|
|
886
|
-
throw new UnrecognizedCaveatTypeError(caveat.type, origin, target);
|
|
887
|
-
}
|
|
888
|
-
if (!hasProperty(caveat, 'value') || caveat.value === undefined) {
|
|
889
|
-
throw new CaveatMissingValueError(caveat, origin, target);
|
|
890
|
-
}
|
|
891
|
-
if (!isValidJson(caveat.value)) {
|
|
892
|
-
throw new CaveatInvalidJsonError(caveat, origin, target);
|
|
893
|
-
}
|
|
894
|
-
// Typecast: TypeScript still believes that the caveat is a PlainObject.
|
|
895
|
-
specification.validator?.(caveat, origin, target);
|
|
896
|
-
}
|
|
897
522
|
/**
|
|
898
523
|
* Initiates a permission request that requires user approval.
|
|
899
524
|
*
|
|
@@ -922,7 +547,7 @@ export class PermissionController extends BaseController {
|
|
|
922
547
|
async requestPermissions(subject, requestedPermissions, options = {}) {
|
|
923
548
|
const { origin } = subject;
|
|
924
549
|
const { id = nanoid(), preserveExistingPermissions = true } = options;
|
|
925
|
-
this.
|
|
550
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_validateRequestedPermissions).call(this, origin, requestedPermissions);
|
|
926
551
|
const metadata = {
|
|
927
552
|
...options.metadata,
|
|
928
553
|
id,
|
|
@@ -932,7 +557,7 @@ export class PermissionController extends BaseController {
|
|
|
932
557
|
metadata,
|
|
933
558
|
permissions: requestedPermissions,
|
|
934
559
|
};
|
|
935
|
-
const approvedRequest = await this.
|
|
560
|
+
const approvedRequest = await __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_requestUserApproval).call(this, permissionsRequest);
|
|
936
561
|
return await __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_handleApprovedPermissions).call(this, {
|
|
937
562
|
subject,
|
|
938
563
|
metadata,
|
|
@@ -974,7 +599,7 @@ export class PermissionController extends BaseController {
|
|
|
974
599
|
async requestPermissionsIncremental(subject, requestedPermissions, options = {}) {
|
|
975
600
|
const { origin } = subject;
|
|
976
601
|
const { id = nanoid() } = options;
|
|
977
|
-
this.
|
|
602
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_validateRequestedPermissions).call(this, origin, requestedPermissions);
|
|
978
603
|
const currentPermissions = this.getPermissions(origin) ?? {};
|
|
979
604
|
const [newPermissions, permissionDiffMap] = __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_mergeIncrementalPermissions).call(this, currentPermissions, requestedPermissions);
|
|
980
605
|
// The second undefined check is just for type narrowing purposes. These values
|
|
@@ -986,7 +611,7 @@ export class PermissionController extends BaseController {
|
|
|
986
611
|
// It does not spark joy to run this validation again after the merger operation.
|
|
987
612
|
// But, optimizing this procedure is probably not worth it, especially considering
|
|
988
613
|
// that the worst-case scenario for validation degrades to the below function call.
|
|
989
|
-
this.
|
|
614
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_validateRequestedPermissions).call(this, origin, newPermissions);
|
|
990
615
|
}
|
|
991
616
|
catch (error) {
|
|
992
617
|
if (error instanceof Error) {
|
|
@@ -1008,7 +633,7 @@ export class PermissionController extends BaseController {
|
|
|
1008
633
|
permissionDiffMap,
|
|
1009
634
|
},
|
|
1010
635
|
};
|
|
1011
|
-
const approvedRequest = await this.
|
|
636
|
+
const approvedRequest = await __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_requestUserApproval).call(this, permissionsRequest);
|
|
1012
637
|
return await __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_handleApprovedPermissions).call(this, {
|
|
1013
638
|
subject,
|
|
1014
639
|
metadata,
|
|
@@ -1016,172 +641,6 @@ export class PermissionController extends BaseController {
|
|
|
1016
641
|
approvedRequest,
|
|
1017
642
|
});
|
|
1018
643
|
}
|
|
1019
|
-
/**
|
|
1020
|
-
* Validates requested permissions. Throws if validation fails.
|
|
1021
|
-
*
|
|
1022
|
-
* This method ensures that the requested permissions are a properly
|
|
1023
|
-
* formatted {@link RequestedPermissions} object, and performs the same
|
|
1024
|
-
* validation as {@link PermissionController.grantPermissions}, except that
|
|
1025
|
-
* consumer-specified permission validator functions are not called, since
|
|
1026
|
-
* they are only called on fully constructed, approved permissions that are
|
|
1027
|
-
* otherwise completely valid.
|
|
1028
|
-
*
|
|
1029
|
-
* Unrecognzied properties on requested permissions are ignored.
|
|
1030
|
-
*
|
|
1031
|
-
* @param origin - The origin of the grantee subject.
|
|
1032
|
-
* @param requestedPermissions - The requested permissions.
|
|
1033
|
-
*/
|
|
1034
|
-
validateRequestedPermissions(origin, requestedPermissions) {
|
|
1035
|
-
if (!isPlainObject(requestedPermissions)) {
|
|
1036
|
-
throw invalidParams({
|
|
1037
|
-
message: `Requested permissions for origin "${origin}" is not a plain object.`,
|
|
1038
|
-
data: { origin, requestedPermissions },
|
|
1039
|
-
});
|
|
1040
|
-
}
|
|
1041
|
-
if (Object.keys(requestedPermissions).length === 0) {
|
|
1042
|
-
throw invalidParams({
|
|
1043
|
-
message: `Permissions request for origin "${origin}" contains no permissions.`,
|
|
1044
|
-
data: { requestedPermissions },
|
|
1045
|
-
});
|
|
1046
|
-
}
|
|
1047
|
-
for (const targetName of Object.keys(requestedPermissions)) {
|
|
1048
|
-
const permission = requestedPermissions[targetName];
|
|
1049
|
-
if (!this.targetExists(targetName)) {
|
|
1050
|
-
throw methodNotFound(targetName, { origin, requestedPermissions });
|
|
1051
|
-
}
|
|
1052
|
-
if (!isPlainObject(permission) ||
|
|
1053
|
-
(permission.parentCapability !== undefined &&
|
|
1054
|
-
targetName !== permission.parentCapability)) {
|
|
1055
|
-
throw invalidParams({
|
|
1056
|
-
message: `Permissions request for origin "${origin}" contains invalid requested permission(s).`,
|
|
1057
|
-
data: { origin, requestedPermissions },
|
|
1058
|
-
});
|
|
1059
|
-
}
|
|
1060
|
-
// Here we validate the permission without invoking its validator, if any.
|
|
1061
|
-
// The validator will be invoked after the permission has been approved.
|
|
1062
|
-
this.validatePermission(this.getPermissionSpecification(targetName),
|
|
1063
|
-
// Typecast: The permission is still a "PlainObject" here.
|
|
1064
|
-
permission, origin, { invokePermissionValidator: false, performCaveatValidation: true });
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
/**
|
|
1068
|
-
* Adds a request to the {@link ApprovalController} using the
|
|
1069
|
-
* {@link AddApprovalRequest} action. Also validates the resulting approved
|
|
1070
|
-
* permissions request, and throws an error if validation fails.
|
|
1071
|
-
*
|
|
1072
|
-
* @param permissionsRequest - The permissions request object.
|
|
1073
|
-
* @returns The approved permissions request object.
|
|
1074
|
-
*/
|
|
1075
|
-
async requestUserApproval(permissionsRequest) {
|
|
1076
|
-
const { origin, id } = permissionsRequest.metadata;
|
|
1077
|
-
const approvedRequest = await this.messenger.call('ApprovalController:addRequest', {
|
|
1078
|
-
id,
|
|
1079
|
-
origin,
|
|
1080
|
-
requestData: permissionsRequest,
|
|
1081
|
-
type: MethodNames.RequestPermissions,
|
|
1082
|
-
}, true);
|
|
1083
|
-
this.validateApprovedPermissions(approvedRequest, { id, origin });
|
|
1084
|
-
return approvedRequest;
|
|
1085
|
-
}
|
|
1086
|
-
/**
|
|
1087
|
-
* Reunites all the side-effects (onPermitted and onFailure) of the requested permissions inside a record of arrays.
|
|
1088
|
-
*
|
|
1089
|
-
* @param permissions - The approved permissions.
|
|
1090
|
-
* @returns The {@link SideEffects} object containing the handlers arrays.
|
|
1091
|
-
*/
|
|
1092
|
-
getSideEffects(permissions) {
|
|
1093
|
-
return Object.keys(permissions).reduce((sideEffectList, targetName) => {
|
|
1094
|
-
if (this.targetExists(targetName)) {
|
|
1095
|
-
const specification = this.getPermissionSpecification(targetName);
|
|
1096
|
-
if (specification.sideEffect) {
|
|
1097
|
-
sideEffectList.permittedHandlers[targetName] =
|
|
1098
|
-
specification.sideEffect.onPermitted;
|
|
1099
|
-
if (specification.sideEffect.onFailure) {
|
|
1100
|
-
sideEffectList.failureHandlers[targetName] =
|
|
1101
|
-
specification.sideEffect.onFailure;
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
return sideEffectList;
|
|
1106
|
-
}, { permittedHandlers: {}, failureHandlers: {} });
|
|
1107
|
-
}
|
|
1108
|
-
/**
|
|
1109
|
-
* Executes the side-effects of the approved permissions while handling the errors if any.
|
|
1110
|
-
* It will pass an instance of the {@link messenger} and the request data associated with the permission request to the handlers through its params.
|
|
1111
|
-
*
|
|
1112
|
-
* @param sideEffects - the side-effect record created by {@link getSideEffects}
|
|
1113
|
-
* @param requestData - the permissions requestData.
|
|
1114
|
-
* @returns the value returned by all the `onPermitted` handlers in an array.
|
|
1115
|
-
*/
|
|
1116
|
-
async executeSideEffects(sideEffects, requestData) {
|
|
1117
|
-
const { permittedHandlers, failureHandlers } = sideEffects;
|
|
1118
|
-
const params = {
|
|
1119
|
-
requestData,
|
|
1120
|
-
messenger: this.messenger,
|
|
1121
|
-
};
|
|
1122
|
-
const promiseResults = await Promise.allSettled(Object.values(permittedHandlers).map((permittedHandler) => permittedHandler(params)));
|
|
1123
|
-
// lib.es2020.promise.d.ts does not export its types so we're using a simple type.
|
|
1124
|
-
const rejectedHandlers = promiseResults.filter((promise) => promise.status === 'rejected');
|
|
1125
|
-
if (rejectedHandlers.length > 0) {
|
|
1126
|
-
const failureHandlersList = Object.values(failureHandlers);
|
|
1127
|
-
if (failureHandlersList.length > 0) {
|
|
1128
|
-
try {
|
|
1129
|
-
await Promise.all(failureHandlersList.map((failureHandler) => failureHandler(params)));
|
|
1130
|
-
}
|
|
1131
|
-
catch (error) {
|
|
1132
|
-
throw internalError('Unexpected error in side-effects', { error });
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1135
|
-
const reasons = rejectedHandlers.map((handler) => handler.reason);
|
|
1136
|
-
reasons.forEach((reason) => {
|
|
1137
|
-
console.error(reason);
|
|
1138
|
-
});
|
|
1139
|
-
throw reasons.length > 1
|
|
1140
|
-
? internalError('Multiple errors occurred during side-effects execution', { errors: reasons })
|
|
1141
|
-
: reasons[0];
|
|
1142
|
-
}
|
|
1143
|
-
// lib.es2020.promise.d.ts does not export its types so we're using a simple type.
|
|
1144
|
-
return promiseResults.map(({ value }) => value);
|
|
1145
|
-
}
|
|
1146
|
-
/**
|
|
1147
|
-
* Validates an approved {@link PermissionsRequest} object. The approved
|
|
1148
|
-
* request must have the required `metadata` and `permissions` properties,
|
|
1149
|
-
* the `id` and `origin` of the `metadata` must match the original request
|
|
1150
|
-
* metadata, and the requested permissions must be valid per
|
|
1151
|
-
* {@link PermissionController.validateRequestedPermissions}. Any extra
|
|
1152
|
-
* metadata properties are ignored.
|
|
1153
|
-
*
|
|
1154
|
-
* An error is thrown if validation fails.
|
|
1155
|
-
*
|
|
1156
|
-
* @param approvedRequest - The approved permissions request object.
|
|
1157
|
-
* @param originalMetadata - The original request metadata.
|
|
1158
|
-
*/
|
|
1159
|
-
validateApprovedPermissions(approvedRequest, originalMetadata) {
|
|
1160
|
-
const { id, origin } = originalMetadata;
|
|
1161
|
-
if (!isPlainObject(approvedRequest) ||
|
|
1162
|
-
!isPlainObject(approvedRequest.metadata)) {
|
|
1163
|
-
throw internalError(`Approved permissions request for subject "${origin}" is invalid.`, { data: { approvedRequest } });
|
|
1164
|
-
}
|
|
1165
|
-
const { metadata: { id: newId, origin: newOrigin }, permissions, } = approvedRequest;
|
|
1166
|
-
if (newId !== id) {
|
|
1167
|
-
throw internalError(`Approved permissions request for subject "${origin}" mutated its id.`, { originalId: id, mutatedId: newId });
|
|
1168
|
-
}
|
|
1169
|
-
if (newOrigin !== origin) {
|
|
1170
|
-
throw internalError(`Approved permissions request for subject "${origin}" mutated its origin.`, { originalOrigin: origin, mutatedOrigin: newOrigin });
|
|
1171
|
-
}
|
|
1172
|
-
try {
|
|
1173
|
-
this.validateRequestedPermissions(origin, permissions);
|
|
1174
|
-
}
|
|
1175
|
-
catch (error) {
|
|
1176
|
-
if (error instanceof Error) {
|
|
1177
|
-
// Re-throw as an internal error; we should never receive invalid approved
|
|
1178
|
-
// permissions.
|
|
1179
|
-
throw internalError(`Invalid approved permissions request: ${error.message}`, error instanceof JsonRpcError ? error.data : undefined);
|
|
1180
|
-
}
|
|
1181
|
-
/* istanbul ignore next: This should be impossible */
|
|
1182
|
-
throw internalError('Unrecognized error type', { error });
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
644
|
/**
|
|
1186
645
|
* Accepts a permissions request created by
|
|
1187
646
|
* {@link PermissionController.requestPermissions}.
|
|
@@ -1190,11 +649,11 @@ export class PermissionController extends BaseController {
|
|
|
1190
649
|
*/
|
|
1191
650
|
async acceptPermissionsRequest(request) {
|
|
1192
651
|
const { id } = request.metadata;
|
|
1193
|
-
if (!this.
|
|
652
|
+
if (!__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_hasApprovalRequest).call(this, { id })) {
|
|
1194
653
|
throw new PermissionsRequestNotFoundError(id);
|
|
1195
654
|
}
|
|
1196
655
|
if (Object.keys(request.permissions).length === 0) {
|
|
1197
|
-
this.
|
|
656
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_rejectPermissionsRequest).call(this, id, invalidParams({
|
|
1198
657
|
message: 'Must request at least one permission.',
|
|
1199
658
|
}));
|
|
1200
659
|
return;
|
|
@@ -1205,7 +664,7 @@ export class PermissionController extends BaseController {
|
|
|
1205
664
|
catch (error) {
|
|
1206
665
|
// If accepting unexpectedly fails, reject the request and re-throw the
|
|
1207
666
|
// error
|
|
1208
|
-
this.
|
|
667
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_rejectPermissionsRequest).call(this, id, error);
|
|
1209
668
|
throw error;
|
|
1210
669
|
}
|
|
1211
670
|
}
|
|
@@ -1216,37 +675,10 @@ export class PermissionController extends BaseController {
|
|
|
1216
675
|
* @param id - The id of the request to be rejected.
|
|
1217
676
|
*/
|
|
1218
677
|
async rejectPermissionsRequest(id) {
|
|
1219
|
-
if (!this.
|
|
678
|
+
if (!__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_hasApprovalRequest).call(this, { id })) {
|
|
1220
679
|
throw new PermissionsRequestNotFoundError(id);
|
|
1221
680
|
}
|
|
1222
|
-
this.
|
|
1223
|
-
}
|
|
1224
|
-
/**
|
|
1225
|
-
* Checks whether the {@link ApprovalController} has a particular permissions
|
|
1226
|
-
* request.
|
|
1227
|
-
*
|
|
1228
|
-
* @see {@link PermissionController.acceptPermissionsRequest} and
|
|
1229
|
-
* {@link PermissionController.rejectPermissionsRequest} for usage.
|
|
1230
|
-
* @param options - The {@link HasApprovalRequest} options.
|
|
1231
|
-
* @param options.id - The id of the approval request to check for.
|
|
1232
|
-
* @returns Whether the specified request exists.
|
|
1233
|
-
*/
|
|
1234
|
-
hasApprovalRequest(options) {
|
|
1235
|
-
return this.messenger.call('ApprovalController:hasRequest', options);
|
|
1236
|
-
}
|
|
1237
|
-
/**
|
|
1238
|
-
* Rejects the permissions request with the specified id, with the specified
|
|
1239
|
-
* error as the reason. This method is effectively a wrapper around a
|
|
1240
|
-
* messenger call for the `ApprovalController:rejectRequest` action.
|
|
1241
|
-
*
|
|
1242
|
-
* @see {@link PermissionController.acceptPermissionsRequest} and
|
|
1243
|
-
* {@link PermissionController.rejectPermissionsRequest} for usage.
|
|
1244
|
-
* @param id - The id of the request to reject.
|
|
1245
|
-
* @param error - The error associated with the rejection.
|
|
1246
|
-
* @returns Nothing
|
|
1247
|
-
*/
|
|
1248
|
-
_rejectPermissionsRequest(id, error) {
|
|
1249
|
-
return this.messenger.call('ApprovalController:rejectRequest', id, error);
|
|
681
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_rejectPermissionsRequest).call(this, id, userRejectedRequest());
|
|
1250
682
|
}
|
|
1251
683
|
/**
|
|
1252
684
|
* Gets the subject's endowments per the specified endowment permission.
|
|
@@ -1264,7 +696,7 @@ export class PermissionController extends BaseController {
|
|
|
1264
696
|
if (!this.hasPermission(origin, targetName)) {
|
|
1265
697
|
throw unauthorized({ data: { origin, targetName } });
|
|
1266
698
|
}
|
|
1267
|
-
return this.
|
|
699
|
+
return __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_getTypedPermissionSpecification).call(this, PermissionType.Endowment, targetName, origin).endowmentGetter({ origin, requestData });
|
|
1268
700
|
}
|
|
1269
701
|
/**
|
|
1270
702
|
* Executes a restricted method as the subject with the given origin.
|
|
@@ -1294,45 +726,163 @@ export class PermissionController extends BaseController {
|
|
|
1294
726
|
async executeRestrictedMethod(origin, targetName, params) {
|
|
1295
727
|
// Throws if the method does not exist
|
|
1296
728
|
const methodImplementation = this.getRestrictedMethod(targetName, origin);
|
|
1297
|
-
const result = await this.
|
|
729
|
+
const result = await __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_executeRestrictedMethod).call(this, methodImplementation, { origin }, targetName, params);
|
|
1298
730
|
if (result === undefined) {
|
|
1299
731
|
throw new Error(`Internal request for method "${targetName}" as origin "${origin}" returned no result.`);
|
|
1300
732
|
}
|
|
1301
733
|
return result;
|
|
1302
734
|
}
|
|
1303
|
-
/**
|
|
1304
|
-
* An internal method used in the controller's `json-rpc-engine` middleware
|
|
1305
|
-
* and {@link PermissionController.executeRestrictedMethod}. Calls the
|
|
1306
|
-
* specified restricted method implementation after decorating it with the
|
|
1307
|
-
* caveats of its permission. Throws if the subject does not have the
|
|
1308
|
-
* requisite permission.
|
|
1309
|
-
*
|
|
1310
|
-
* ATTN: Parameter validation is the responsibility of the caller, or
|
|
1311
|
-
* the restricted method implementation in the case of `params`.
|
|
1312
|
-
*
|
|
1313
|
-
* @see {@link PermissionController.executeRestrictedMethod} and
|
|
1314
|
-
* {@link PermissionController.createPermissionMiddleware} for usage.
|
|
1315
|
-
* @param methodImplementation - The implementation of the method to call.
|
|
1316
|
-
* @param subject - Metadata about the subject that made the request.
|
|
1317
|
-
* @param method - The method name
|
|
1318
|
-
* @param params - Params needed for executing the restricted method
|
|
1319
|
-
* @returns The result of the restricted method implementation
|
|
1320
|
-
*/
|
|
1321
|
-
_executeRestrictedMethod(methodImplementation, subject, method, params = []) {
|
|
1322
|
-
const { origin } = subject;
|
|
1323
|
-
const permission = this.getPermission(origin, method);
|
|
1324
|
-
if (!permission) {
|
|
1325
|
-
throw unauthorized({ data: { origin, method } });
|
|
1326
|
-
}
|
|
1327
|
-
return decorateWithCaveats(methodImplementation, permission, this._caveatSpecifications)({ method, params, context: { origin } });
|
|
1328
|
-
}
|
|
1329
735
|
}
|
|
1330
|
-
_PermissionController_instances = new WeakSet(),
|
|
1331
|
-
|
|
736
|
+
_PermissionController_caveatSpecifications = new WeakMap(), _PermissionController_permissionSpecifications = new WeakMap(), _PermissionController_unrestrictedMethods = new WeakMap(), _PermissionController_instances = new WeakSet(), _PermissionController_getPermissionSpecification = function _PermissionController_getPermissionSpecification(targetName) {
|
|
737
|
+
return __classPrivateFieldGet(this, _PermissionController_permissionSpecifications, "f")[targetName];
|
|
738
|
+
}, _PermissionController_getCaveatSpecification = function _PermissionController_getCaveatSpecification(caveatType) {
|
|
739
|
+
return __classPrivateFieldGet(this, _PermissionController_caveatSpecifications, "f")[caveatType];
|
|
740
|
+
}, _PermissionController_expectGetCaveatMerger = function _PermissionController_expectGetCaveatMerger(caveatType) {
|
|
741
|
+
const { merger } = __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_getCaveatSpecification).call(this, caveatType);
|
|
1332
742
|
if (merger === undefined) {
|
|
1333
743
|
throw new CaveatMergerDoesNotExistError(caveatType);
|
|
1334
744
|
}
|
|
1335
745
|
return merger;
|
|
746
|
+
}, _PermissionController_validatePermissionSpecifications = function _PermissionController_validatePermissionSpecifications(permissionSpecifications, caveatSpecifications) {
|
|
747
|
+
Object.entries(permissionSpecifications).forEach(([targetName, { permissionType, targetName: innerTargetName, allowedCaveats },]) => {
|
|
748
|
+
if (!permissionType || !hasProperty(PermissionType, permissionType)) {
|
|
749
|
+
throw new Error(`Invalid permission type: "${permissionType}"`);
|
|
750
|
+
}
|
|
751
|
+
if (!targetName) {
|
|
752
|
+
throw new Error(`Invalid permission target name: "${targetName}"`);
|
|
753
|
+
}
|
|
754
|
+
if (targetName !== innerTargetName) {
|
|
755
|
+
throw new Error(`Invalid permission specification: target name "${targetName}" must match specification.targetName value "${innerTargetName}".`);
|
|
756
|
+
}
|
|
757
|
+
if (allowedCaveats) {
|
|
758
|
+
allowedCaveats.forEach((caveatType) => {
|
|
759
|
+
if (!hasProperty(caveatSpecifications, caveatType)) {
|
|
760
|
+
throw new UnrecognizedCaveatTypeError(caveatType);
|
|
761
|
+
}
|
|
762
|
+
const specification = caveatSpecifications[caveatType];
|
|
763
|
+
const isRestrictedMethodCaveat = isRestrictedMethodCaveatSpecification(specification);
|
|
764
|
+
if ((permissionType === PermissionType.RestrictedMethod &&
|
|
765
|
+
!isRestrictedMethodCaveat) ||
|
|
766
|
+
(permissionType === PermissionType.Endowment &&
|
|
767
|
+
isRestrictedMethodCaveat)) {
|
|
768
|
+
throw new CaveatSpecificationMismatchError(specification, permissionType);
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
});
|
|
773
|
+
}, _PermissionController_registerMessageHandlers = function _PermissionController_registerMessageHandlers() {
|
|
774
|
+
this.messenger.registerActionHandler(`${controllerName}:clearPermissions`, () => this.clearState());
|
|
775
|
+
this.messenger.registerActionHandler(`${controllerName}:getEndowments`, (origin, targetName, requestData) => this.getEndowments(origin, targetName, requestData));
|
|
776
|
+
this.messenger.registerActionHandler(`${controllerName}:getSubjectNames`, () => this.getSubjectNames());
|
|
777
|
+
this.messenger.registerActionHandler(`${controllerName}:getPermissions`, (origin) => this.getPermissions(origin));
|
|
778
|
+
this.messenger.registerActionHandler(`${controllerName}:hasPermission`, (origin, targetName) => this.hasPermission(origin, targetName));
|
|
779
|
+
this.messenger.registerActionHandler(`${controllerName}:hasPermissions`, (origin) => this.hasPermissions(origin));
|
|
780
|
+
this.messenger.registerActionHandler(`${controllerName}:grantPermissions`, this.grantPermissions.bind(this));
|
|
781
|
+
this.messenger.registerActionHandler(`${controllerName}:grantPermissionsIncremental`, this.grantPermissionsIncremental.bind(this));
|
|
782
|
+
this.messenger.registerActionHandler(`${controllerName}:requestPermissions`, (subject, permissions) => this.requestPermissions(subject, permissions));
|
|
783
|
+
this.messenger.registerActionHandler(`${controllerName}:requestPermissionsIncremental`, (subject, permissions) => this.requestPermissionsIncremental(subject, permissions));
|
|
784
|
+
this.messenger.registerActionHandler(`${controllerName}:revokeAllPermissions`, (origin) => this.revokeAllPermissions(origin));
|
|
785
|
+
this.messenger.registerActionHandler(`${controllerName}:revokePermissionForAllSubjects`, (target) => this.revokePermissionForAllSubjects(target));
|
|
786
|
+
this.messenger.registerActionHandler(`${controllerName}:revokePermissions`, this.revokePermissions.bind(this));
|
|
787
|
+
this.messenger.registerActionHandler(`${controllerName}:updateCaveat`, (origin, target, caveatType, caveatValue) => {
|
|
788
|
+
this.updateCaveat(origin, target, caveatType, caveatValue);
|
|
789
|
+
});
|
|
790
|
+
this.messenger.registerActionHandler(`${controllerName}:getCaveat`, (origin, target, caveatType) => this.getCaveat(origin, target, caveatType));
|
|
791
|
+
}, _PermissionController_getTypedPermissionSpecification = function _PermissionController_getTypedPermissionSpecification(permissionType, targetName, requestingOrigin) {
|
|
792
|
+
const failureError = permissionType === PermissionType.RestrictedMethod
|
|
793
|
+
? methodNotFound(targetName, requestingOrigin ? { origin: requestingOrigin } : undefined)
|
|
794
|
+
: new EndowmentPermissionDoesNotExistError(targetName, requestingOrigin);
|
|
795
|
+
if (!__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_targetExists).call(this, targetName)) {
|
|
796
|
+
throw failureError;
|
|
797
|
+
}
|
|
798
|
+
const specification = __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_getPermissionSpecification).call(this, targetName);
|
|
799
|
+
if (!hasSpecificationType(specification, permissionType)) {
|
|
800
|
+
throw failureError;
|
|
801
|
+
}
|
|
802
|
+
return specification;
|
|
803
|
+
}, _PermissionController_deletePermission = function _PermissionController_deletePermission(subjects, origin, target) {
|
|
804
|
+
const { permissions } = subjects[origin];
|
|
805
|
+
if (Object.keys(permissions).length > 1) {
|
|
806
|
+
delete permissions[target];
|
|
807
|
+
}
|
|
808
|
+
else {
|
|
809
|
+
delete subjects[origin];
|
|
810
|
+
}
|
|
811
|
+
}, _PermissionController_setCaveat = function _PermissionController_setCaveat(origin, target, caveatType, caveatValue) {
|
|
812
|
+
this.update((draftState) => {
|
|
813
|
+
const subject = draftState.subjects[origin];
|
|
814
|
+
// Unreachable because `hasCaveat` is always called before this, and it
|
|
815
|
+
// throws if permissions are missing. TypeScript needs this, however.
|
|
816
|
+
/* istanbul ignore if */
|
|
817
|
+
if (!subject) {
|
|
818
|
+
throw new UnrecognizedSubjectError(origin);
|
|
819
|
+
}
|
|
820
|
+
const permission = subject.permissions[target];
|
|
821
|
+
/* istanbul ignore if: practically impossible, but TypeScript wants it */
|
|
822
|
+
if (!permission) {
|
|
823
|
+
throw new PermissionDoesNotExistError(origin, target);
|
|
824
|
+
}
|
|
825
|
+
const caveat = {
|
|
826
|
+
type: caveatType,
|
|
827
|
+
value: caveatValue,
|
|
828
|
+
};
|
|
829
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_validateCaveat).call(this, caveat, origin, target);
|
|
830
|
+
let addedCaveat = false;
|
|
831
|
+
if (permission.caveats) {
|
|
832
|
+
const caveatIndex = permission.caveats.findIndex((existingCaveat) => existingCaveat.type === caveat.type);
|
|
833
|
+
if (caveatIndex === -1) {
|
|
834
|
+
permission.caveats.push(caveat);
|
|
835
|
+
addedCaveat = true;
|
|
836
|
+
}
|
|
837
|
+
else {
|
|
838
|
+
permission.caveats.splice(caveatIndex, 1, caveat);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
else {
|
|
842
|
+
// At this point, we don't know if the specific permission is allowed
|
|
843
|
+
// to have caveats, but it should be impossible to call this method
|
|
844
|
+
// for a permission that may not have any caveats. If all else fails,
|
|
845
|
+
// the permission validator is also called.
|
|
846
|
+
// @ts-expect-error See above comment
|
|
847
|
+
permission.caveats = [caveat];
|
|
848
|
+
addedCaveat = true;
|
|
849
|
+
}
|
|
850
|
+
// Mutating a caveat does not warrant permission validation, but mutating
|
|
851
|
+
// the caveat array does.
|
|
852
|
+
if (addedCaveat) {
|
|
853
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_validateModifiedPermission).call(this, permission, origin, {
|
|
854
|
+
invokePermissionValidator: true,
|
|
855
|
+
performCaveatValidation: false, // We just validated the caveat
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
}, _PermissionController_deleteCaveat = function _PermissionController_deleteCaveat(permission, caveatType, origin) {
|
|
860
|
+
/* istanbul ignore if: not possible in our usage */
|
|
861
|
+
if (!permission.caveats) {
|
|
862
|
+
throw new CaveatDoesNotExistError(origin, permission.parentCapability, caveatType);
|
|
863
|
+
}
|
|
864
|
+
const caveatIndex = permission.caveats.findIndex((existingCaveat) => existingCaveat.type === caveatType);
|
|
865
|
+
if (caveatIndex === -1) {
|
|
866
|
+
throw new CaveatDoesNotExistError(origin, permission.parentCapability, caveatType);
|
|
867
|
+
}
|
|
868
|
+
if (permission.caveats.length === 1) {
|
|
869
|
+
permission.caveats = null;
|
|
870
|
+
}
|
|
871
|
+
else {
|
|
872
|
+
permission.caveats.splice(caveatIndex, 1);
|
|
873
|
+
}
|
|
874
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_validateModifiedPermission).call(this, permission, origin, {
|
|
875
|
+
invokePermissionValidator: true,
|
|
876
|
+
performCaveatValidation: false, // No caveat object was mutated
|
|
877
|
+
});
|
|
878
|
+
}, _PermissionController_validateModifiedPermission = function _PermissionController_validateModifiedPermission(permission, origin, validationFlags) {
|
|
879
|
+
/* istanbul ignore if: this should be impossible */
|
|
880
|
+
if (!__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_targetExists).call(this, permission.parentCapability)) {
|
|
881
|
+
throw new Error(`Fatal: Existing permission target "${permission.parentCapability}" has no specification.`);
|
|
882
|
+
}
|
|
883
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_validatePermission).call(this, __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_getPermissionSpecification).call(this, permission.parentCapability), permission, origin, validationFlags);
|
|
884
|
+
}, _PermissionController_targetExists = function _PermissionController_targetExists(target) {
|
|
885
|
+
return hasProperty(__classPrivateFieldGet(this, _PermissionController_permissionSpecifications, "f"), target);
|
|
1336
886
|
}, _PermissionController_applyGrantedPermissions = function _PermissionController_applyGrantedPermissions({ approvedPermissions, subject, mergePermissions, preserveExistingPermissions, requestData, }) {
|
|
1337
887
|
const { origin } = subject;
|
|
1338
888
|
if (!origin || typeof origin !== 'string') {
|
|
@@ -1344,7 +894,7 @@ _PermissionController_instances = new WeakSet(), _PermissionController_expectGet
|
|
|
1344
894
|
}
|
|
1345
895
|
: {});
|
|
1346
896
|
for (const [requestedTarget, approvedPermission] of Object.entries(approvedPermissions)) {
|
|
1347
|
-
if (!this.
|
|
897
|
+
if (!__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_targetExists).call(this, requestedTarget)) {
|
|
1348
898
|
throw methodNotFound(requestedTarget);
|
|
1349
899
|
}
|
|
1350
900
|
if (approvedPermission.parentCapability !== undefined &&
|
|
@@ -1354,9 +904,9 @@ _PermissionController_instances = new WeakSet(), _PermissionController_expectGet
|
|
|
1354
904
|
// We have verified that the target exists, and reassign it to change its
|
|
1355
905
|
// type.
|
|
1356
906
|
const targetName = requestedTarget;
|
|
1357
|
-
const specification = this.
|
|
907
|
+
const specification = __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_getPermissionSpecification).call(this, targetName);
|
|
1358
908
|
// The requested caveats are validated here.
|
|
1359
|
-
const caveats = this.
|
|
909
|
+
const caveats = __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_constructCaveats).call(this, origin, targetName, approvedPermission.caveats);
|
|
1360
910
|
const permissionOptions = {
|
|
1361
911
|
caveats,
|
|
1362
912
|
invoker: origin,
|
|
@@ -1372,14 +922,120 @@ _PermissionController_instances = new WeakSet(), _PermissionController_expectGet
|
|
|
1372
922
|
if (mergePermissions) {
|
|
1373
923
|
permission = __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_mergePermission).call(this, permissions[targetName], permission)[0];
|
|
1374
924
|
}
|
|
1375
|
-
this.
|
|
925
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_validatePermission).call(this, specification, permission, origin, {
|
|
1376
926
|
invokePermissionValidator: true,
|
|
1377
927
|
performCaveatValidation: true,
|
|
1378
928
|
});
|
|
1379
929
|
permissions[targetName] = permission;
|
|
1380
930
|
}
|
|
1381
|
-
this.
|
|
931
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_setValidatedPermissions).call(this, origin, permissions);
|
|
1382
932
|
return permissions;
|
|
933
|
+
}, _PermissionController_validatePermission = function _PermissionController_validatePermission(specification, permission, origin, { invokePermissionValidator, performCaveatValidation, }) {
|
|
934
|
+
const { allowedCaveats, validator, targetName } = specification;
|
|
935
|
+
if (specification.subjectTypes?.length &&
|
|
936
|
+
specification.subjectTypes.length > 0) {
|
|
937
|
+
const metadata = this.messenger.call('SubjectMetadataController:getSubjectMetadata', origin);
|
|
938
|
+
if (!metadata ||
|
|
939
|
+
metadata.subjectType === null ||
|
|
940
|
+
!specification.subjectTypes.includes(metadata.subjectType)) {
|
|
941
|
+
throw specification.permissionType === PermissionType.RestrictedMethod
|
|
942
|
+
? methodNotFound(targetName, { origin })
|
|
943
|
+
: new EndowmentPermissionDoesNotExistError(targetName, origin);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
if (hasProperty(permission, 'caveats')) {
|
|
947
|
+
const { caveats } = permission;
|
|
948
|
+
if (caveats !== null && !(Array.isArray(caveats) && caveats.length > 0)) {
|
|
949
|
+
throw new InvalidCaveatsPropertyError(origin, targetName, caveats);
|
|
950
|
+
}
|
|
951
|
+
const seenCaveatTypes = new Set();
|
|
952
|
+
caveats?.forEach((caveat) => {
|
|
953
|
+
if (performCaveatValidation) {
|
|
954
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_validateCaveat).call(this, caveat, origin, targetName);
|
|
955
|
+
}
|
|
956
|
+
if (!allowedCaveats?.includes(caveat.type)) {
|
|
957
|
+
throw new ForbiddenCaveatError(caveat.type, origin, targetName);
|
|
958
|
+
}
|
|
959
|
+
if (seenCaveatTypes.has(caveat.type)) {
|
|
960
|
+
throw new DuplicateCaveatError(caveat.type, origin, targetName);
|
|
961
|
+
}
|
|
962
|
+
seenCaveatTypes.add(caveat.type);
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
if (invokePermissionValidator && validator) {
|
|
966
|
+
validator(permission, origin, targetName);
|
|
967
|
+
}
|
|
968
|
+
}, _PermissionController_setValidatedPermissions = function _PermissionController_setValidatedPermissions(origin, permissions) {
|
|
969
|
+
this.update((draftState) => {
|
|
970
|
+
if (!draftState.subjects[origin]) {
|
|
971
|
+
draftState.subjects[origin] = { origin, permissions: {} };
|
|
972
|
+
}
|
|
973
|
+
draftState.subjects[origin].permissions = castDraft(permissions);
|
|
974
|
+
});
|
|
975
|
+
}, _PermissionController_constructCaveats = function _PermissionController_constructCaveats(origin, target, requestedCaveats) {
|
|
976
|
+
const caveatArray = requestedCaveats?.map((requestedCaveat) => {
|
|
977
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_validateCaveat).call(this, requestedCaveat, origin, target);
|
|
978
|
+
// Reassign so that we have a fresh object.
|
|
979
|
+
const { type, value } = requestedCaveat;
|
|
980
|
+
return { type, value };
|
|
981
|
+
});
|
|
982
|
+
return caveatArray && isNonEmptyArray(caveatArray)
|
|
983
|
+
? caveatArray
|
|
984
|
+
: undefined;
|
|
985
|
+
}, _PermissionController_validateCaveat = function _PermissionController_validateCaveat(caveat, origin, target) {
|
|
986
|
+
if (!isPlainObject(caveat)) {
|
|
987
|
+
throw new InvalidCaveatError(caveat, origin, target);
|
|
988
|
+
}
|
|
989
|
+
if (Object.keys(caveat).length !== 2) {
|
|
990
|
+
throw new InvalidCaveatFieldsError(caveat, origin, target);
|
|
991
|
+
}
|
|
992
|
+
if (typeof caveat.type !== 'string') {
|
|
993
|
+
throw new InvalidCaveatTypeError(caveat, origin, target);
|
|
994
|
+
}
|
|
995
|
+
const specification = __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_getCaveatSpecification).call(this, caveat.type);
|
|
996
|
+
if (!specification) {
|
|
997
|
+
throw new UnrecognizedCaveatTypeError(caveat.type, origin, target);
|
|
998
|
+
}
|
|
999
|
+
if (!hasProperty(caveat, 'value') || caveat.value === undefined) {
|
|
1000
|
+
throw new CaveatMissingValueError(caveat, origin, target);
|
|
1001
|
+
}
|
|
1002
|
+
if (!isValidJson(caveat.value)) {
|
|
1003
|
+
throw new CaveatInvalidJsonError(caveat, origin, target);
|
|
1004
|
+
}
|
|
1005
|
+
// Typecast: TypeScript still believes that the caveat is a PlainObject.
|
|
1006
|
+
specification.validator?.(caveat, origin, target);
|
|
1007
|
+
}, _PermissionController_validateRequestedPermissions = function _PermissionController_validateRequestedPermissions(origin, requestedPermissions) {
|
|
1008
|
+
if (!isPlainObject(requestedPermissions)) {
|
|
1009
|
+
throw invalidParams({
|
|
1010
|
+
message: `Requested permissions for origin "${origin}" is not a plain object.`,
|
|
1011
|
+
data: { origin, requestedPermissions },
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
if (Object.keys(requestedPermissions).length === 0) {
|
|
1015
|
+
throw invalidParams({
|
|
1016
|
+
message: `Permissions request for origin "${origin}" contains no permissions.`,
|
|
1017
|
+
data: { requestedPermissions },
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
for (const targetName of Object.keys(requestedPermissions)) {
|
|
1021
|
+
const permission = requestedPermissions[targetName];
|
|
1022
|
+
if (!__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_targetExists).call(this, targetName)) {
|
|
1023
|
+
throw methodNotFound(targetName, { origin, requestedPermissions });
|
|
1024
|
+
}
|
|
1025
|
+
if (!isPlainObject(permission) ||
|
|
1026
|
+
(permission.parentCapability !== undefined &&
|
|
1027
|
+
targetName !== permission.parentCapability)) {
|
|
1028
|
+
throw invalidParams({
|
|
1029
|
+
message: `Permissions request for origin "${origin}" contains invalid requested permission(s).`,
|
|
1030
|
+
data: { origin, requestedPermissions },
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
// Here we validate the permission without invoking its validator, if any.
|
|
1034
|
+
// The validator will be invoked after the permission has been approved.
|
|
1035
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_validatePermission).call(this, __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_getPermissionSpecification).call(this, targetName),
|
|
1036
|
+
// Typecast: The permission is still a "PlainObject" here.
|
|
1037
|
+
permission, origin, { invokePermissionValidator: false, performCaveatValidation: true });
|
|
1038
|
+
}
|
|
1383
1039
|
}, _PermissionController_mergeIncrementalPermissions = function _PermissionController_mergeIncrementalPermissions(existingPermissions, incrementalRequestedPermissions) {
|
|
1384
1040
|
const permissionDiffMap = {};
|
|
1385
1041
|
// Use immer's produce as a convenience for calculating the new permissions
|
|
@@ -1457,6 +1113,25 @@ _PermissionController_instances = new WeakSet(), _PermissionController_expectGet
|
|
|
1457
1113
|
diff,
|
|
1458
1114
|
]
|
|
1459
1115
|
: [];
|
|
1116
|
+
}, _PermissionController_requestUserApproval =
|
|
1117
|
+
/**
|
|
1118
|
+
* Adds a request to the {@link ApprovalController} using the
|
|
1119
|
+
* {@link ApprovalControllerAddRequestAction} action. Also validates the resulting approved
|
|
1120
|
+
* permissions request, and throws an error if validation fails.
|
|
1121
|
+
*
|
|
1122
|
+
* @param permissionsRequest - The permissions request object.
|
|
1123
|
+
* @returns The approved permissions request object.
|
|
1124
|
+
*/
|
|
1125
|
+
async function _PermissionController_requestUserApproval(permissionsRequest) {
|
|
1126
|
+
const { origin, id } = permissionsRequest.metadata;
|
|
1127
|
+
const approvedRequest = await this.messenger.call('ApprovalController:addRequest', {
|
|
1128
|
+
id,
|
|
1129
|
+
origin,
|
|
1130
|
+
requestData: permissionsRequest,
|
|
1131
|
+
type: MethodNames.RequestPermissions,
|
|
1132
|
+
}, true);
|
|
1133
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_validateApprovedPermissions).call(this, approvedRequest, { id, origin });
|
|
1134
|
+
return approvedRequest;
|
|
1460
1135
|
}, _PermissionController_handleApprovedPermissions =
|
|
1461
1136
|
/**
|
|
1462
1137
|
* Accepts a permissions request that has been approved by the user. This
|
|
@@ -1474,9 +1149,9 @@ _PermissionController_instances = new WeakSet(), _PermissionController_expectGet
|
|
|
1474
1149
|
async function _PermissionController_handleApprovedPermissions({ subject, metadata, preserveExistingPermissions, approvedRequest, }) {
|
|
1475
1150
|
const { permissions: approvedPermissions, ...requestData } = approvedRequest;
|
|
1476
1151
|
const approvedMetadata = { ...metadata };
|
|
1477
|
-
const sideEffects = this.
|
|
1152
|
+
const sideEffects = __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_getSideEffects).call(this, approvedPermissions);
|
|
1478
1153
|
if (Object.values(sideEffects.permittedHandlers).length > 0) {
|
|
1479
|
-
const sideEffectsData = await this.
|
|
1154
|
+
const sideEffectsData = await __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_executeSideEffects).call(this, sideEffects, approvedRequest);
|
|
1480
1155
|
approvedMetadata.data = Object.keys(sideEffects.permittedHandlers).reduce((acc, permission, i) => ({ [permission]: sideEffectsData[i], ...acc }), {});
|
|
1481
1156
|
}
|
|
1482
1157
|
return [
|
|
@@ -1488,5 +1163,94 @@ async function _PermissionController_handleApprovedPermissions({ subject, metada
|
|
|
1488
1163
|
}),
|
|
1489
1164
|
approvedMetadata,
|
|
1490
1165
|
];
|
|
1166
|
+
}, _PermissionController_getSideEffects = function _PermissionController_getSideEffects(permissions) {
|
|
1167
|
+
return Object.keys(permissions).reduce((sideEffectList, targetName) => {
|
|
1168
|
+
if (__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_targetExists).call(this, targetName)) {
|
|
1169
|
+
const specification = __classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_getPermissionSpecification).call(this, targetName);
|
|
1170
|
+
if (specification.sideEffect) {
|
|
1171
|
+
sideEffectList.permittedHandlers[targetName] =
|
|
1172
|
+
specification.sideEffect.onPermitted;
|
|
1173
|
+
if (specification.sideEffect.onFailure) {
|
|
1174
|
+
sideEffectList.failureHandlers[targetName] =
|
|
1175
|
+
specification.sideEffect.onFailure;
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
return sideEffectList;
|
|
1180
|
+
}, { permittedHandlers: {}, failureHandlers: {} });
|
|
1181
|
+
}, _PermissionController_executeSideEffects =
|
|
1182
|
+
/**
|
|
1183
|
+
* Executes the side-effects of the approved permissions while handling the errors if any.
|
|
1184
|
+
* It will pass an instance of the {@link messenger} and the request data associated with the permission request to the handlers through its params.
|
|
1185
|
+
*
|
|
1186
|
+
* @param sideEffects - the side-effect record created by {@link #getSideEffects}
|
|
1187
|
+
* @param requestData - the permissions requestData.
|
|
1188
|
+
* @returns the value returned by all the `onPermitted` handlers in an array.
|
|
1189
|
+
*/
|
|
1190
|
+
async function _PermissionController_executeSideEffects(sideEffects, requestData) {
|
|
1191
|
+
const { permittedHandlers, failureHandlers } = sideEffects;
|
|
1192
|
+
const params = {
|
|
1193
|
+
requestData,
|
|
1194
|
+
messenger: this.messenger,
|
|
1195
|
+
};
|
|
1196
|
+
const promiseResults = await Promise.allSettled(Object.values(permittedHandlers).map((permittedHandler) => permittedHandler(params)));
|
|
1197
|
+
// lib.es2020.promise.d.ts does not export its types so we're using a simple type.
|
|
1198
|
+
const rejectedHandlers = promiseResults.filter((promise) => promise.status === 'rejected');
|
|
1199
|
+
if (rejectedHandlers.length > 0) {
|
|
1200
|
+
const failureHandlersList = Object.values(failureHandlers);
|
|
1201
|
+
if (failureHandlersList.length > 0) {
|
|
1202
|
+
try {
|
|
1203
|
+
await Promise.all(failureHandlersList.map((failureHandler) => failureHandler(params)));
|
|
1204
|
+
}
|
|
1205
|
+
catch (error) {
|
|
1206
|
+
throw internalError('Unexpected error in side-effects', { error });
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
const reasons = rejectedHandlers.map((handler) => handler.reason);
|
|
1210
|
+
reasons.forEach((reason) => {
|
|
1211
|
+
console.error(reason);
|
|
1212
|
+
});
|
|
1213
|
+
throw reasons.length > 1
|
|
1214
|
+
? internalError('Multiple errors occurred during side-effects execution', { errors: reasons })
|
|
1215
|
+
: reasons[0];
|
|
1216
|
+
}
|
|
1217
|
+
// lib.es2020.promise.d.ts does not export its types so we're using a simple type.
|
|
1218
|
+
return promiseResults.map(({ value }) => value);
|
|
1219
|
+
}, _PermissionController_validateApprovedPermissions = function _PermissionController_validateApprovedPermissions(approvedRequest, originalMetadata) {
|
|
1220
|
+
const { id, origin } = originalMetadata;
|
|
1221
|
+
if (!isPlainObject(approvedRequest) ||
|
|
1222
|
+
!isPlainObject(approvedRequest.metadata)) {
|
|
1223
|
+
throw internalError(`Approved permissions request for subject "${origin}" is invalid.`, { data: { approvedRequest } });
|
|
1224
|
+
}
|
|
1225
|
+
const { metadata: { id: newId, origin: newOrigin }, permissions, } = approvedRequest;
|
|
1226
|
+
if (newId !== id) {
|
|
1227
|
+
throw internalError(`Approved permissions request for subject "${origin}" mutated its id.`, { originalId: id, mutatedId: newId });
|
|
1228
|
+
}
|
|
1229
|
+
if (newOrigin !== origin) {
|
|
1230
|
+
throw internalError(`Approved permissions request for subject "${origin}" mutated its origin.`, { originalOrigin: origin, mutatedOrigin: newOrigin });
|
|
1231
|
+
}
|
|
1232
|
+
try {
|
|
1233
|
+
__classPrivateFieldGet(this, _PermissionController_instances, "m", _PermissionController_validateRequestedPermissions).call(this, origin, permissions);
|
|
1234
|
+
}
|
|
1235
|
+
catch (error) {
|
|
1236
|
+
if (error instanceof Error) {
|
|
1237
|
+
// Re-throw as an internal error; we should never receive invalid approved
|
|
1238
|
+
// permissions.
|
|
1239
|
+
throw internalError(`Invalid approved permissions request: ${error.message}`, error instanceof JsonRpcError ? error.data : undefined);
|
|
1240
|
+
}
|
|
1241
|
+
/* istanbul ignore next: This should be impossible */
|
|
1242
|
+
throw internalError('Unrecognized error type', { error });
|
|
1243
|
+
}
|
|
1244
|
+
}, _PermissionController_hasApprovalRequest = function _PermissionController_hasApprovalRequest(options) {
|
|
1245
|
+
return this.messenger.call('ApprovalController:hasRequest', options);
|
|
1246
|
+
}, _PermissionController_rejectPermissionsRequest = function _PermissionController_rejectPermissionsRequest(id, error) {
|
|
1247
|
+
return this.messenger.call('ApprovalController:rejectRequest', id, error);
|
|
1248
|
+
}, _PermissionController_executeRestrictedMethod = function _PermissionController_executeRestrictedMethod(methodImplementation, subject, method, params = []) {
|
|
1249
|
+
const { origin } = subject;
|
|
1250
|
+
const permission = this.getPermission(origin, method);
|
|
1251
|
+
if (!permission) {
|
|
1252
|
+
throw unauthorized({ data: { origin, method } });
|
|
1253
|
+
}
|
|
1254
|
+
return decorateWithCaveats(methodImplementation, permission, __classPrivateFieldGet(this, _PermissionController_caveatSpecifications, "f"))({ method, params, context: { origin } });
|
|
1491
1255
|
};
|
|
1492
1256
|
//# sourceMappingURL=PermissionController.mjs.map
|