Skip to content

Instantly share code, notes, and snippets.

@simonw
Created October 23, 2025 16:45
Show Gist options
  • Select an option

  • Save simonw/99f0fdc4e40529f2074c19b2d5fba089 to your computer and use it in GitHub Desktop.

Select an option

Save simonw/99f0fdc4e40529f2074c19b2d5fba089 to your computer and use it in GitHub Desktop.
Terminal output HTML
<pre style="background: rgb(42, 42, 42); color: rgb(255, 255, 255); padding: 15px; border-radius: 4px;"><span style="color: rgb(255, 255, 255);">&gt; Take a look at this issue and see what you think https://github.com/simonw/datasette/issues/2520 </span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Fetch</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(https://github.com/simonw/datasette/issues/2520)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Received </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">239.1KB</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> (200 OK)</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/utils/permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">20</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/utils/permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">30</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/utils/permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">50</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Search</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(pattern: "resolve_permissions_from_catalog</span>\<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(", type: "py", output_mode: "content")</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Found </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">96 </span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">lines </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">(ctrl+o to expand)</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/views/special.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">80</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/default_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">50</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">30</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/utils/actions_sql.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">100</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Looking at this issue, here's my analysis:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Current State</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> The permission SQL system currently has:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:action</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Available </span>✓<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> (e.g., "view-table")</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:actor</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Available, but it's just the actor ID as a string (e.g., "alice"), not the full actor dict</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Looking at the code:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 1. In </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">datasette/utils/permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">, the </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">resolve_permissions_from_catalog()</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> function receives </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">actor: str</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> and makes it</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> available as </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:actor</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> parameter in SQL queries.</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 2. In </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">datasette/views/special.py:318</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">, the actor dict is converted to just the ID:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> actor_id = actor.get(</span><span style="color: rgb(180, 36, 25); background: rgb(0, 0, 0);">"id"</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">) </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">if</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> actor </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">else</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">None</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> rows = </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">await</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> resolve_permissions_from_catalog(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> db,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> actor=</span><span style="color: rgb(46, 174, 187); background: rgb(0, 0, 0);">str</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(actor_id) </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">if</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> actor_id </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">is</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">not</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">None</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">else</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(180, 36, 25); background: rgb(0, 0, 0);">""</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> ...</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> )</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 3. The test examples show plugins using it like:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">WHERE</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> :actor = :</span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">user</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">What's Missing</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> The issue asks for three parameters:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 1. </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:actor</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Should be the full actor dict as JSON (currently it's just the ID string)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 2. </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:actor_id</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Should be specifically the actor's ID (currently this is what </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:actor</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> contains)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 3. </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:action</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Already available </span>✓
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">My Thoughts</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> This is a </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">good enhancement</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> that would:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 1. </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Improve clarity</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">: Separate </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:actor</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> (full dict) from </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:actor_id</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> (just the ID)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 2. </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Enable richer permission logic</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">: Allow SQL to check actor attributes like roles, teams, or other metadata beyond just</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> the ID</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 3. </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Better API consistency</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">: The rest of Datasette treats </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">actor</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> as a dict, so permission SQL should too</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Implementation Considerations</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 1. </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Store actor as JSON</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">: Pass the full actor dict as JSON string to SQLite, which can then use </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">json_extract()</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> to access</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> fields</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 2. </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Backward compatibility</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">: Need to decide if </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:actor</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> should change from string to JSON (breaking) or add new parameters</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 3. </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Documentation</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">: As mentioned in the issue, clear docs are essential showing how to use all three parameters</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Would you like me to implement this enhancement?</span>
<span style="color: rgb(255, 255, 255);">&gt; Yes </span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/utils/permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">35</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/utils/permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">datasette/utils/permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">1</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> addition </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 1 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> # perm_utils.py</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 2 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> from __future__ import annotations</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 3 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 4 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ import json</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 5 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple, Union</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 6 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> import sqlite3</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 7 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/utils/permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">datasette/utils/permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">5</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> additions and </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">3</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> removals </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 37 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 38 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 39 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> def build_rules_union(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 40 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> actor: </span><span style="color: rgb(255, 255, 255); background: rgb(162, 67, 88);">str</span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">, plugins: Sequence[PermissionSQL]</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 40 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> actor: </span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">Optional[dict]</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">, plugins: Sequence[PermissionSQL]</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 41 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> ) -&gt; Tuple[str, Dict[str, Any]]:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 42 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> """</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 43 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> Compose plugin SQL into a UNION ALL with namespaced parameters.</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 44 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 45 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> Returns:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 46 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> union_sql: a SELECT with columns (parent, child, allow, reason, source_plugin)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 47 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> params: dict of bound parameters including :actor and namespaced plugin params</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 47 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> params: dict of bound parameters including :actor </span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">(JSON), :actor_id, </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">and namespaced plugin params</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 48 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> """</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 49 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> parts: List[str] = []</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 50 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- params: Dict[str, Any] = </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">{"actor": actor</span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">}</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 50 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ actor_json = json.dumps(actor) if actor else None</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 51 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ actor_id = actor.get("id") if actor else None</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 52 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ params: Dict[str, Any] = </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">{"actor": actor_json, "actor_id": actor_id</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">}</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 53 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 54 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> for i, p in enumerate(plugins):</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 55 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> rewrite, ns_params = _namespace_params(i, p.params)</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/utils/permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">datasette/utils/permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">2</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> additions and </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">1</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> removal </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 80 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 81 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> async def resolve_permissions_from_catalog(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 82 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> db,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 83 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- actor: str,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 83 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ actor: Optional[dict],</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 84 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> plugins: Sequence[PluginOrFactory],</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 85 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> action: str,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 86 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> candidate_sql: str,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">...</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 98 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> where rows is an iterable of sqlite3.Row</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 99 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> - plugins are either PermissionSQL objects or callables accepting (action: str)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 100 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> and returning PermissionSQL instances selecting (parent, child, allow, reason)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 101 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ - actor is the actor dict (or None), made available as :actor (JSON), :actor_id, and :action</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 102 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 103 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> Decision policy:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 104 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> 1) Specificity first: child (depth=2) &gt; parent (depth=1) &gt; root (depth=0)</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/utils/permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">38</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/utils/permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">15</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/utils/permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">datasette/utils/permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">2</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> additions and </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">1</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> removal </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 195 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 196 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> async def resolve_permissions_with_candidates(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 197 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> db,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 198 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- actor: str,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 198 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ actor: Optional[dict],</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 199 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> plugins: Sequence[PluginOrFactory],</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 200 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> candidates: List[Tuple[str, Optional[str]]],</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 201 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> action: str,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">...</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 207 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> the candidates as a UNION of parameterized SELECTs in a CTE.</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 208 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 209 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> candidates: list of (parent, child) where child can be None for parent-scoped actions.</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 210 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ actor: actor dict (or None), made available as :actor (JSON), :actor_id, and :action</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 211 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> """</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 212 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> # Build a small CTE for candidates.</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 213 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> cand_rows_sql: List[str] = []</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/views/special.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">datasette/views/special.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">1</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> addition and </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">2</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> removals </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 312 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> continue</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 313 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> plugins.append(candidate)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 314 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 315 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- actor_id = actor.get("id") if actor else None</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 315 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> rows = await resolve_permissions_from_catalog(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 316 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> db,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 317 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- actor=str(actor_id) if actor_id is not None else "",</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 317 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ actor=actor,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 318 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> plugins=plugins,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 319 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> action=action,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 320 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> candidate_sql=candidate_sql,</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">30</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">10</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">tests/test_utils_permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">1</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> addition and </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">1</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> removal </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 32 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> """</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 33 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> SELECT NULL AS parent, NULL AS child, 1 AS allow,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 34 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> 'global allow for ' || :user || ' on ' || :action AS reason</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 35 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> WHERE :</span><span style="color: rgb(255, 255, 255); background: rgb(162, 67, 88);">actor</span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> = :user</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 35 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> WHERE :</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">actor_id</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> = :user</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 36 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> """,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 37 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">{"user": user, "action": action</span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">},</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 38 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> )</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">tests/test_utils_permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">1</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> addition and </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">1</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> removal </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 47 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> """</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 48 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> SELECT :parent AS parent, :child AS child, 0 AS allow,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 49 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> 'deny ' || :parent || '/' || :child || ' for ' || :user || ' on ' || :action AS reason</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 50 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> WHERE :</span><span style="color: rgb(255, 255, 255); background: rgb(162, 67, 88);">actor</span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> = :user</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 50 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> WHERE :</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">actor_id</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> = :user</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 51 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> """,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 52 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">{"parent": parent, "child": child, "user": user, "action": action</span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">},</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 53 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> )</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">Error editing file</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">20</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">tests/test_utils_permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">5</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> additions and </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">5</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> removals </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 76 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> """</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 77 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> SELECT :parent AS parent, NULL AS child, 1 AS allow,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 78 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> 'allow full parent for ' || :user || ' on ' || :action AS reason</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 79 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> WHERE :</span><span style="color: rgb(255, 255, 255); background: rgb(162, 67, 88);">actor</span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> = :user</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 79 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> WHERE :</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">actor_id</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> = :user</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 80 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> """,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 81 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">{"parent": parent, "user": user, "action": action</span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">},</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 82 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> )</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">...</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 91 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> """</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 92 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> SELECT :parent AS parent, :child AS child, 1 AS allow,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 93 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> 'allow child for ' || :user || ' on ' || :action AS reason</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 94 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> WHERE :</span><span style="color: rgb(255, 255, 255); background: rgb(162, 67, 88);">actor</span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> = :user</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 94 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> WHERE :</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">actor_id</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> = :user</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 95 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> """,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 96 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">{"parent": parent, "child": child, "user": user, "action": action</span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">},</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 97 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> )</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">...</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 121 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> """</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 122 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> SELECT :parent AS parent, :child AS child, 1 AS allow,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 123 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> 'team grant at child for ' || :user || ' on ' || :action AS reason</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 124 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> WHERE :</span><span style="color: rgb(255, 255, 255); background: rgb(162, 67, 88);">actor</span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> = :user</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 124 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> WHERE :</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">actor_id</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> = :user</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 125 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> """,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 126 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">{"parent": parent, "child": child, "user": user, "action": action</span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">},</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 127 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> )</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">...</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 132 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> """</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 133 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> SELECT :parent AS parent, :child AS child, 0 AS allow,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 134 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> 'exception deny at child for ' || :user || ' on ' || :action AS reason</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 135 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> WHERE :</span><span style="color: rgb(255, 255, 255); background: rgb(162, 67, 88);">actor</span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> = :user</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 135 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> WHERE :</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">actor_id</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> = :user</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 136 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> """,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 137 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">{"parent": parent, "child": child, "user": user, "action": action</span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">},</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 138 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> )</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">...</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 153 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> """</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 154 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> SELECT NULL AS parent, NULL AS child, 1 AS allow,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 155 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> 'global allow for ' || :user || ' on ' || :action AS reason</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 156 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> WHERE :</span><span style="color: rgb(255, 255, 255); background: rgb(162, 67, 88);">actor</span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> = :user</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 156 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> WHERE :</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">actor_id</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> = :user</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 157 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> """,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 158 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">{"user": user, "action": action</span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">},</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 159 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> )</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Search</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(pattern: "resolve_permissions_from_catalog</span>\<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(</span>\<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">s*db,</span>\<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">s*"", path: "tests/test_utils_permissions.py", output_mode:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> "content")</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Found </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">0 </span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">lines </span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Search</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(pattern: "await resolve_permissions_from_catalog", path: "tests/test_utils_permissions.py", output_mode: "content")</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Found </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">43 </span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">lines </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">(ctrl+o to expand)</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">tests/test_utils_permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">2</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> additions and </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">2</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> removals </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 247 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> plugin_org_policy_deny_parent("hr"),</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 248 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> ]</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 249 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> rows = await resolve_permissions_from_catalog(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 250 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> db, "alice", plugins, VIEW_TABLE, TABLE_CANDIDATES_SQL, implicit_deny=True</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 250 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> db, </span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">{</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">id": "</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">alice"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">}</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">, plugins, VIEW_TABLE, TABLE_CANDIDATES_SQL, implicit_deny=True</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 251 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> )</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 252 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> # Alice can see everything except accounting/sales and hr/*</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 253 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> assert "/accounting/sales" in res_denied(rows)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">...</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 290 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> ), # child allow beats parent deny</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 291 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> ]</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 292 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> rows = await resolve_permissions_from_catalog(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 293 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> db, "alice", plugins, VIEW_TABLE, TABLE_CANDIDATES_SQL, implicit_deny=True</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 293 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> db, </span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">{</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">id": "</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">alice"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">}</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">, plugins, VIEW_TABLE, TABLE_CANDIDATES_SQL, implicit_deny=True</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 294 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> )</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 295 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 296 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> # table02 allowed, other analytics tables denied</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">tests/test_utils_permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">2</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> additions and </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">2</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> removals </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 269 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> *plugin_conflicting_same_child_rules("carol", "analytics", "secret"),</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 270 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> ]</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 271 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> rows = await resolve_permissions_from_catalog(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 272 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> db, "carol", plugins, VIEW_TABLE, TABLE_CANDIDATES_SQL, implicit_deny=True</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 272 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> db, </span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">{</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">id": "</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">carol"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">}</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">, plugins, VIEW_TABLE, TABLE_CANDIDATES_SQL, implicit_deny=True</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 273 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> )</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 274 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> allowed_analytics = res_allowed(rows, parent="analytics")</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 275 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> denied_analytics = res_denied(rows, parent="analytics")</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">...</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 328 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> plugin_allow_parent_for_user("carol", "analytics"),</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 329 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> ]</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 330 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> rows = await resolve_permissions_from_catalog(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 331 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> db, "carol", plugins, VIEW_TABLE, PARENT_CANDIDATES_SQL, implicit_deny=True</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 331 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> db, </span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">{</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">id": "</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">carol"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">}</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">, plugins, VIEW_TABLE, PARENT_CANDIDATES_SQL, implicit_deny=True</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 332 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> )</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 333 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> d = </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">{r["resource"]: r["allow"] for r in rows</span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">}</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 334 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> assert d["/analytics"] == 1</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">tests/test_utils_permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">1</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> addition and </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">1</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> removal </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 311 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> ), # parent allow (more specific)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 312 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> ]</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 313 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> rows = await resolve_permissions_from_catalog(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 314 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> db, "bob", plugins, VIEW_TABLE, TABLE_CANDIDATES_SQL, implicit_deny=True</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 314 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> db, </span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">{</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">id": "</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">bob"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">}</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">, plugins, VIEW_TABLE, TABLE_CANDIDATES_SQL, implicit_deny=True</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 315 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> )</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 316 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> for r in rows:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 317 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> if r["parent"] == "accounting":</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">tests/test_utils_permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">2</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> additions and </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">2</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> removals </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 342 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 343 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> # implicit_deny=True -&gt; everything denied with reason 'implicit deny'</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 344 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> rows = await resolve_permissions_from_catalog(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 345 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> db, "erin", plugins, VIEW_TABLE, TABLE_CANDIDATES_SQL, implicit_deny=True</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 345 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> db, </span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">{</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">id": "</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">erin"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">}</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">, plugins, VIEW_TABLE, TABLE_CANDIDATES_SQL, implicit_deny=True</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 346 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> )</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 347 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> assert all(r["allow"] == 0 and r["reason"] == "implicit deny" for r in rows)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 348 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 349 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> # implicit_deny=False -&gt; no winner =&gt; allow is None, reason is None</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 350 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> rows2 = await resolve_permissions_from_catalog(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 351 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> db, "erin", plugins, VIEW_TABLE, TABLE_CANDIDATES_SQL, implicit_deny=False</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 351 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> db, </span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">{</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">id": "</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">erin"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">}</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">, plugins, VIEW_TABLE, TABLE_CANDIDATES_SQL, implicit_deny=False</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 352 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> )</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 353 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> assert all(r["allow"] is None and r["reason"] is None for r in rows2)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 354 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">tests/test_utils_permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">3</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> additions and </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">3</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> removals </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 377 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> plugins = [</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 378 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> plugin_root_deny_for_all(),</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 379 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> plugin_allow_parent_for_user(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 380 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> "dev", "analytics"</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 380 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> </span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">{</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">id": "</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">dev"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">}</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">, "analytics"</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 381 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> ), # analytics rescued if included by candidates</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 382 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> ]</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 383 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 384 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> # Case 1: exclude memory dbs, require schema_version &gt;= 2 -&gt; only analytics appear, and thus are </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> allowed</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 385 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> rows = await resolve_permissions_from_catalog(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 386 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> db,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 387 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> "dev",</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 387 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> </span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">{</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">id": "</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">dev"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">}</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 388 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> plugins,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 389 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> VIEW_TABLE,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 390 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> candidate_sql,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">...</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 398 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> # but root deny wins except where specifically allowed (none except analytics parent allow doesn</span>’<span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">t </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> apply to table depth if candidate includes children; still fine</span>—<span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">policy is explicit).</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 399 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> rows2 = await resolve_permissions_from_catalog(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 400 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> db,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 401 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> "dev",</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 401 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> </span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">{</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">id": "</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">dev"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">}</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 402 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> plugins,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 403 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> VIEW_TABLE,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 404 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> candidate_sql,</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">tests/test_utils_permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">2</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> additions and </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">2</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> removals</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 418 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 419 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> view_rows = await resolve_permissions_from_catalog(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 420 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> db,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 421 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> "dana",</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 421 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> </span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">{</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">id": "</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">dana"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">}</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 422 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> plugins,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 423 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> VIEW_TABLE,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 424 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> TABLE_CANDIDATES_SQL,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">...</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 429 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 430 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> insert_rows = await resolve_permissions_from_catalog(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 431 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> db,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 432 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> "dana",</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 432 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> </span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">{</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">id": "</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">dana"</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">}</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 433 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> plugins,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 434 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> "insert-row",</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 435 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> TABLE_CANDIDATES_SQL,</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(pytest tests/test_utils_permissions.py -xvs) </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">timeout: 1m 0s</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">Error: ============================= test session starts ==============================</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">platform darwin -- Python 3.10.4, pytest-8.4.2, pluggy-1.5.0 -- </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">/Users/simon/.local/share/virtualenvs/datasette-AWNrQs95/bin/python</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">cachedir: .pytest_cache</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">SQLite: 3.51.0</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">rootdir: /Users/simon/Dropbox/Development/datasette</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">configfile: pytest.ini</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">plugins: cov-6.0.0, asyncio-0.25.3, anyio-4.4.0, timeout-2.3.1, xdist-3.6.1</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">asyncio: mode=strict, asyncio_default_fixture_loop_scope=None</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">collecting ... collected 8 items</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>…<span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> +91 lines (</span><span style="color: rgb(102, 102, 102); background: rgb(0, 0, 0);">ctrl+o</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">to see all)</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">15</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">tests/test_utils_permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">1</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> addition and </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">1</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> removal</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 377 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> plugins = [</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 378 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> plugin_root_deny_for_all(),</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 379 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> plugin_allow_parent_for_user(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 380 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> </span><span style="color: rgb(255, 255, 255); background: rgb(162, 67, 88);">{</span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">"</span><span style="color: rgb(255, 255, 255); background: rgb(162, 67, 88);">id": "</span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">dev"</span><span style="color: rgb(255, 255, 255); background: rgb(162, 67, 88);">}</span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">, "analytics"</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 380 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> "dev", "analytics"</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 381 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> ), # analytics rescued if included by candidates</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 382 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> ]</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 383 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Search</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(pattern: "plugin_</span>\<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">w+</span>\<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(</span>\<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">s*</span>\<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">{", path: "tests/test_utils_permissions.py", output_mode: "content")</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Found </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">0 </span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">lines </span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(pytest tests/test_utils_permissions.py -xvs) </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">timeout: 1m 0s</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> ============================= test session starts ============================== </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> platform darwin -- Python 3.10.4, pytest-8.4.2, pluggy-1.5.0 -- /Users/simon/.local/share/virtualenvs/datasette-AWNr</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Qs95/bin/python</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>…<span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> +34 lines (ctrl+o to expand)</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(pytest tests/test_actions_sql.py tests/test_tables_endpoint.py -xvs) </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">timeout: 1m 0s</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> ============================= test session starts ============================== </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> platform darwin -- Python 3.10.4, pytest-8.4.2, pluggy-1.5.0 -- /Users/simon/.local/share/virtualenvs/datasette-AWNr</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Qs95/bin/python</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>…<span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> +30 lines (ctrl+o to expand)</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(pytest tests/test_permissions.py -x -q) </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">timeout: 1m 0s</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">........................................................................ [ 40%]</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> ........................................................................ [ 81%]</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> ................................. [100%]</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 177 passed in 18.80s</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">7</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">tests/test_utils_permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">46</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> additions </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 438 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> assert insert_rows and all(r["allow"] == 0 for r in insert_rows)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 439 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> assert all(r["reason"] == "implicit deny" for r in insert_rows)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 440 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> assert all(r["action"] == "insert-row" for r in insert_rows)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 441 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 442 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 443 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ @pytest.mark.asyncio</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 444 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ async def test_actor_actor_id_action_parameters_available(db):</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 445 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ """Test that :actor (JSON), :actor_id, and :action are all available in SQL"""</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 446 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ await seed_catalog(db)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 447 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 448 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ def plugin_using_all_parameters() -&gt; PluginProvider:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 449 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ def provider(action: str) -&gt; PermissionSQL:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 450 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ return PermissionSQL(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 451 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ "test_all_params",</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 452 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ """</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 453 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ SELECT NULL AS parent, NULL AS child, 1 AS allow,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 454 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ 'Actor ID: ' || COALESCE(:actor_id, 'null') ||</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 455 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ ', Actor JSON: ' || COALESCE(:actor, 'null') ||</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 456 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ ', Action: ' || :action AS reason</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 457 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ WHERE :actor_id = 'test_user' AND :action = 'view-table'</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 458 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ AND json_extract(:actor, '$.role') = 'admin'</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 459 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ """,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 460 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">{</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">},</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 461 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ )</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 462 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 463 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ return provider</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 464 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 465 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ plugins = [plugin_using_all_parameters()]</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 466 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 467 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ # Test with full actor dict</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 468 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ rows = await resolve_permissions_from_catalog(</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 469 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ db,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 470 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">{"id": "test_user", "role": "admin"</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">},</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 471 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ plugins,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 472 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ "view-table",</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 473 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ TABLE_CANDIDATES_SQL,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 474 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ implicit_deny=True,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 475 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ )</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 476 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 477 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ # Should have allowed rows with reason containing all the info</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 478 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ allowed = [r for r in rows if r["allow"] == 1]</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 479 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ assert len(allowed) &gt; 0</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 480 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 481 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ # Check that the reason string contains evidence of all parameters</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 482 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ reason = allowed[0]["reason"]</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 483 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ assert "test_user" in reason</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 484 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ assert "view-table" in reason</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 485 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ # The :actor parameter should be the JSON string</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 486 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ assert "Actor JSON:" in reason</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(pytest tests/test_utils_permissions.py::test_actor_actor_id_action_parameters_available -xvs) </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">timeout: 1m 0s</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">Error: ============================= test session starts ==============================</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">platform darwin -- Python 3.10.4, pytest-8.4.2, pluggy-1.5.0 -- </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">/Users/simon/.local/share/virtualenvs/datasette-AWNrQs95/bin/python</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">cachedir: .pytest_cache</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">SQLite: 3.51.0</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">rootdir: /Users/simon/Dropbox/Development/datasette</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">configfile: pytest.ini</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">plugins: cov-6.0.0, asyncio-0.25.3, anyio-4.4.0, timeout-2.3.1, xdist-3.6.1</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">asyncio: mode=strict, asyncio_default_fixture_loop_scope=None</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">collecting ... collected 1 item</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>…<span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> +74 lines (</span><span style="color: rgb(102, 102, 102); background: rgb(0, 0, 0);">ctrl+o</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">to see all)</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/utils/permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">30</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/utils/permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">datasette/utils/permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">1</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> removal</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 125 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> all_params = </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">{</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 126 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> **(candidate_params or </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">{</span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">}),</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 127 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> **rule_params,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 128 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- "actor": actor,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 128 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> "action": action,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 129 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">}</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 130 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(pytest tests/test_utils_permissions.py::test_actor_actor_id_action_parameters_available -xvs) </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">timeout: 1m 0s</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> ============================= test session starts ============================== </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> platform darwin -- Python 3.10.4, pytest-8.4.2, pluggy-1.5.0 -- /Users/simon/.local/share/virtualenvs/datasette-AWNr</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Qs95/bin/python</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>…<span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> +13 lines (ctrl+o to expand)</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(pytest tests/test_utils_permissions.py tests/test_actions_sql.py tests/test_tables_endpoint.py</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> tests/test_permissions.py -v)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> ============================= test session starts ============================== </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> platform darwin -- Python 3.10.4, pytest-8.4.2, pluggy-1.5.0 -- /Users/simon/.local/share/virtualenvs/datasette-AWNr</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Qs95/bin/python</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>…<span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> +228 lines (ctrl+o to expand)</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(pytest tests/test_black.py -v) </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">timeout: 30s</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">Error: ============================= test session starts ==============================</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">platform darwin -- Python 3.10.4, pytest-8.4.2, pluggy-1.5.0 -- </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">/Users/simon/.local/share/virtualenvs/datasette-AWNrQs95/bin/python</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">cachedir: .pytest_cache</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">SQLite: 3.51.0</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">rootdir: /Users/simon/Dropbox/Development/datasette</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">configfile: pytest.ini</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">plugins: cov-6.0.0, asyncio-0.25.3, anyio-4.4.0, timeout-2.3.1, xdist-3.6.1</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">asyncio: mode=strict, asyncio_default_fixture_loop_scope=None</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(252, 81, 109); background: rgb(0, 0, 0);">collecting ... collected 1 item</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>…<span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> +21 lines (</span><span style="color: rgb(102, 102, 102); background: rgb(0, 0, 0);">ctrl+o</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">to see all)</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(black tests/test_utils_permissions.py)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">reformatted tests/test_utils_permissions.py</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> All done! </span>✨<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>🍰<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>✨
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 1 file reformatted.</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(pytest tests/test_black.py -v) </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">timeout: 30s</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> ============================= test session starts ============================== </span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> platform darwin -- Python 3.10.4, pytest-8.4.2, pluggy-1.5.0 -- /Users/simon/.local/share/virtualenvs/datasette-AWNr</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Qs95/bin/python</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>…<span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> +11 lines (ctrl+o to expand)</span>
⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Perfect! I've successfully implemented issue #2520. Here's a summary of what was done:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Implementation Summary</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">Changes Made</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 1. </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">build_rules_union()</span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> in </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">datasette/utils/permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Changed signature to accept </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">actor: Optional[dict]</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> instead of </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">actor: str</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Now creates three parameters:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:actor</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Full actor dict as JSON string (via </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">json.dumps()</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:actor_id</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Just the actor's ID field</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Both are added to the params dict returned</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 2. </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">resolve_permissions_from_catalog()</span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> and </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">resolve_permissions_with_candidates()</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Changed signatures to accept </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">actor: Optional[dict]</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> instead of </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">actor: str</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Updated docstrings to document the new parameters available in SQL</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 3. </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Updated call site in </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">datasette/views/special.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Now passes the full actor dict to </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">resolve_permissions_from_catalog()</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> instead of extracting the ID</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 4. </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Updated test files</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Changed all SQL queries to use </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:actor_id</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> instead of </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:actor</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> when comparing with user strings</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Updated all </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">resolve_permissions_from_catalog()</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> calls to pass actor dicts like </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">{"id": "alice"</span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">}</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> instead of strings</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 5. </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Added comprehensive test</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> (</span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">test_actor_actor_id_action_parameters_available</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">):</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Demonstrates all three parameters working together</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Shows using </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">json_extract(:actor, '$.role')</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> to access actor properties</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Verifies </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:actor_id</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> and </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:action</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> work correctly</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">What's Now Available in Permission SQL</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Plugin developers can now use these three parameters in their permission SQL:</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:actor</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - The full actor dict as a JSON string (can use </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">json_extract()</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> to access fields)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:actor_id</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - The actor's ID field (for simple comparisons)</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">:action</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - The action being checked (e.g., "view-table")</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">Example Usage</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">SELECT</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">NULL</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">AS</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> parent, </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">NULL</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">AS</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> child, </span><span style="color: rgb(47, 180, 29); background: rgb(0, 0, 0);">1</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">AS</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> allow,</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(180, 36, 25); background: rgb(0, 0, 0);">'Allow for admin users'</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">AS</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> reason</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">WHERE</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> :actor_id = </span><span style="color: rgb(180, 36, 25); background: rgb(0, 0, 0);">'specific_user'</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">AND</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> json_extract(:actor, </span><span style="color: rgb(180, 36, 25); background: rgb(0, 0, 0);">'$.role'</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">) = </span><span style="color: rgb(180, 36, 25); background: rgb(0, 0, 0);">'admin'</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">AND</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> :action = </span><span style="color: rgb(180, 36, 25); background: rgb(0, 0, 0);">'view-table'</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">Test Results</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>✅<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> All 206 permission tests pass</span>✅<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Black formatting test passes</span>✅<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> New test demonstrates all three parameters working</span>
<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> The implementation is complete and ready!</span>
</pre>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment