import pytest from datasette.app import Datasette from datasette.resources import DatabaseResource, TableResource @pytest.mark.asyncio async def test_default_deny_denies_default_permissions(): """Test that default_deny=True denies default permissions""" # Without default_deny, anonymous users can view instance/database/tables ds_normal = Datasette() await ds_normal.invoke_startup() # Add a test database db = ds_normal.add_memory_database("test_db_normal") await db.execute_write("create table test_table (id integer primary key)") await ds_normal._refresh_schemas() # Trigger catalog refresh # Test default behavior - anonymous user should be able to view response = await ds_normal.client.get("/") assert response.status_code == 200 response = await ds_normal.client.get("/test_db_normal") assert response.status_code == 200 response = await ds_normal.client.get("/test_db_normal/test_table") assert response.status_code == 200 # With default_deny=True, anonymous users should be denied ds_deny = Datasette(default_deny=True) await ds_deny.invoke_startup() # Add the same test database db = ds_deny.add_memory_database("test_db_deny") await db.execute_write("create table test_table (id integer primary key)") await ds_deny._refresh_schemas() # Trigger catalog refresh # Anonymous user should be denied response = await ds_deny.client.get("/") assert response.status_code == 403 response = await ds_deny.client.get("/test_db_deny") assert response.status_code == 403 response = await ds_deny.client.get("/test_db_deny/test_table") assert response.status_code == 403 @pytest.mark.asyncio async def test_default_deny_with_root_user(): """Test that root user still has access when default_deny=True""" ds = Datasette(default_deny=True) ds.root_enabled = True await ds.invoke_startup() root_actor = {"id": "root"} # Root user should have all permissions even with default_deny assert await ds.allowed(action="view-instance", actor=root_actor) is True assert ( await ds.allowed( action="view-database", actor=root_actor, resource=DatabaseResource("test_db"), ) is True ) assert ( await ds.allowed( action="view-table", actor=root_actor, resource=TableResource("test_db", "test_table"), ) is True ) assert ( await ds.allowed( action="execute-sql", actor=root_actor, resource=DatabaseResource("test_db") ) is True ) @pytest.mark.asyncio async def test_default_deny_with_config_allow(): """Test that config allow rules still work with default_deny=True""" ds = Datasette(default_deny=True, config={"allow": {"id": "user1"}}) await ds.invoke_startup() # Anonymous user should be denied assert await ds.allowed(action="view-instance", actor=None) is False # Authenticated user with explicit permission should have access assert await ds.allowed(action="view-instance", actor={"id": "user1"}) is True # Different user should be denied assert await ds.allowed(action="view-instance", actor={"id": "user2"}) is False @pytest.mark.asyncio async def test_default_deny_basic_permissions(): """Test that default_deny=True denies basic permissions""" ds = Datasette(default_deny=True) await ds.invoke_startup() # Anonymous user should be denied all default permissions assert await ds.allowed(action="view-instance", actor=None) is False assert ( await ds.allowed( action="view-database", actor=None, resource=DatabaseResource("test_db") ) is False ) assert ( await ds.allowed( action="view-table", actor=None, resource=TableResource("test_db", "test_table"), ) is False ) assert ( await ds.allowed( action="execute-sql", actor=None, resource=DatabaseResource("test_db") ) is False ) # Authenticated user without explicit permission should also be denied assert await ds.allowed(action="view-instance", actor={"id": "user"}) is False @pytest.mark.asyncio async def test_default_deny_root_no_config_index_does_not_500(): # https://github.com/simonw/datasette/issues/2644 # --default-deny --root with no config file must not 500 on the index # pages. Rendering those pages computes is_private (include_is_private), # which references the anon_rules CTE - that CTE must still be defined # even when there are no anonymous permission rules at all. ds = Datasette(default_deny=True) ds.root_enabled = True await ds.invoke_startup() db = ds.add_memory_database("test_db_2644") await db.execute_write("create table test_table (id integer primary key)") await ds._refresh_schemas() cookie = ds.sign({"a": {"id": "root"}}, "actor") for path in ("/", "/test_db_2644", "/test_db_2644/test_table"): response = await ds.client.get(path, cookies={"ds_actor": cookie}) assert response.status_code == 200, f"{path} returned {response.status_code}"