Comments (6)
While investigating this issue I created a new debug mechanism, see this issue:
from datasette.
I implemented the new pattern like so:
diff --git a/datasette/app.py b/datasette/app.py
index d943b97b..5a54630c 100644
--- a/datasette/app.py
+++ b/datasette/app.py
@@ -903,6 +903,8 @@ class Datasette:
# Use default from registered permission, if available
if default is DEFAULT_NOT_SET and action in self.permissions:
default = self.permissions[action].default
+ results = []
+ # Every plugin gets a go
for check in pm.hook.permission_allowed(
datasette=self,
actor=actor,
@@ -911,7 +913,16 @@ class Datasette:
):
check = await await_me_maybe(check)
if check is not None:
- result = check
+ results.append(check)
+
+ result = None
+ # If anyone said False it's false
+ if any(r is False for r in results):
+ result = False
+ elif any(r is True for r in results):
+ # Otherwise, if anyone said True it's true
+ result = True
+
used_default = False
if result is None:
result = default
All of the tests pass with the exception of this one:
def test_permissions_cascade(cascade_app_client, path, permissions, expected_status):
# ...
> assert (
response.status == expected_status
), "path: {}, permissions: {}, expected_status: {}, status: {}".format(
path, permissions, expected_status, response.status
)
E AssertionError: path: /fixtures.db, permissions: ['download'], expected_status: 200, status: 403
E assert 403 == 200
E + where 403 = <datasette.utils.testing.TestResponse object at 0x107ec6e30>.status
/Users/simon/Dropbox/Development/datasette/tests/test_permissions.py:535: AssertionError
================================== short test summary info ==================================
FAILED tests/test_permissions.py::test_permissions_cascade[/fixtures.db-permissions26-200] - AssertionError: path: /fixtures.db, permissions: ['download'], expected_status: 200, sta...
============================= 1 failed, 711 deselected in 1.70s =============================
Here's the relevant source code for that test:
datasette/tests/test_permissions.py
Lines 506 to 541 in 232a304
So this tests expects that if an actor has "can_download": 1
they should be able to download the database file - response.status
should be 200, but the test fails because it comes back 403
.
Here's the plugin that respects that can_download
thing:
datasette/tests/plugins/my_plugin.py
Lines 211 to 218 in 232a304
I don't fully understand this failure, in part because I don't understand how the configuration works that default-blocks the database download.
from datasette.
Using this new feature:
DATASETTE_TRACE_PLUGINS=1 just test --lf -x --pdb
Gives:
actor_from_request:
{ 'datasette': <datasette.app.Datasette object at 0x10914a110>,
'request': <asgi.Request method="GET" url="http://localhost/fixtures.db">}
Hook implementations:
[ <HookImpl plugin_name='datasette.actor_auth_cookie', plugin=<module 'datasette.actor_auth_cookie' from '/Users/simon/Dropbox/Development/datasette/datasette/actor_auth_cookie.py'>>,
<HookImpl plugin_name='datasette.default_permissions', plugin=<module 'datasette.default_permissions' from '/Users/simon/Dropbox/Development/datasette/datasette/default_permissions.py'>>,
<HookImpl plugin_name='my_plugin.py', plugin=<module 'my_plugin.py' from '/Users/simon/Dropbox/Development/datasette/tests/plugins/my_plugin.py'>>,
<HookImpl plugin_name='my_plugin_2.py', plugin=<module 'my_plugin_2.py' from '/Users/simon/Dropbox/Development/datasette/tests/plugins/my_plugin_2.py'>>]
Results:
[ <function actor_from_request.<locals>.inner at 0x10941c280>,
{'can_download': 1, 'id': 'test'}]
permission_allowed:
{ 'action': 'view-database-download',
'actor': { 'can_download': 1,
'id': 'test'},
'datasette': <datasette.app.Datasette object at 0x10914a110>,
'resource': 'fixtures'}
Hook implementations:
[ <HookImpl plugin_name='datasette.default_permissions', plugin=<module 'datasette.default_permissions' from '/Users/simon/Dropbox/Development/datasette/datasette/default_permissions.py'>>,
<HookImpl plugin_name='my_plugin.py', plugin=<module 'my_plugin.py' from '/Users/simon/Dropbox/Development/datasette/tests/plugins/my_plugin.py'>>,
<HookImpl plugin_name='my_plugin_2.py', plugin=<module 'my_plugin_2.py' from '/Users/simon/Dropbox/Development/datasette/tests/plugins/my_plugin_2.py'>>,
<HookImpl plugin_name='datasette.default_permissions', plugin=<module 'datasette.default_permissions' from '/Users/simon/Dropbox/Development/datasette/datasette/default_permissions.py'>>]
Results:
[ <function permission_allowed_default.<locals>.inner at 0x10941c5e0>,
<function permission_allowed.<locals>.inner at 0x10941c790>,
1]
permission_allowed:
{ 'action': 'view-database',
'actor': { 'can_download': 1,
'id': 'test'},
'datasette': <datasette.app.Datasette object at 0x10914a110>,
'resource': 'fixtures'}
Hook implementations:
[ <HookImpl plugin_name='datasette.default_permissions', plugin=<module 'datasette.default_permissions' from '/Users/simon/Dropbox/Development/datasette/datasette/default_permissions.py'>>,
<HookImpl plugin_name='my_plugin.py', plugin=<module 'my_plugin.py' from '/Users/simon/Dropbox/Development/datasette/tests/plugins/my_plugin.py'>>,
<HookImpl plugin_name='my_plugin_2.py', plugin=<module 'my_plugin_2.py' from '/Users/simon/Dropbox/Development/datasette/tests/plugins/my_plugin_2.py'>>,
<HookImpl plugin_name='datasette.default_permissions', plugin=<module 'datasette.default_permissions' from '/Users/simon/Dropbox/Development/datasette/datasette/default_permissions.py'>>]
Results:
[ <function permission_allowed_default.<locals>.inner at 0x10941c040>,
<function permission_allowed.<locals>.inner at 0x10941c430>]
forbidden:
{ 'datasette': <datasette.app.Datasette object at 0x10914a110>,
'message': 'view-database',
'request': <asgi.Request method="GET" url="http://localhost/fixtures.db">}
Hook implementations:
[ <HookImpl plugin_name='datasette.forbidden', plugin=<module 'datasette.forbidden' from '/Users/simon/Dropbox/Development/datasette/datasette/forbidden.py'>>,
<HookImpl plugin_name='my_plugin.py', plugin=<module 'my_plugin.py' from '/Users/simon/Dropbox/Development/datasette/tests/plugins/my_plugin.py'>>]
Results:
[ <function forbidden.<locals>.inner at 0x10941c5e0>]
Which suggests to me that possibly permission_allowed
is passing for view-database-download
and then failing for view-database
.
But I'd expect that to work because of the cascade here:
datasette/datasette/views/database.py
Lines 296 to 305 in 232a304
from datasette.
Weirdly in the debugger:
> /Users/simon/Dropbox/Development/datasette/datasette/app.py(972)ensure_permissions()
-> if ok is not None:
(Pdb) ok
(Pdb) actor
{'id': 'test', 'can_download': 1}
(Pdb) action
'view-database-download'
(Pdb) list
967 actor,
968 action,
969 resource=resource,
970 default=None,
971 )
972 -> if ok is not None:
973 if ok:
974 return
975 else:
976 raise Forbidden(action)
977
(Pdb) resource
'fixtures'
(Pdb) actor
{'id': 'test', 'can_download': 1}
(Pdb) action
'view-database-download'
(Pdb) type(ok)
<class 'NoneType'>
I would expect ok
to be True
there because the view-database-download
check should have succeeded.
from datasette.
It failed because the plugin returned 1
but my code checked for any(r is True for r in results)
.
from datasette.
Updated documentation to explain this more clearly: https://docs.datasette.io/en/latest/authentication.html#how-permissions-are-resolved
from datasette.
Related Issues (20)
- Fix font size on filter inputs
- base_url getting appended twice in redirects when applying filters? HOT 3
- Accessibility: add a `lang` attribute to `html` HOT 1
- What minimal SQLite version should Datasette support? HOT 9
- Remove upserts in `set_XXX_metadata()` methods
- PyOdide test failure HOT 7
- Canned queries with named parameters fail with error against SQLite 3.46.0 HOT 13
- derive_named_parameters() method that works with latest SQLite HOT 5
- Flaky test_max_csv_mb test HOT 3
- Very weird flaky test_create_table_ignore_replace and test_upsert tests HOT 18
- multiple plugins extending the same base template?
- Consider using isolation_level="IMMEDIATE" for write connections HOT 5
- Database/Table/Row not found errors echo back text from URL HOT 7
- Proposal — Datasette JSON API changes for 1.0 HOT 4
- Block or rate limit based on User Agent? HOT 3
- Datasette 1.0 Canned Queries Broken HOT 3
- [Error] no such table: main.events HOT 1
- Cannot run datasette on 3.13 HOT 1
- Documentation Bug: URL Syntax at SQLite Full-Text Search with Wildcards
- FTS5 wildcard query does not work as Intended
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from datasette.