diff --git a/datasette/app.py b/datasette/app.py index 2df6e4e8e1..b521a8c74a 100644 --- a/datasette/app.py +++ b/datasette/app.py @@ -1820,6 +1820,7 @@ async def menu_links(): "base_url": self.setting("base_url"), "csrftoken": request.scope["csrftoken"] if request else lambda: "", "datasette_version": __version__, + "default_deny": self.default_deny, }, **extra_template_vars, } diff --git a/datasette/templates/api_explorer.html b/datasette/templates/api_explorer.html index dc393c203e..cb9da81136 100644 --- a/datasette/templates/api_explorer.html +++ b/datasette/templates/api_explorer.html @@ -8,7 +8,7 @@ {% block content %} -

API Explorer{% if private %} 🔒{% endif %}

+

API Explorer{% if default_deny and not private %} 🌐{% elif not default_deny and private %} 🔒{% endif %}

Use this tool to try out the {% if datasette_version %} diff --git a/datasette/templates/database.html b/datasette/templates/database.html index 42b4ca0b73..25207f91d9 100644 --- a/datasette/templates/database.html +++ b/datasette/templates/database.html @@ -15,7 +15,7 @@ {% block content %}

{% set action_links, action_title = database_actions(), "Database actions" %} {% include "_action_menu.html" %} @@ -50,7 +50,7 @@

Custom SQL query

Queries

{% endif %} @@ -62,7 +62,7 @@

Tables -

{{ table.name }}{% if table.private %} 🔒{% endif %}{% if table.hidden %} (hidden){% endif %}

+

{{ table.name }}{% if default_deny and not table.private %} 🌐{% elif not default_deny and table.private %} 🔒{% endif %}{% if table.hidden %} (hidden){% endif %}

{% for column in table.columns %}{{ column }}{% if not loop.last %}, {% endif %}{% endfor %}

{% if table.count is none %}Many rows{% elif table.count == count_limit + 1 %}>{{ "{:,}".format(count_limit) }} rows{% else %}{{ "{:,}".format(table.count) }} row{% if table.count == 1 %}{% else %}s{% endif %}{% endif %}

@@ -77,7 +77,7 @@

{{ table.name }}{% if t

Views

{% endif %} diff --git a/datasette/templates/index.html b/datasette/templates/index.html index 0334927983..57bdb4e9d9 100644 --- a/datasette/templates/index.html +++ b/datasette/templates/index.html @@ -9,7 +9,7 @@ {% block body_class %}index{% endblock %} {% block content %} -

{{ metadata.title or "Datasette" }}{% if private %} 🔒{% endif %}

+

{{ metadata.title or "Datasette" }}{% if default_deny and not private %} 🌐{% elif not default_deny and private %} 🔒{% endif %}

{% set action_links, action_title = homepage_actions, "Homepage actions" %} {% include "_action_menu.html" %} @@ -19,7 +19,7 @@

{{ metadata.title or "Datasette" }}{% if private %} 🔒{% endif %}

{% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %} {% for database in databases %} -

{{ database.name }}{% if database.private %} 🔒{% endif %}

+

{{ database.name }}{% if default_deny and not database.private %} 🌐{% elif not default_deny and database.private %} 🔒{% endif %}

{% if database.show_table_row_counts %}{{ "{:,}".format(database.table_rows_sum) }} rows in {% endif %}{{ database.tables_count }} table{% if database.tables_count != 1 %}s{% endif %}{% if database.hidden_tables_count %}, {% endif -%} {% if database.hidden_tables_count -%} @@ -30,7 +30,7 @@

-

{% for table in database.tables_and_views_truncated %}{{ table.name }}{% if table.private %} 🔒{% endif %}{% if not loop.last %}, {% endif %}{% endfor %}{% if database.tables_and_views_more %}, ...{% endif %}

+

{% for table in database.tables_and_views_truncated %}{{ table.name }}{% if default_deny and not table.private %} 🌐{% elif not default_deny and table.private %} 🔒{% endif %}{% if not loop.last %}, {% endif %}{% endfor %}{% if database.tables_and_views_more %}, ...{% endif %}

{% endfor %} {% endblock %} diff --git a/datasette/templates/query.html b/datasette/templates/query.html index a6e9a3aa52..9621237cc4 100644 --- a/datasette/templates/query.html +++ b/datasette/templates/query.html @@ -28,7 +28,7 @@

This query cannot be executed because the database is immutable.

{% endif %} -

{{ metadata.title or database }}{% if canned_query and not metadata.title %}: {{ canned_query }}{% endif %}{% if private %} 🔒{% endif %}

+

{{ metadata.title or database }}{% if canned_query and not metadata.title %}: {{ canned_query }}{% endif %}{% if default_deny and not private %} 🌐{% elif not default_deny and private %} 🔒{% endif %}

{% set action_links, action_title = query_actions(), "Query actions" %} {% include "_action_menu.html" %} diff --git a/datasette/templates/row.html b/datasette/templates/row.html index db43e71a60..0a19f9a22c 100644 --- a/datasette/templates/row.html +++ b/datasette/templates/row.html @@ -20,7 +20,7 @@ {% endblock %} {% block content %} -

{{ table }}: {{ ', '.join(primary_key_values) }}{% if private %} 🔒{% endif %}

+

{{ table }}: {{ ', '.join(primary_key_values) }}{% if default_deny and not private %} 🌐{% elif not default_deny and private %} 🔒{% endif %}

{% set action_links, action_title = row_actions, "Row actions" %} {% include "_action_menu.html" %} diff --git a/datasette/templates/table.html b/datasette/templates/table.html index 25ff31efaf..9237d3d632 100644 --- a/datasette/templates/table.html +++ b/datasette/templates/table.html @@ -22,7 +22,7 @@ {% block content %} {% set action_links, action_title = actions(), "View actions" if is_view else "Table actions" %} {% include "_action_menu.html" %} diff --git a/tests/test_default_deny.py b/tests/test_default_deny.py index 81e95b845b..890c381291 100644 --- a/tests/test_default_deny.py +++ b/tests/test_default_deny.py @@ -96,6 +96,39 @@ async def test_default_deny_with_config_allow(): assert await ds.allowed(action="view-instance", actor={"id": "user2"}) is False +@pytest.mark.asyncio +async def test_default_deny_shows_public_icons_not_private_icons(): + ds = Datasette( + default_deny=True, + config={ + "allow": {"id": "user1"}, + "databases": { + "test_db": { + "allow": True, + "tables": { + "public_table": {"allow": True}, + "private_table": {"allow": {"id": "user1"}}, + }, + } + }, + }, + ) + await ds.invoke_startup() + + db = ds.add_memory_database("test_db") + await db.execute_write("create table public_table (id integer primary key)") + await db.execute_write("create table private_table (id integer primary key)") + await ds._refresh_schemas() + + cookie = ds.client.actor_cookie({"id": "user1"}) + response = await ds.client.get("/test_db", cookies={"ds_actor": cookie}) + assert response.status_code == 200 + + # In --default-deny mode show public icons, not private padlocks + assert ">public_table 🌐" in response.text + assert ">private_table 🔒" not in response.text + + @pytest.mark.asyncio async def test_default_deny_basic_permissions(): """Test that default_deny=True denies basic permissions"""