Skip to content

Commit

Permalink
Experimental fix for token restriction bug in #2102
Browse files Browse the repository at this point in the history
Also includes a prototype implementation of --actor option from #2153 which I'm using for testing this.
  • Loading branch information
simonw committed Aug 24, 2023
1 parent bdf59eb commit 6d57a8c
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 1 deletion.
10 changes: 9 additions & 1 deletion datasette/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,10 @@ def uninstall(packages, yes):
"--token",
help="API token to send with --get requests",
)
@click.option(
"--actor",
help="Actor to use for --get requests",
)
@click.option("--version-note", help="Additional note to show on /-/versions")
@click.option("--help-settings", is_flag=True, help="Show available settings")
@click.option("--pdb", is_flag=True, help="Launch debugger on any errors")
Expand Down Expand Up @@ -499,6 +503,7 @@ def serve(
root,
get,
token,
actor,
version_note,
help_settings,
pdb,
Expand Down Expand Up @@ -611,7 +616,10 @@ def serve(
headers = {}
if token:
headers["Authorization"] = "Bearer {}".format(token)
response = client.get(get, headers=headers)
cookies = {}
if actor:
cookies["ds_actor"] = client.actor_cookie(json.loads(actor))

Check warning on line 621 in datasette/cli.py

View check run for this annotation

Codecov / codecov/patch

datasette/cli.py#L621

Added line #L621 was not covered by tests
response = client.get(get, headers=headers, cookies=cookies)
click.echo(response.text)
exit_code = 0 if response.status == 200 else 1
sys.exit(exit_code)
Expand Down
24 changes: 24 additions & 0 deletions datasette/default_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,30 @@ def permission_allowed_actor_restrictions(datasette, actor, action, resource):
return None
_r = actor.get("_r")

# Special case for view-instance: it's allowed if there are any view-database
# or view-table permissions defined
if action == "view-instance":
database_rules = _r.get("d") or {}
for rules in database_rules.values():
if "vd" in rules or "view-database" in rules:
return None

Check warning on line 196 in datasette/default_permissions.py

View check run for this annotation

Codecov / codecov/patch

datasette/default_permissions.py#L195-L196

Added lines #L195 - L196 were not covered by tests
# Now check resources
resource_rules = _r.get("r") or {}
for _database, resources in resource_rules.items():
for rules in resources.values():
if "vt" in rules or "view-table" in rules:
return None

# Special case for view-database: it's allowed if there are any view-table permissions
# defined within that database
if action == "view-database":
database_name = resource
resource_rules = _r.get("r") or {}
resources_in_database = resource_rules.get(database_name) or {}
for rules in resources_in_database.values():
if "vt" in rules or "view-table" in rules:
return None

# Does this action have an abbreviation?
to_check = {action}
permission = datasette.permissions.get(action)
Expand Down
1 change: 1 addition & 0 deletions docs/cli-reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ Once started you can access it at ``http://localhost:8001``
--get TEXT Run an HTTP GET request against this path,
print results and exit
--token TEXT API token to send with --get requests
--actor TEXT Actor to use for --get requests
--version-note TEXT Additional note to show on /-/versions
--help-settings Show available settings
--pdb Launch debugger on any errors
Expand Down
1 change: 1 addition & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ def test_metadata_yaml():
secret=None,
root=False,
token=None,
actor=None,
version_note=None,
get=None,
help_settings=False,
Expand Down
13 changes: 13 additions & 0 deletions tests/test_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1018,3 +1018,16 @@ async def test_api_explorer_visibility(
assert response.status_code == 403
finally:
perms_ds._metadata_local = prev_metadata


@pytest.mark.asyncio
async def test_view_table_token_can_access_table(perms_ds):
actor = {
"id": "restricted-token",
"token": "dstok",
# Restricted to just view-table on perms_ds_two/t1
"_r": {"r": {"perms_ds_two": {"t1": ["vt"]}}},
}
cookies = {"ds_actor": perms_ds.client.actor_cookie(actor)}
response = await perms_ds.client.get("/perms_ds_two/t1.json", cookies=cookies)
assert response.status_code == 200

0 comments on commit 6d57a8c

Please sign in to comment.