Skip to content

Commit

Permalink
Use the Permissions API (closes #121, #32) (#138)
Browse files Browse the repository at this point in the history
This is building on top of w3c/permissions#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 <[email protected]>
  • Loading branch information
johannhof and annevk authored Jan 5, 2023
1 parent 54caceb commit a20c3cc
Showing 1 changed file with 54 additions and 41 deletions.
95 changes: 54 additions & 41 deletions storage-access.bs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -148,8 +150,6 @@ A <dfn>storage access flag set</dfn> is a set of zero or more of the following f

: The <dfn for="storage access flag set" id=has-storage-access-flag>has storage access flag</dfn>
:: 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 <dfn for="storage access flag set" id=was-expressly-denied-storage-access-flag>was expressly denied storage access flag</dfn>
:: 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 <dfn type="abstract-op">obtain a storage access flag set</dfn> for a [=partitioned storage key=] |key| from a [=/storage access map=] |map|, run the following steps:

Expand All @@ -158,10 +158,6 @@ To <dfn type="abstract-op">obtain a storage access flag set</dfn> for a [=partit
1. [=map/Set=] |map|[|key|] to |flags|.
1. Return |map|[|key|].

To <dfn type="abstract-op">save the storage access flag set</dfn> 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|].

<h3 id="the-document-object">Changes to {{Document}}</h3>

<pre class="idl">
Expand All @@ -187,14 +183,8 @@ When invoked on {{Document}} |doc|, the <dfn export method for=Document><code>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=]?
Expand All @@ -220,17 +210,18 @@ When invoked on {{Document}} |doc|, the <dfn export method for=Document><code>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=]?

<h4 id="ua-policy">User Agent storage access policies</h4>
Expand All @@ -242,36 +233,21 @@ To <dfn type="abstract-op">determine if a site has storage access</dfn> 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 <dfn type="abstract-op">determine the storage access policy</dfn> 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=] "<a permission><code>storage-access</code></a>".
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}}.

<h3 id="navigation">Changes to navigation</h3>

Expand All @@ -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

Expand Down Expand Up @@ -313,6 +288,44 @@ To the [=parse a sandboxing directive=] algorithm, add the following under step
<li>The [=sandbox storage access by user activation flag=], unless <var ignore>tokens</var> contains the <dfn export attr-value for=iframe/sandbox>allow-storage-access-by-user-activation</dfn> keyword.
</ul>

<h2 id="permissions-integration">Permissions Integration</h2>

The Storage Access API defines a [=powerful feature=] identified by the [=powerful feature/name=] "<dfn export permission><code>storage-access</code></dfn>". It defines the following permission-related algorithms:

<dl>
<dt>[=powerful feature/permission query algorithm=]</dt>
<dd>
To query the "<a permission><code>storage-access</code></a>" 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.
</dd>
<dt>[=powerful feature/permission key type=]</dt>
<dd>
A [=permission key=] of the "<a permission><code>storage-access</code></a>" feature is a [=tuple=] consisting of a [=site=] <dfn for="permission key">top-level</dfn> and an [=/origin=] <dfn for="permission key">requester</dfn>.

ISSUE(privacycg/storage-access#147): Note that this will likely change to a (site, site) keying.
</dd>
<dt>[=powerful feature/permission key generation algorithm=]</dt>
<dd>
To generate a new [=permission key=] for the "<a permission><code>storage-access</code></a>" 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|).
</dd>
<dt>[=powerful feature/permission key comparison algorithm=]</dt>
<dd>
To compare the [=permission keys=] |key1| and |key2| for the "<a permission><code>storage-access</code></a>" 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.
</dd>
</dl>

<h2 id="permissions-policy-integration">Permissions Policy Integration</h2>

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

0 comments on commit a20c3cc

Please sign in to comment.