mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
Added permission check to every view, closes #808
This commit is contained in:
parent
bd4de0647d
commit
86dec9e8ff
13 changed files with 220 additions and 2 deletions
|
|
@ -49,6 +49,7 @@ from .utils import (
|
|||
)
|
||||
from .utils.asgi import (
|
||||
AsgiLifespan,
|
||||
Forbidden,
|
||||
NotFound,
|
||||
Request,
|
||||
Response,
|
||||
|
|
@ -1003,6 +1004,10 @@ class DatasetteRouter(AsgiRouter):
|
|||
status = 404
|
||||
info = {}
|
||||
message = exception.args[0]
|
||||
elif isinstance(exception, Forbidden):
|
||||
status = 403
|
||||
info = {}
|
||||
message = exception.args[0]
|
||||
elif isinstance(exception, DatasetteError):
|
||||
status = exception.status
|
||||
info = exception.error_dict
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@
|
|||
</h2>
|
||||
<p><strong>Actor:</strong> {{ check.actor|tojson }}</p>
|
||||
{% if check.resource_type %}
|
||||
<p><strong>Resource:</strong> {{ check.resource_type }}: {{ check.resource_identifier }}</p>
|
||||
<p><strong>Resource:</strong> {{ check.resource_type }} = {{ check.resource_identifier }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@ class NotFound(Exception):
|
|||
pass
|
||||
|
||||
|
||||
class Forbidden(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Request:
|
||||
def __init__(self, scope, receive):
|
||||
self.scope = scope
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ from datasette.utils.asgi import (
|
|||
AsgiWriter,
|
||||
AsgiRouter,
|
||||
AsgiView,
|
||||
Forbidden,
|
||||
NotFound,
|
||||
Response,
|
||||
)
|
||||
|
|
@ -63,6 +64,19 @@ class BaseView(AsgiView):
|
|||
response.body = b""
|
||||
return response
|
||||
|
||||
async def check_permission(
|
||||
self, request, action, resource_type=None, resource_identifier=None
|
||||
):
|
||||
ok = await self.ds.permission_allowed(
|
||||
request.scope.get("actor"),
|
||||
action,
|
||||
resource_type=resource_type,
|
||||
resource_identifier=resource_identifier,
|
||||
default=True,
|
||||
)
|
||||
if not ok:
|
||||
raise Forbidden(action)
|
||||
|
||||
def database_url(self, database):
|
||||
db = self.ds.databases[database]
|
||||
base_url = self.ds.config("base_url")
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ class DatabaseView(DataView):
|
|||
name = "database"
|
||||
|
||||
async def data(self, request, database, hash, default_labels=False, _size=None):
|
||||
await self.check_permission(request, "view-database", "database", database)
|
||||
metadata = (self.ds.metadata("databases") or {}).get(database, {})
|
||||
self.ds.update_with_inherited_metadata(metadata)
|
||||
|
||||
|
|
@ -89,6 +90,9 @@ class DatabaseDownload(DataView):
|
|||
name = "database_download"
|
||||
|
||||
async def view_get(self, request, database, hash, correct_hash_present, **kwargs):
|
||||
await self.check_permission(
|
||||
request, "view-database-download", "database", database
|
||||
)
|
||||
if database not in self.ds.databases:
|
||||
raise DatasetteError("Invalid database", status=404)
|
||||
db = self.ds.databases[database]
|
||||
|
|
@ -128,6 +132,10 @@ class QueryView(DataView):
|
|||
|
||||
# Respect canned query permissions
|
||||
if canned_query:
|
||||
await self.check_permission(
|
||||
request, "view-query", "query", (database, canned_query)
|
||||
)
|
||||
# TODO: fix this to use that permission check
|
||||
if not actor_matches_allow(
|
||||
request.scope.get("actor", None), metadata.get("allow")
|
||||
):
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ class IndexView(BaseView):
|
|||
self.ds = datasette
|
||||
|
||||
async def get(self, request, as_format):
|
||||
await self.check_permission(request, "view-index")
|
||||
databases = []
|
||||
for name, db in self.ds.databases.items():
|
||||
table_names = await db.table_names()
|
||||
|
|
|
|||
|
|
@ -267,6 +267,8 @@ class TableView(RowTableShared):
|
|||
if not is_view and not table_exists:
|
||||
raise NotFound("Table not found: {}".format(table))
|
||||
|
||||
await self.check_permission(request, "view-table", "table", (database, table))
|
||||
|
||||
pks = await db.primary_keys(table)
|
||||
table_columns = await db.table_columns(table)
|
||||
|
||||
|
|
@ -844,6 +846,9 @@ class RowView(RowTableShared):
|
|||
|
||||
async def data(self, request, database, hash, table, pk_path, default_labels=False):
|
||||
pk_values = urlsafe_components(pk_path)
|
||||
await self.check_permission(
|
||||
request, "view-row", "row", tuple([database, table] + list(pk_values))
|
||||
)
|
||||
db = self.ds.databases[database]
|
||||
pks = await db.primary_keys(table)
|
||||
use_rowid = not pks
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue