Better example plugin for permission_allowed

Also fixed it so default permission checks run after plugin permission checks, refs #818
This commit is contained in:
Simon Willison 2020-06-08 15:09:57 -07:00
commit e0a4664fba
3 changed files with 41 additions and 5 deletions

View file

@ -2,7 +2,7 @@ from datasette import hookimpl
from datasette.utils import actor_matches_allow
@hookimpl
@hookimpl(tryfirst=True)
def permission_allowed(datasette, actor, action, resource):
if action == "permissions-debug":
if actor and actor.get("id") == "root":

View file

@ -174,11 +174,11 @@ To limit access to the ``users`` table in your ``bakery.db`` database:
This works for SQL views as well - you can treat them as if they are tables.
.. warning::
Restricting access to tables and views in this way will NOT prevent users from querying them using arbitrary SQL queries.
Restricting access to tables and views in this way will NOT prevent users from querying them using arbitrary SQL queries, `like this <https://latest.datasette.io/fixtures?sql=select+*+from+facetable>`__ for example.
If you are restricting access to specific tables you should also use the ``"allow_sql"`` block to prevent users from accessing
.. _authentication_permissions_table:
.. _authentication_permissions_query:
Controlling access to specific canned queries
---------------------------------------------

View file

@ -1006,7 +1006,7 @@ Instead of returning a dictionary, this function can return an awaitable functio
.. _plugin_permission_allowed:
permission_allowed(datasette, actor, action, resource)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``datasette`` - :ref:`internals_datasette`
You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)``, or to execute SQL queries.
@ -1022,4 +1022,40 @@ permission_allowed(datasette, actor, action, resource)
Called to check that an actor has permission to perform an action on a resource. Can return ``True`` if the action is allowed, ``False`` if the action is not allowed or ``None`` if the plugin does not have an opinion one way or the other.
See :ref:`permissions` for a full list of permissions included in Datasette core.
Here's an example plugin which randomly selects if a permission should be allowed or denied, except for ``view-instance`` which always uses the default permission scheme instead.
.. code-block:: python
from datasette import hookimpl
import random
@hookimpl
def permission_allowed(action):
if action != "view-instance":
# Return True or False at random
return random.random() > 0.5
# Returning None falls back to default permissions
This function can alternatively return an awaitable function which itself returns ``True``, ``False`` or ``None``. You can use this option if you need to execute additional database queries using ``await datasette.execute(...)``.
Here's an example that allows users to view the ``admin_log`` table only if their actor ``id`` is present in the ``admin_users`` table. It aso disallows arbitrary SQL queries for the ``staff.db`` database for all users.
.. code-block:: python
@hookimpl
def permission_allowed(datasette, actor, action, resource):
async def inner():
if action == "execute-sql" and resource == "staff":
return False
if action == "view-table" and resource == ("staff", "admin_log"):
if not actor:
return False
user_id = actor["id"]
return await datasette.get_database("staff").execute(
"select count(*) from admin_users where user_id = :user_id",
{"user_id": user_id},
)
return inner
See :ref:`permissions` for a full list of permissions that are included in Datasette core.