Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions datasette/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
2 changes: 1 addition & 1 deletion datasette/templates/api_explorer.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

{% block content %}

<h1>API Explorer{% if private %} 🔒{% endif %}</h1>
<h1>API Explorer{% if default_deny and not private %} 🌐{% elif not default_deny and private %} 🔒{% endif %}</h1>

<p>Use this tool to try out the
{% if datasette_version %}
Expand Down
8 changes: 4 additions & 4 deletions datasette/templates/database.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

{% block content %}
<div class="page-header" style="border-color: #{{ database_color }}">
<h1>{{ metadata.title or database }}{% if private %} 🔒{% endif %}</h1>
<h1>{{ metadata.title or database }}{% if default_deny and not private %} 🌐{% elif not default_deny and private %} 🔒{% endif %}</h1>
</div>
{% set action_links, action_title = database_actions(), "Database actions" %}
{% include "_action_menu.html" %}
Expand Down Expand Up @@ -50,7 +50,7 @@ <h3>Custom SQL query</h3>
<h2 id="queries">Queries</h2>
<ul class="bullets">
{% for query in queries %}
<li><a href="{{ urls.query(database, query.name) }}{% if query.fragment %}#{{ query.fragment }}{% endif %}" title="{{ query.description or query.sql }}">{{ query.title or query.name }}</a>{% if query.private %} 🔒{% endif %}</li>
<li><a href="{{ urls.query(database, query.name) }}{% if query.fragment %}#{{ query.fragment }}{% endif %}" title="{{ query.description or query.sql }}">{{ query.title or query.name }}</a>{% if default_deny and not query.private %} 🌐{% elif not default_deny and query.private %} 🔒{% endif %}</li>
{% endfor %}
</ul>
{% endif %}
Expand All @@ -62,7 +62,7 @@ <h2 id="tables">Tables <a style="font-weight: normal; font-size: 0.75em; padding
{% for table in tables %}
{% if show_hidden or not table.hidden %}
<div class="db-table">
<h3><a href="{{ urls.table(database, table.name) }}">{{ table.name }}</a>{% if table.private %} 🔒{% endif %}{% if table.hidden %}<em> (hidden)</em>{% endif %}</h3>
<h3><a href="{{ urls.table(database, table.name) }}">{{ table.name }}</a>{% if default_deny and not table.private %} 🌐{% elif not default_deny and table.private %} 🔒{% endif %}{% if table.hidden %}<em> (hidden)</em>{% endif %}</h3>
<p><em>{% for column in table.columns %}{{ column }}{% if not loop.last %}, {% endif %}{% endfor %}</em></p>
<p>{% if table.count is none %}Many rows{% elif table.count == count_limit + 1 %}&gt;{{ "{:,}".format(count_limit) }} rows{% else %}{{ "{:,}".format(table.count) }} row{% if table.count == 1 %}{% else %}s{% endif %}{% endif %}</p>
</div>
Expand All @@ -77,7 +77,7 @@ <h3><a href="{{ urls.table(database, table.name) }}">{{ table.name }}</a>{% if t
<h2 id="views">Views</h2>
<ul class="bullets">
{% for view in views %}
<li><a href="{{ urls.database(database) }}/{{ view.name|urlencode }}">{{ view.name }}</a>{% if view.private %} 🔒{% endif %}</li>
<li><a href="{{ urls.database(database) }}/{{ view.name|urlencode }}">{{ view.name }}</a>{% if default_deny and not view.private %} 🌐{% elif not default_deny and view.private %} 🔒{% endif %}</li>
{% endfor %}
</ul>
{% endif %}
Expand Down
6 changes: 3 additions & 3 deletions datasette/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
{% block body_class %}index{% endblock %}

{% block content %}
<h1>{{ metadata.title or "Datasette" }}{% if private %} 🔒{% endif %}</h1>
<h1>{{ metadata.title or "Datasette" }}{% if default_deny and not private %} 🌐{% elif not default_deny and private %} 🔒{% endif %}</h1>

{% set action_links, action_title = homepage_actions, "Homepage actions" %}
{% include "_action_menu.html" %}
Expand All @@ -19,7 +19,7 @@ <h1>{{ metadata.title or "Datasette" }}{% if private %} 🔒{% endif %}</h1>
{% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %}

{% for database in databases %}
<h2 style="padding-left: 10px; border-left: 10px solid #{{ database.color }}"><a href="{{ urls.database(database.name) }}">{{ database.name }}</a>{% if database.private %} 🔒{% endif %}</h2>
<h2 style="padding-left: 10px; border-left: 10px solid #{{ database.color }}"><a href="{{ urls.database(database.name) }}">{{ database.name }}</a>{% if default_deny and not database.private %} 🌐{% elif not default_deny and database.private %} 🔒{% endif %}</h2>
<p>
{% 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 -%}
Expand All @@ -30,7 +30,7 @@ <h2 style="padding-left: 10px; border-left: 10px solid #{{ database.color }}"><a
{{ "{:,}".format(database.views_count) }} view{% if database.views_count != 1 %}s{% endif %}
{% endif %}
</p>
<p>{% for table in database.tables_and_views_truncated %}<a href="{{ urls.table(database.name, table.name) }}"{% if table.count %} title="{{ table.count }} rows"{% endif %}>{{ table.name }}</a>{% if table.private %} 🔒{% endif %}{% if not loop.last %}, {% endif %}{% endfor %}{% if database.tables_and_views_more %}, <a href="{{ urls.database(database.name) }}">...</a>{% endif %}</p>
<p>{% for table in database.tables_and_views_truncated %}<a href="{{ urls.table(database.name, table.name) }}"{% if table.count %} title="{{ table.count }} rows"{% endif %}>{{ table.name }}</a>{% 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 %}, <a href="{{ urls.database(database.name) }}">...</a>{% endif %}</p>
{% endfor %}

{% endblock %}
2 changes: 1 addition & 1 deletion datasette/templates/query.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<p class="message-error">This query cannot be executed because the database is immutable.</p>
{% endif %}

<h1 style="padding-left: 10px; border-left: 10px solid #{{ database_color }}">{{ metadata.title or database }}{% if canned_query and not metadata.title %}: {{ canned_query }}{% endif %}{% if private %} 🔒{% endif %}</h1>
<h1 style="padding-left: 10px; border-left: 10px solid #{{ database_color }}">{{ 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 %}</h1>
{% set action_links, action_title = query_actions(), "Query actions" %}
{% include "_action_menu.html" %}

Expand Down
2 changes: 1 addition & 1 deletion datasette/templates/row.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
{% endblock %}

{% block content %}
<h1 style="padding-left: 10px; border-left: 10px solid #{{ database_color }}">{{ table }}: {{ ', '.join(primary_key_values) }}{% if private %} 🔒{% endif %}</h1>
<h1 style="padding-left: 10px; border-left: 10px solid #{{ database_color }}">{{ table }}: {{ ', '.join(primary_key_values) }}{% if default_deny and not private %} 🌐{% elif not default_deny and private %} 🔒{% endif %}</h1>

{% set action_links, action_title = row_actions, "Row actions" %}
{% include "_action_menu.html" %}
Expand Down
2 changes: 1 addition & 1 deletion datasette/templates/table.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

{% block content %}
<div class="page-header" style="border-color: #{{ database_color }}">
<h1>{{ metadata.get("title") or table }}{% if is_view %} (view){% endif %}{% if private %} 🔒{% endif %}</h1>
<h1>{{ metadata.get("title") or table }}{% if is_view %} (view){% endif %}{% if default_deny and not private %} 🌐{% elif not default_deny and private %} 🔒{% endif %}</h1>
</div>
{% set action_links, action_title = actions(), "View actions" if is_view else "Table actions" %}
{% include "_action_menu.html" %}
Expand Down
33 changes: 33 additions & 0 deletions tests/test_default_deny.py
Original file line number Diff line number Diff line change
Expand Up @@ -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</a> 🌐</h3>" in response.text
assert ">private_table</a> 🔒</h3>" not in response.text


@pytest.mark.asyncio
async def test_default_deny_basic_permissions():
"""Test that default_deny=True denies basic permissions"""
Expand Down