allow_sql block to control execute-sql upermission in metadata.json, closes #813

Also removed the --config allow_sql:0 mechanism in favour of the new allow_sql block.
This commit is contained in:
Simon Willison 2020-06-08 17:05:44 -07:00
commit 49d6d2f7b0
16 changed files with 92 additions and 44 deletions

View file

@ -110,7 +110,6 @@ CONFIG_OPTIONS = (
"Allow users to download the original SQLite database files",
),
ConfigOption("suggest_facets", True, "Calculate and display suggested facets"),
ConfigOption("allow_sql", True, "Allow arbitrary SQL queries via ?sql= parameter"),
ConfigOption(
"default_cache_ttl",
5,

View file

@ -34,3 +34,11 @@ def permission_allowed(datasette, actor, action, resource):
if allow is None:
return True
return actor_matches_allow(actor, allow)
elif action == "execute-sql":
# Use allow_sql block from database block, or from top-level
database_allow_sql = datasette.metadata("allow_sql", database=resource)
if database_allow_sql is None:
database_allow_sql = datasette.metadata("allow_sql")
if database_allow_sql is None:
return True
return actor_matches_allow(actor, database_allow_sql)

View file

@ -22,7 +22,7 @@
{% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %}
{% if config.allow_sql %}
{% if allow_execute_sql %}
<form class="sql" action="{{ database_url(database) }}" method="get">
<h3>Custom SQL query</h3>
<p><textarea id="sql-editor" name="sql">{% if tables %}select * from {{ tables[0].name|escape_sqlite }}{% else %}select sqlite_version(){% endif %}</textarea></p>

View file

@ -35,7 +35,7 @@
<form class="sql" action="{{ database_url(database) }}{% if canned_query %}/{{ canned_query }}{% endif %}" method="{% if canned_write %}post{% else %}get{% endif %}">
<h3>Custom SQL query{% if display_rows %} returning {% if truncated %}more than {% endif %}{{ "{:,}".format(display_rows|length) }} row{% if display_rows|length == 1 %}{% else %}s{% endif %}{% endif %} <span class="show-hide-sql">{% if hide_sql %}(<a href="{{ path_with_removed_args(request, {'_hide_sql': '1'}) }}">show</a>){% else %}(<a href="{{ path_with_added_args(request, {'_hide_sql': '1'}) }}">hide</a>){% endif %}</span></h3>
{% if not hide_sql %}
{% if editable and config.allow_sql %}
{% if editable and allow_execute_sql %}
<p><textarea id="sql-editor" name="sql">{% if query and query.sql %}{{ query.sql }}{% else %}select * from {{ tables[0].name|escape_sqlite }}{% endif %}</textarea></p>
{% else %}
<pre id="sql-query">{% if query %}{{ query.sql }}{% endif %}</pre>

View file

@ -109,7 +109,7 @@
</div>
{% endif %}
{% if query.sql and config.allow_sql %}
{% if query.sql and allow_execute_sql %}
<p><a class="not-underlined" title="{{ query.sql }}" href="{{ database_url(database) }}?{{ {'sql': query.sql}|urlencode|safe }}{% if query.params %}&amp;{{ query.params|urlencode|safe }}{% endif %}">&#x270e; <span class="underlined">View and edit SQL</span></a></p>
{% endif %}

View file

@ -26,8 +26,6 @@ class DatabaseView(DataView):
self.ds.update_with_inherited_metadata(metadata)
if request.args.get("sql"):
if not self.ds.config("allow_sql"):
raise DatasetteError("sql= is not allowed", status=400)
sql = request.args.get("sql")
validate_sql_select(sql)
return await QueryView(self.ds).data(
@ -90,6 +88,9 @@ class DatabaseView(DataView):
"private": not await self.ds.permission_allowed(
None, "view-database", database
),
"allow_execute_sql": await self.ds.permission_allowed(
request.actor, "execute-sql", database, default=True
),
},
{
"show_hidden": request.args.get("_show_hidden"),
@ -289,6 +290,9 @@ class QueryView(DataView):
"columns": columns,
"query": {"sql": sql, "params": params},
"private": private,
"allow_execute_sql": await self.ds.permission_allowed(
request.actor, "execute-sql", database, default=True
),
},
extra_template,
templates,

View file

@ -342,8 +342,10 @@ class TableView(RowTableShared):
extra_wheres_for_ui = []
# Add _where= from querystring
if "_where" in request.args:
if not self.ds.config("allow_sql"):
raise DatasetteError("_where= is not allowed", status=400)
if not await self.ds.permission_allowed(
request.actor, "execute-sql", resource=database, default=True,
):
raise DatasetteError("_where= is not allowed", status=403)
else:
where_clauses.extend(request.args.getlist("_where"))
extra_wheres_for_ui = [
@ -839,6 +841,9 @@ class TableView(RowTableShared):
"next": next_value and str(next_value) or None,
"next_url": next_url,
"private": private,
"allow_execute_sql": await self.ds.permission_allowed(
request.actor, "execute-sql", database, default=True
),
},
extra_template,
(