From a20c3cc596666d4eccc882f81e078b572f68236a Mon Sep 17 00:00:00 2001 From: Johann Hofmann Date: Thu, 5 Jan 2023 13:39:41 +0100 Subject: [PATCH] Use the Permissions API (closes #121, #32) (#138) This is building on top of https://github.com/w3c/permissions/pull/390 to integrate SAA with permissions. It's deleting a lot of old manual state management but doesn't get rid of the (global) storage access map altogether, since that is done in #141. Co-authored-by: Anne van Kesteren --- storage-access.bs | 95 +++++++++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 41 deletions(-) diff --git a/storage-access.bs b/storage-access.bs index 2751e06..ffa9102 100644 --- a/storage-access.bs +++ b/storage-access.bs @@ -34,6 +34,8 @@ urlPrefix: https://fetch.spec.whatwg.org/; spec: Fetch spec: RFC6265; urlPrefix: https://tools.ietf.org/html/rfc6265 type: dfn text: cookie store; url: section-5.3 +urlPrefix: https://w3c.github.io/permissions/; spec: permissions + text: permissions task source; url: #permissions-task-source; type: dfn urlPrefix: https://w3c.github.io/webdriver/webdriver-spec.html#; spec: webdriver type: dfn text: current browsing context; url: dfn-current-browsing-context @@ -148,8 +150,6 @@ A storage access flag set is a set of zero or more of the following f : The has storage access flag :: When set, this flag indicates |embedded origin| has access to its [=unpartitioned data=] when it's loaded in a [=third party context=] on |top-level site|. -: The was expressly denied storage access flag -:: When set, this flag indicates that the user expressly denied |embedded origin| access to its [=unpartitioned data=] when it's loaded in a [=third party context=] on |top-level site|. To obtain a storage access flag set for a [=partitioned storage key=] |key| from a [=/storage access map=] |map|, run the following steps: @@ -158,10 +158,6 @@ To obtain a storage access flag set for a [=partit 1. [=map/Set=] |map|[|key|] to |flags|. 1. Return |map|[|key|]. -To save the storage access flag set for a [=partitioned storage key=] |key| in a [=/storage access map=] |map|, run the following steps: - -1. [=map/Set=] [=global storage access map=][|key|] to |map|[|key|]. -

Changes to {{Document}}

@@ -187,14 +183,8 @@ When invoked on {{Document}} |doc|, the ha
 1. If |doc|'s [=Document/origin=] is [=same origin=] with the [=top-level origin=] of |doc|'s [=relevant settings object=], [=/resolve=] |p| with true and return |p|.
 1. Let |key| be the result of [=generate a partitioned storage key|generating a partitioned storage key=] from |doc|.
 1. If |key| is failure, [=resolve=] |p| with false and return |p|.
-1. Run these steps [=in parallel=]:
-    1. Let |map| be the result of [=obtain the storage access map|obtaining the storage access map=] for |doc|.
-    1. Let |flag set| be the result of [=obtain a storage access flag set|obtaining the storage access flag set=] with |key| from |map|.
-    1. If |flag set|'s [=was expressly denied storage access flag=] is set, [=queue a global task=] on the [=permission task source=] given |global| to [=/resolve=] |p| with false, and abort these steps.
-    1. If |flag set|'s [=has storage access flag=] is set, [=queue a global task=] on the [=permission task source=] given |global| to [=/resolve=] |p| with true, and abort these steps.
-    1. Let |hasAccess| be [=a new promise=].
-    1. [=Determine the storage access policy=] with |key|, |doc| and |hasAccess|.
-    1. [=Queue a global task=] on the [=permission task source=] given |global| to [=/resolve=] |p| with the result of |hasAccess|.
+1. Let |hasAccess| be the result of running [=determine if a site has storage access=] with |key| and |doc|.
+1. [=Queue a global task=] on the [=permissions task source=] given |global| to [=/resolve=] |p| with |hasAccess|.
 1. Return |p|.
 
 ISSUE: Shouldn't step 8 be [=same site=]?
@@ -220,17 +210,18 @@ When invoked on {{Document}} |doc|, the re
 1. If |key| is failure, [=reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
 1. Let |map| be the result of [=obtain the storage access map|obtaining the storage access map=] for |doc|.
 1. Let |flag set| be the result of [=obtain a storage access flag set|obtaining the storage access flag set=] with |key| from |map|.
-1. If |flag set|'s [=was expressly denied storage access flag=] is set, [=reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
 1. If |flag set|'s [=has storage access flag=] is set, [=/resolve=] and return |p|.
 1. Otherwise, run these steps [=in parallel=]:
     1. Let |hasAccess| be [=a new promise=].
     1. [=Determine the storage access policy=] with |key|, |doc| and |hasAccess|.
-    1. [=Queue a global task=] on the [=permission task source=] given |global| to
+    1. [=Queue a global task=] on the [=permissions task source=] given |global| to
         1. Set |flag set|'s [=has storage access flag=].
-        1. Resolve or reject |p| based on the result of |hasAccess|.
-    1. [=Save the storage access flag set=] for |key| in |map|.
+        1. If |hasAccess| is true, resolve |p|.
+        1. Reject |p| with a "{{NotAllowedError}}" {{DOMException}}.
 1. Return |p|.
 
+ISSUE(privacycg/storage-access#144): We shouldn't use the permissions task source here.
+
 ISSUE: Shouldn't step 9 be [=same site=]?
 
 

User Agent storage access policies

@@ -242,36 +233,21 @@ To determine if a site has storage access with [=p 1. Let |map| be the result of [=obtain the storage access map|obtaining the storage access map=] for |doc|. 1. Let |flag set| be the result of [=obtain a storage access flag set|obtaining the storage access flag set=] with |key| from |map|. 1. If |flag set|'s [=has storage access flag=] is set, return true. -1. Let |has storage access| (a [=boolean=]) be the result of running an [=implementation-defined=] set of steps to determine if |key|'s [=partitioned storage key/embedded origin=] has access to its [=unpartitioned data=] on |key|'s [=partitioned storage key/top-level site=]. -1. If |has storage access| is true, set |flag set|'s [=has storage access flag=]. -1. [=Save the storage access flag set=] for |key| in |map|. -1. Return |has storage access|. +1. Return false. To determine the storage access policy for [=partitioned storage key=] |key| with {{Document}} |doc| and {{Promise}} |p|, run these steps: 1. Let |map| be the result of [=obtain the storage access map|obtaining the storage access map=] for |doc|. 1. Let |flag set| be the result of [=obtain a storage access flag set|obtaining the storage access flag set=] with |key| from |map|. 1. Let |implicitly granted| and |implicitly denied| (each a [=boolean=]) be the result of running an [=implementation-defined=] set of steps to determine if |key|'s [=partitioned storage key/embedded origin=]'s request for storage access on |key|'s [=partitioned storage key/top-level site=] should be granted or denied without prompting the user. - - Note: These [=implementation-defined=] set of steps might result in |flag set|'s [=has storage access flag=] and [=was expressly denied storage access flag=] changing, since the User Agent could have relevant out-of-band information (e.g. a user preference that changed) that this specification is unaware of. 1. Let |global| be |doc|'s [=relevant global object=]. -1. If |implicitly granted| is true, [=queue a global task=] on the [=permission task source=] given |global| to [=/resolve=] |p|, and return. -1. If |implicitly denied| is true, [=queue a global task=] on the [=permission task source=] given |global| to [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}}, and return |p|. -1. Ask the user if they would like to grant |key|'s [=partitioned storage key/embedded origin=] access to its [=unpartitioned data=] when it's loaded in a [=third party context=] on |key|'s [=partitioned storage key/top-level site=], and wait for an answer. Let |expressly granted| and |expressly denied| (both [=booleans=]) be the result. - - Note: While |expressly granted| and |expressly denied| cannot both be true, they could both be false in User Agents which allow users to dismiss the prompt without choosing to allow or deny the request. (Such a dismissal is interpreted in this algorithm as a denial.) -1. If |expressly granted| is true, run these steps: - 1. Unset |flag set|'s [=was expressly denied storage access flag=]. - 1. [=Save the storage access flag set=] for |key| in |map|. - 1. [=Queue a global task=] on the [=permission task source=] given |global| to [=/resolve=] |p|, and return. +1. If |implicitly granted| is true, [=queue a global task=] on the [=permissions task source=] given |global| to [=/resolve=] |p|, and return. +1. If |implicitly denied| is true, [=queue a global task=] on the [=permissions task source=] given |global| to [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}}, and return. +1. Let |permissionState| be the result of [=requesting permission to use=] "storage-access". +1. If |permissionState| is "granted", [=queue a global task=] on the [=permissions task source=] given |global| to [=/resolve=] |p|, and return. 1. Unset |flag set|'s [=has storage access flag=]. -1. If |expressly denied| is true, run these steps: - 1. If |doc|'s {{Window}} object has [=transient activation=], [=consume user activation=] with it. - 1. Set |flag set|'s [=was expressly denied storage access flag=]. -1. [=Save the storage access flag set=] for |key| in |map|. -1. [=Queue a global task=] on the [=permission task source=] given |global| to [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}}. - -ISSUE: [since this is UA-defined, does it make sense to follow-up separately with a user prompt?](https://github.com/privacycg/storage-access/pull/24#discussion_r408784492) +1. If |doc|'s {{Window}} object has [=transient activation=], [=consume user activation=] with it. +1. [=Queue a global task=] on the [=permissions task source=] given |global| to [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}}. @@ -283,7 +259,6 @@ Before changing the current entry of a session history, run the following steps: 1. If |key| is failure, abort these steps. 1. Let |flag set| be the result of [=obtain a storage access flag set|obtaining the storage access flag set=] with |key| from |map|. 1. Unset |flag set|'s [=has storage access flag=]. -1. [=Save the storage access flag set=] for |key| in |map|. ISSUE(privacycg/storage-access#3): What this section should look like ultimately hinges on @@ -313,6 +288,44 @@ To the [=parse a sandboxing directive=] algorithm, add the following under step
  • The [=sandbox storage access by user activation flag=], unless tokens contains the allow-storage-access-by-user-activation keyword. +

    Permissions Integration

    + +The Storage Access API defines a [=powerful feature=] identified by the [=powerful feature/name=] "storage-access". It defines the following permission-related algorithms: + +
    +
    [=powerful feature/permission query algorithm=]
    +
    + To query the "storage-access" permission, given a {{PermissionDescriptor}} |permissionDesc| and a {{PermissionStatus}} |status|: + + 1. Set |status|'s {{PermissionStatus/state}} to |permissionDesc|'s [=permission state=]. + 1. If |status|'s {{PermissionStatus/state}} is [=permission/denied=], set |status|'s {{PermissionStatus/state}} to [=permission/prompt=]. + + Note: The "denied" permission state is not revealed to avoid exposing the user's decision to developers. This is done to prevent retaliation against the user and repeated prompting to the detriment of the user experience. +
    +
    [=powerful feature/permission key type=]
    +
    + A [=permission key=] of the "storage-access" feature is a [=tuple=] consisting of a [=site=] top-level and an [=/origin=] requester. + + ISSUE(privacycg/storage-access#147): Note that this will likely change to a (site, site) keying. +
    +
    [=powerful feature/permission key generation algorithm=]
    +
    + To generate a new [=permission key=] for the "storage-access" feature, given an [=environment settings object=] |settings|, run the following steps: + + 1. Let |topLevelSite| be |settings|' [=top-level site=]. + 1. Let |embeddedOrigin| be |settings|' [=environment settings object/origin=]. + 1. Return (|topLevelSite|, |embeddedOrigin|). +
    +
    [=powerful feature/permission key comparison algorithm=]
    +
    + To compare the [=permission keys=] |key1| and |key2| for the "storage-access" feature, run the following steps: + + 1. If |key1|'s [=permission key/top-level=] is not [=same site=] with |key2|'s [=permission key/top-level=], return false. + 1. If |key1|'s [=permission key/requester=] is not [=same origin=] with |key2|'s [=permission key/requester=], return false. + 1. Return true. +
    +
    +

    Permissions Policy Integration

    The Storage Access API defines a [=policy-controlled feature=] identified by the string `"storage-access"`. Its [=default allowlist=] is `"*"`.