-
-
Notifications
You must be signed in to change notification settings - Fork 690
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Table/database that is private due to inherited permissions does not show padlock #1829
Comments
Here's the relevant code from the table page: datasette/datasette/views/table.py Lines 215 to 227 in 4218c9c
Note how Here's the same code for the database page: datasette/datasette/views/database.py Lines 139 to 141 in 4218c9c
And for canned query pages: datasette/datasette/views/database.py Lines 228 to 240 in 4218c9c
|
There's also a datasette/datasette/views/database.py Lines 65 to 77 in 4218c9c
And is defined here: Lines 694 to 710 in 4218c9c
It's actually documented as a public method here: https://docs.datasette.io/en/stable/internals.html#await-check-visibility-actor-action-resource-none
Note that this documented method cannot actually do the right thing - because it's not being given the multiple permissions that need to be checked in order to completely answer the question. So I probably need to redesign that method a bit. |
I think |
Here's what I've got so far: diff --git a/datasette/app.py b/datasette/app.py
index 5fa4955c..df9eae49 100644
--- a/datasette/app.py
+++ b/datasette/app.py
@@ -1,5 +1,5 @@
import asyncio
-from typing import Sequence, Union, Tuple
+from typing import Sequence, Union, Tuple, Optional
import asgi_csrf
import collections
import datetime
@@ -707,7 +707,7 @@ class Datasette:
Raises datasette.Forbidden() if any of the checks fail
"""
- assert actor is None or isinstance(actor, dict)
+ assert actor is None or isinstance(actor, dict), "actor must be None or a dict"
for permission in permissions:
if isinstance(permission, str):
action = permission
@@ -732,23 +732,34 @@ class Datasette:
else:
raise Forbidden(action)
- async def check_visibility(self, actor, action, resource):
+ async def check_visibility(
+ self,
+ actor: dict,
+ action: Optional[str] = None,
+ resource: Optional[str] = None,
+ permissions: Optional[
+ Sequence[Union[Tuple[str, Union[str, Tuple[str, str]]], str]]
+ ] = None,
+ ):
"""Returns (visible, private) - visible = can you see it, private = can others see it too"""
- visible = await self.permission_allowed(
- actor,
- action,
- resource=resource,
- default=True,
- )
- if not visible:
+ if permissions:
+ assert (
+ not action and not resource
+ ), "Can't use action= or resource= with permissions="
+ else:
+ permissions = [(action, resource)]
+ try:
+ await self.ensure_permissions(actor, permissions)
+ except Forbidden:
return False, False
- private = not await self.permission_allowed(
- None,
- action,
- resource=resource,
- default=True,
- )
- return visible, private
+ # User can see it, but can the anonymous user see it?
+ try:
+ await self.ensure_permissions(None, permissions)
+ except Forbidden:
+ # It's visible but private
+ return True, True
+ # It's visible to everyone
+ return True, False
async def execute(
self,
diff --git a/datasette/views/table.py b/datasette/views/table.py
index 60c092f9..f73b0957 100644
--- a/datasette/views/table.py
+++ b/datasette/views/table.py
@@ -28,7 +28,7 @@ from datasette.utils import (
urlsafe_components,
value_as_boolean,
)
-from datasette.utils.asgi import BadRequest, NotFound
+from datasette.utils.asgi import BadRequest, Forbidden, NotFound
from datasette.filters import Filters
from .base import DataView, DatasetteError, ureg
from .database import QueryView
@@ -213,18 +213,16 @@ class TableView(DataView):
raise NotFound(f"Table not found: {table_name}")
# Ensure user has permission to view this table
- await self.ds.ensure_permissions(
+ visible, private = await self.ds.check_visibility(
request.actor,
- [
+ permissions=[
("view-table", (database_name, table_name)),
("view-database", database_name),
"view-instance",
],
)
-
- private = not await self.ds.permission_allowed(
- None, "view-table", (database_name, table_name), default=True
- )
+ if not visible:
+ raise Forbidden("You do not have permission to view this table")
# Handle ?_filter_column and redirect, if present
redirect_params = filters_should_redirect(request.args) Still needs tests and a documentation update. Also this fix is currently only applied on the table page - needs to be applied on database, row and query pages too. |
I'll finish this in a PR: |
Useful test: if you sign in as root to https://latest.datasette.io/_internal/columns/_internal,columns,database_name you can see there's no padlock icon on that page or on https://latest.datasette.io/_internal/columns - fixing this bug should fix that. |
Updated docs for |
I noticed that a table page that is private because the database or instance is private, e.g. this one:
Is not displaying the padlock icon that indicates the table is not visible to the public.
Same issue for the database page too, which in this case is private due to
view-instance
.The text was updated successfully, but these errors were encountered: