Extra permission rules for /-/create, closes #1937

pull/1961/head
Simon Willison 2022-12-14 12:21:18 -08:00
rodzic e238df3959
commit c094dde3ff
3 zmienionych plików z 64 dodań i 1 usunięć

Wyświetl plik

@ -613,6 +613,13 @@ class TableCreateView(BaseView):
ignore = data.get("ignore")
replace = data.get("replace")
if replace:
# Must have update-row permission
if not await self.ds.permission_allowed(
request.actor, "update-row", resource=database_name
):
return _error(["Permission denied - need update-row"], 403)
table_name = data.get("table")
if not table_name:
return _error(["Table is required"])
@ -630,6 +637,13 @@ class TableCreateView(BaseView):
if rows and row:
return _error(["Cannot specify both rows and row"])
if rows or row:
# Must have insert-row permission
if not await self.ds.permission_allowed(
request.actor, "insert-row", resource=database_name
):
return _error(["Permission denied - need insert-row"], 403)
if columns:
if rows or row:
return _error(["Cannot specify columns with rows or row"])

Wyświetl plik

@ -830,7 +830,8 @@ If the table is successfully created this will return a ``201`` status code and
Creating a table from example data
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Instead of specifying ``columns`` directly you can instead pass a single example ``row`` or a list of ``rows``. Datasette will create a table with a schema that matches those rows and insert them for you:
Instead of specifying ``columns`` directly you can instead pass a single example ``row`` or a list of ``rows``.
Datasette will create a table with a schema that matches those rows and insert them for you:
::
@ -855,6 +856,8 @@ Instead of specifying ``columns`` directly you can instead pass a single example
"pk": "id"
}
Doing this requires both the :ref:`permissions_create_table` and :ref:`permissions_insert_row` permissions.
The ``201`` response here will be similar to the ``columns`` form, but will also include the number of rows that were inserted as ``row_count``:
.. code-block:: json
@ -884,6 +887,8 @@ If you pass a row to the create endpoint with a primary key that already exists
You can avoid this error by passing the same ``"ignore": true`` or ``"replace": true`` options to the create endpoint as you can to the :ref:`insert endpoint <TableInsertView>`.
To use the ``"replace": true`` option you will also need the :ref:`permissions_update_row` permission.
.. _TableDropView:
Dropping tables

Wyświetl plik

@ -1096,6 +1096,50 @@ async def test_create_table(ds_write, input, expected_status, expected_response)
assert data == expected_response
@pytest.mark.asyncio
@pytest.mark.parametrize(
"permissions,body,expected_status,expected_errors",
(
(["create-table"], {"table": "t", "columns": [{"name": "c"}]}, 201, None),
# Need insert-row too if you use "rows":
(
["create-table"],
{"table": "t", "rows": [{"name": "c"}]},
403,
["Permission denied - need insert-row"],
),
# This should work:
(
["create-table", "insert-row"],
{"table": "t", "rows": [{"name": "c"}]},
201,
None,
),
# If you use replace: true you need update-row too:
(
["create-table", "insert-row"],
{"table": "t", "rows": [{"id": 1}], "pk": "id", "replace": True},
403,
["Permission denied - need update-row"],
),
),
)
async def test_create_table_permissions(
ds_write, permissions, body, expected_status, expected_errors
):
token = ds_write.create_token("root", restrict_all=["view-instance"] + permissions)
response = await ds_write.client.post(
"/data/-/create",
json=body,
headers=_headers(token),
)
assert response.status_code == expected_status
if expected_errors:
data = response.json()
assert data["ok"] is False
assert data["errors"] == expected_errors
@pytest.mark.asyncio
@pytest.mark.parametrize(
"input,expected_rows_after",