feat(permission-controller): Add `requestPermissionsIncremental()` and caveat merger functions
[!NOTE] The diff is not as bad as it seems; just hide whitespace on the diff view. Actual changes are about
+600of source code and+900of tests. Commits are individually reviewable.
Explanation
Adds a new method PermissionController.requestPermissionsIncremental(), which allows callers to incrementally request more authority for a subject without erasing any existing permissions or caveats. requestPermissions() can, based on the preserveExistingPermissions flag, already preserve any existing permissions not named in the request, but will always erase existing caveats. requestPermissionsIncremental(), on the other hand, will merge the requested permissions and their caveats with the existing permissions and their caveats, if any. It will also return the diff of the resulting changes, intended for consumption in the UI.
Permissions are merged in the fashion of a right-biased union, where requested permissions are the right-hand side. This operation is like a union in set theory, except the right-hand operand overwrites values of the left-hand operand in case of collisions. At present, it is impossible to define a generic implementation of caveat merging. Therefore, caveat specifications now include an optional property merger, where consumers can specify the function that will merge caveats together during incremental permission requests. This function must also implement a right-biased union, returning the new caveat object and the diff of the caveat values expressed in the caveat's value type.
See ARCHITECTURE.md for a specification of the right-biased operation.
Notably, requestPermissionsIncremental() will fail if it needs to merge a caveat for which no merger function is specified. If we are to expose incremental permission requests to third parties, every caveat will require a merger. The intention is therefore for all caveats to specify merger functions, and then make the merger property required in the caveat specification.
Finally, this PR commits us to the following principle in caveat design:
The existence of an authority must be represented by the presence of a value. For an elaboration and justification for this principle, again see
ARCHITECTURE.md.
References
Closes: #4163
Changelog
@metamask/permission-controller
- ADDED:
requestIncrementalPermissions()- Adds a method for incrementally requesting permissions, in the fashion of a right-biased union, where the requested permissions are the right-hand side.
- This enables callers to request additional authority down to the individual caveat, without disturbing other permissions or caveats. In addition, the method returns the diff of the resulting changes, which can be used by future user confirmations for this method.
- This method will fail if it attempts to merge caveats for which no caveat merger has been specified.
- ADDED: Consumer-specified caveat merger functions
- Adds a new property,
merger, to caveat specifications, enabling consumers to describe how their caveats should be merged during incremental permission requests.
- Adds a new property,
Checklist
- [x] I've updated the test suite for new or updated code as appropriate
- [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
- [x] I've highlighted breaking changes using the "BREAKING" category above as appropriate