mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
Respect query permissions on database page, refs #800
This commit is contained in:
parent
14f6b4d200
commit
3f83d4632a
5 changed files with 47 additions and 3 deletions
|
|
@ -60,7 +60,7 @@
|
||||||
<h2 id="queries">Queries</h2>
|
<h2 id="queries">Queries</h2>
|
||||||
<ul>
|
<ul>
|
||||||
{% for query in queries %}
|
{% for query in queries %}
|
||||||
<li><a href="{{ database_url(database) }}/{{ query.name|urlencode }}{% if query.fragment %}#{{ query.fragment }}{% endif %}" title="{{ query.description or query.sql }}">{{ query.title or query.name }}</a></li>
|
<li><a href="{{ database_url(database) }}/{{ query.name|urlencode }}{% if query.fragment %}#{{ query.fragment }}{% endif %}" title="{{ query.description or query.sql }}">{{ query.title or query.name }}</a> {% if query.requires_auth %} - requires authentication{% endif %}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
||||||
|
|
@ -857,6 +857,7 @@ def call_with_supported_arguments(fn, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
def actor_matches_allow(actor, allow):
|
def actor_matches_allow(actor, allow):
|
||||||
|
actor = actor or {}
|
||||||
if allow is None:
|
if allow is None:
|
||||||
return True
|
return True
|
||||||
for key, values in allow.items():
|
for key, values in allow.items():
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import os
|
||||||
import jinja2
|
import jinja2
|
||||||
|
|
||||||
from datasette.utils import (
|
from datasette.utils import (
|
||||||
|
actor_matches_allow,
|
||||||
to_css_class,
|
to_css_class,
|
||||||
validate_sql_select,
|
validate_sql_select,
|
||||||
is_url,
|
is_url,
|
||||||
|
|
@ -53,6 +54,16 @@ class DatabaseView(DataView):
|
||||||
)
|
)
|
||||||
|
|
||||||
tables.sort(key=lambda t: (t["hidden"], t["name"]))
|
tables.sort(key=lambda t: (t["hidden"], t["name"]))
|
||||||
|
canned_queries = [
|
||||||
|
dict(
|
||||||
|
query,
|
||||||
|
requires_auth=not actor_matches_allow(None, query.get("allow", None)),
|
||||||
|
)
|
||||||
|
for query in self.ds.get_canned_queries(database)
|
||||||
|
if actor_matches_allow(
|
||||||
|
request.scope.get("actor", None), query.get("allow", None)
|
||||||
|
)
|
||||||
|
]
|
||||||
return (
|
return (
|
||||||
{
|
{
|
||||||
"database": database,
|
"database": database,
|
||||||
|
|
@ -60,7 +71,7 @@ class DatabaseView(DataView):
|
||||||
"tables": tables,
|
"tables": tables,
|
||||||
"hidden_count": len([t for t in tables if t["hidden"]]),
|
"hidden_count": len([t for t in tables if t["hidden"]]),
|
||||||
"views": views,
|
"views": views,
|
||||||
"queries": self.ds.get_canned_queries(database),
|
"queries": canned_queries,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"show_hidden": request.args.get("_show_hidden"),
|
"show_hidden": request.args.get("_show_hidden"),
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ def canned_write_client():
|
||||||
"sql": "delete from names where rowid = :rowid",
|
"sql": "delete from names where rowid = :rowid",
|
||||||
"write": True,
|
"write": True,
|
||||||
"on_success_message": "Name deleted",
|
"on_success_message": "Name deleted",
|
||||||
|
"allow": {"id": "root"},
|
||||||
},
|
},
|
||||||
"update_name": {
|
"update_name": {
|
||||||
"sql": "update names set name = :name where rowid = :rowid",
|
"sql": "update names set name = :name where rowid = :rowid",
|
||||||
|
|
@ -52,7 +53,11 @@ def test_insert(canned_write_client):
|
||||||
|
|
||||||
def test_custom_success_message(canned_write_client):
|
def test_custom_success_message(canned_write_client):
|
||||||
response = canned_write_client.post(
|
response = canned_write_client.post(
|
||||||
"/data/delete_name", {"rowid": 1}, allow_redirects=False, csrftoken_from=True
|
"/data/delete_name",
|
||||||
|
{"rowid": 1},
|
||||||
|
cookies={"ds_actor": canned_write_client.ds.sign({"id": "root"}, "actor")},
|
||||||
|
allow_redirects=False,
|
||||||
|
csrftoken_from=True,
|
||||||
)
|
)
|
||||||
assert 302 == response.status
|
assert 302 == response.status
|
||||||
messages = canned_write_client.ds.unsign(
|
messages = canned_write_client.ds.unsign(
|
||||||
|
|
@ -93,3 +98,27 @@ def test_insert_error(canned_write_client):
|
||||||
def test_custom_params(canned_write_client):
|
def test_custom_params(canned_write_client):
|
||||||
response = canned_write_client.get("/data/update_name?extra=foo")
|
response = canned_write_client.get("/data/update_name?extra=foo")
|
||||||
assert '<input type="text" id="qp3" name="extra" value="foo">' in response.text
|
assert '<input type="text" id="qp3" name="extra" value="foo">' in response.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_canned_query_permissions_on_database_page(canned_write_client):
|
||||||
|
# Without auth only shows three queries
|
||||||
|
query_names = [
|
||||||
|
q["name"] for q in canned_write_client.get("/data.json").json["queries"]
|
||||||
|
]
|
||||||
|
assert ["add_name", "add_name_specify_id", "update_name"] == query_names
|
||||||
|
|
||||||
|
# With auth shows four
|
||||||
|
response = canned_write_client.get(
|
||||||
|
"/data.json",
|
||||||
|
cookies={"ds_actor": canned_write_client.ds.sign({"id": "root"}, "actor")},
|
||||||
|
)
|
||||||
|
assert 200 == response.status
|
||||||
|
assert [
|
||||||
|
{"name": "add_name", "requires_auth": False},
|
||||||
|
{"name": "add_name_specify_id", "requires_auth": False},
|
||||||
|
{"name": "delete_name", "requires_auth": True},
|
||||||
|
{"name": "update_name", "requires_auth": False},
|
||||||
|
] == [
|
||||||
|
{"name": q["name"], "requires_auth": q["requires_auth"]}
|
||||||
|
for q in response.json["queries"]
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -466,6 +466,9 @@ def test_multi_params(data, should_raise):
|
||||||
[
|
[
|
||||||
({"id": "root"}, None, True),
|
({"id": "root"}, None, True),
|
||||||
({"id": "root"}, {}, False),
|
({"id": "root"}, {}, False),
|
||||||
|
(None, None, True),
|
||||||
|
(None, {}, False),
|
||||||
|
(None, {"id": "root"}, False),
|
||||||
# Special "*" value for any key:
|
# Special "*" value for any key:
|
||||||
({"id": "root"}, {"id": "*"}, True),
|
({"id": "root"}, {"id": "*"}, True),
|
||||||
({}, {"id": "*"}, False),
|
({}, {"id": "*"}, False),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue