From 264bba5db22df5811ee4a9c66ecff3287500a6ff Mon Sep 17 00:00:00 2001 From: Automated Date: Wed, 5 Nov 2025 18:51:58 +0000 Subject: [PATCH] Populate docs/ from 0.65.2 --- docs/_templates/base.html | 31 - docs/authentication.rst | 1183 ++++++------------------------------- docs/changelog.rst | 520 +--------------- docs/cli-reference.rst | 134 +---- docs/conf.py | 26 +- docs/contributing.rst | 69 +-- docs/custom_templates.rst | 138 +++-- docs/deploying.rst | 4 +- docs/facets.rst | 131 +--- docs/full_text_search.rst | 45 +- docs/getting_started.rst | 27 +- docs/index.rst | 10 +- docs/installation.rst | 43 +- docs/internals.rst | 930 ++++------------------------- docs/introspection.rst | 62 +- docs/json_api.rst | 708 +++------------------- docs/metadata.rst | 520 ++++------------ docs/pages.rst | 46 +- docs/plugin_hooks.rst | 1014 +++---------------------------- docs/plugins.rst | 277 +-------- docs/publish.rst | 4 +- docs/settings.rst | 78 +-- docs/spatialite.rst | 5 +- docs/sql_queries.rst | 356 ++--------- docs/testing_plugins.rst | 60 +- docs/writing_plugins.rst | 165 +----- 26 files changed, 914 insertions(+), 5672 deletions(-) diff --git a/docs/_templates/base.html b/docs/_templates/base.html index 9dea86eb..969de5ab 100644 --- a/docs/_templates/base.html +++ b/docs/_templates/base.html @@ -4,34 +4,3 @@ {{ super() }} {% endblock %} - -{% block scripts %} -{{ super() }} - -{% endblock %} diff --git a/docs/authentication.rst b/docs/authentication.rst index e69b0aa4..5ffddfae 100644 --- a/docs/authentication.rst +++ b/docs/authentication.rst @@ -4,20 +4,20 @@ Authentication and permissions ================================ -Datasette doesn't require authentication by default. Any visitor to a Datasette instance can explore the full data and execute read-only SQL queries. +Datasette does not require authentication by default. Any visitor to a Datasette instance can explore the full data and execute read-only SQL queries. -Datasette can be configured to only allow authenticated users, or to control which databases, tables, and queries can be accessed by the public or by specific users. Datasette's plugin system can be used to add many different styles of authentication, such as user accounts, single sign-on or API keys. +Datasette's plugin system can be used to add many different styles of authentication, such as user accounts, single sign-on or API keys. .. _authentication_actor: Actors ====== -Through plugins, Datasette can support both authenticated users (with cookies) and authenticated API clients (via authentication tokens). The word "actor" is used to cover both of these cases. +Through plugins, Datasette can support both authenticated users (with cookies) and authenticated API agents (via authentication tokens). The word "actor" is used to cover both of these cases. -Every request to Datasette has an associated actor value, available in the code as ``request.actor``. This can be ``None`` for unauthenticated requests, or a JSON compatible Python dictionary for authenticated users or API clients. +Every request to Datasette has an associated actor value, available in the code as ``request.actor``. This can be ``None`` for unauthenticated requests, or a JSON compatible Python dictionary for authenticated users or API agents. -The actor dictionary can be any shape - the design of that data structure is left up to the plugins. Actors should always include a unique ``"id"`` string, as demonstrated by the "root" actor below. +The actor dictionary can be any shape - the design of that data structure is left up to the plugins. A useful convention is to include an ``"id"`` string, as demonstrated by the "root" actor below. Plugins can use the :ref:`plugin_hook_actor_from_request` hook to implement custom logic for authenticating an actor based on the incoming HTTP request. @@ -28,33 +28,18 @@ Using the "root" actor Datasette currently leaves almost all forms of authentication to plugins - `datasette-auth-github `__ for example. -The one exception is the "root" account, which you can sign into while using Datasette on your local machine. The root user has **all permissions** - they can perform any action regardless of other permission rules. - -The ``--root`` flag is designed for local development and testing. When you start Datasette with ``--root``, the root user automatically receives every permission, including: - -* All view permissions (``view-instance``, ``view-database``, ``view-table``, etc.) -* All write permissions (``insert-row``, ``update-row``, ``delete-row``, ``create-table``, ``alter-table``, ``drop-table``) -* Debug permissions (``permissions-debug``, ``debug-menu``) -* Any custom permissions defined by plugins - -If you add explicit deny rules in ``datasette.yaml`` those can still block the -root actor from specific databases or tables. - -The ``--root`` flag sets an internal ``root_enabled`` switch—without it, a signed-in user with ``{"id": "root"}`` is treated like any other actor. +The one exception is the "root" account, which you can sign into while using Datasette on your local machine. This provides access to a small number of debugging features. To sign in as root, start Datasette using the ``--root`` command-line option, like this:: - datasette --root - -Datasette will output a single-use-only login URL on startup:: - + $ datasette --root http://127.0.0.1:8001/-/auth-token?token=786fc524e0199d70dc9a581d851f466244e114ca92f33aa3b42a139e9388daa7 INFO: Started server process [25801] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8001 (Press CTRL+C to quit) -Click on that link and then visit ``http://127.0.0.1:8001/-/actor`` to confirm that you are authenticated as an actor that looks like this: +The URL on the first line includes a one-use token which can be used to sign in as the "root" actor in your browser. Click on that link and then visit ``http://127.0.0.1:8001/-/actor`` to confirm that you are authenticated as an actor that looks like this: .. code-block:: json @@ -67,7 +52,7 @@ Click on that link and then visit ``http://127.0.0.1:8001/-/actor`` to confirm t Permissions =========== -Datasette's permissions system is built around SQL queries. Datasette and its plugins construct SQL queries to resolve the list of resources that an actor cas access. +Datasette has an extensive permissions system built-in, which can be further extended and customized by plugins. The key question the permissions system answers is this: @@ -75,79 +60,30 @@ The key question the permissions system answers is this: **Actors** are :ref:`described above `. -An **action** is a string describing the action the actor would like to perform. A full list is :ref:`provided below ` - examples include ``view-table`` and ``execute-sql``. +An **action** is a string describing the action the actor would like to perform. A full list is :ref:`provided below ` - examples include ``view-table`` and ``execute-sql``. A **resource** is the item the actor wishes to interact with - for example a specific database or table. Some actions, such as ``permissions-debug``, are not associated with a particular resource. -Datasette's built-in view actions (``view-database``, ``view-table`` etc) are allowed by Datasette's default configuration: unless you :ref:`configure additional permission rules ` unauthenticated users will be allowed to access content. +Datasette's built-in view permissions (``view-database``, ``view-table`` etc) default to *allow* - unless you :ref:`configure additional permission rules ` unauthenticated users will be allowed to access content. -Other actions, including those introduced by plugins, will default to *deny*. - -.. _authentication_permissions_explained: - -How permissions are resolved ----------------------------- - -Datasette performs permission checks using the internal :ref:`datasette_allowed`, method which accepts keyword arguments for ``action``, ``resource`` and an optional ``actor``. - -``resource`` should be an instance of the appropriate ``Resource`` subclass from :mod:`datasette.resources`—for example ``InstanceResource()``, ``DatabaseResource(database="...``)`` or ``TableResource(database="...", table="...")``. This defaults to ``InstanceResource()`` if not specified. - -When a check runs Datasette gathers allow/deny rules from multiple sources and -compiles them into a SQL query. The resulting query describes all of the -resources an actor may access for that action, together with the reasons those -resources were allowed or denied. The combined sources are: - -* ``allow`` blocks configured in :ref:`datasette.yaml `. -* :ref:`Actor restrictions ` encoded into the actor dictionary or API token. -* The "root" user shortcut when ``--root`` (or :attr:`Datasette.root_enabled `) is active, replying ``True`` to all permission chucks unless configuration rules deny them at a more specific level. -* Any additional SQL provided by plugins implementing :ref:`plugin_hook_permission_resources_sql`. - -Datasette evaluates the SQL to determine if the requested ``resource`` is -included. Explicit deny rules returned by configuration or plugins will block -access even if other rules allowed it. +Permissions with potentially harmful effects should default to *deny*. Plugin authors should account for this when designing new plugins - for example, the `datasette-upload-csvs `__ plugin defaults to deny so that installations don't accidentally allow unauthenticated users to create new tables by uploading a CSV file. .. _authentication_permissions_allow: Defining permissions with "allow" blocks ---------------------------------------- -One way to define permissions in Datasette is to use an ``"allow"`` block :ref:`in the datasette.yaml file `. This is a JSON document describing which actors are allowed to perform an action against a specific resource. - -Each ``allow`` block is compiled into SQL and combined with any -:ref:`plugin-provided rules ` to produce -the cascading allow/deny decisions that power :ref:`datasette_allowed`. +The standard way to define permissions in Datasette is to use an ``"allow"`` block. This is a JSON document describing which actors are allowed to perform a permission. The most basic form of allow block is this (`allow demo `__, `deny demo `__): -.. [[[cog - from metadata_doc import config_example - import textwrap - config_example(cog, textwrap.dedent( - """ - allow: - id: root - """).strip(), - "YAML", "JSON" - ) -.. ]]] +.. code-block:: json -.. tab:: YAML - - .. code-block:: yaml - - allow: - id: root - -.. tab:: JSON - - .. code-block:: json - - { - "allow": { + { + "allow": { "id": "root" - } } -.. [[[end]]] + } This will match any actors with an ``"id"`` property of ``"root"`` - for example, an actor that looks like this: @@ -160,98 +96,29 @@ This will match any actors with an ``"id"`` property of ``"root"`` - for example An allow block can specify "deny all" using ``false`` (`demo `__): -.. [[[cog - from metadata_doc import config_example - import textwrap - config_example(cog, textwrap.dedent( - """ - allow: false - """).strip(), - "YAML", "JSON" - ) -.. ]]] +.. code-block:: json -.. tab:: YAML - - .. code-block:: yaml - - allow: false - -.. tab:: JSON - - .. code-block:: json - - { - "allow": false - } -.. [[[end]]] + { + "allow": false + } An ``"allow"`` of ``true`` allows all access (`demo `__): -.. [[[cog - from metadata_doc import config_example - import textwrap - config_example(cog, textwrap.dedent( - """ - allow: true - """).strip(), - "YAML", "JSON" - ) -.. ]]] +.. code-block:: json -.. tab:: YAML - - .. code-block:: yaml - - allow: true - -.. tab:: JSON - - .. code-block:: json - - { - "allow": true - } -.. [[[end]]] + { + "allow": true + } Allow keys can provide a list of values. These will match any actor that has any of those values (`allow demo `__, `deny demo `__): -.. [[[cog - from metadata_doc import config_example - import textwrap - config_example(cog, textwrap.dedent( - """ - allow: - id: - - simon - - cleopaws - """).strip(), - "YAML", "JSON" - ) -.. ]]] +.. code-block:: json -.. tab:: YAML - - .. code-block:: yaml - - allow: - id: - - simon - - cleopaws - -.. tab:: JSON - - .. code-block:: json - - { - "allow": { - "id": [ - "simon", - "cleopaws" - ] - } + { + "allow": { + "id": ["simon", "cleopaws"] } -.. [[[end]]] + } This will match any actor with an ``"id"`` of either ``"simon"`` or ``"cleopaws"``. @@ -259,154 +126,53 @@ Actors can have properties that feature a list of values. These will be matched .. code-block:: json - { - "id": "simon", - "roles": ["staff", "developer"] - } + { + "id": "simon", + "roles": ["staff", "developer"] + } This allow block will provide access to any actor that has ``"developer"`` as one of their roles (`allow demo `__, `deny demo `__): -.. [[[cog - from metadata_doc import config_example - import textwrap - config_example(cog, textwrap.dedent( - """ - allow: - roles: - - developer - """).strip(), - "YAML", "JSON" - ) -.. ]]] +.. code-block:: json -.. tab:: YAML - - .. code-block:: yaml - - allow: - roles: - - developer - -.. tab:: JSON - - .. code-block:: json - - { - "allow": { - "roles": [ - "developer" - ] - } + { + "allow": { + "roles": ["developer"] } -.. [[[end]]] + } Note that "roles" is not a concept that is baked into Datasette - it's a convention that plugins can choose to implement and act on. If you want to provide access to any actor with a value for a specific key, use ``"*"``. For example, to match any logged-in user specify the following (`allow demo `__, `deny demo `__): -.. [[[cog - from metadata_doc import config_example - import textwrap - config_example(cog, textwrap.dedent( - """ - allow: - id: "*" - """).strip(), - "YAML", "JSON" - ) -.. ]]] +.. code-block:: json -.. tab:: YAML - - .. code-block:: yaml - - allow: - id: "*" - -.. tab:: JSON - - .. code-block:: json - - { - "allow": { + { + "allow": { "id": "*" - } } -.. [[[end]]] + } You can specify that only unauthenticated actors (from anonymous HTTP requests) should be allowed access using the special ``"unauthenticated": true`` key in an allow block (`allow demo `__, `deny demo `__): -.. [[[cog - from metadata_doc import config_example - import textwrap - config_example(cog, textwrap.dedent( - """ - allow: - unauthenticated: true - """).strip(), - "YAML", "JSON" - ) -.. ]]] +.. code-block:: json -.. tab:: YAML - - .. code-block:: yaml - - allow: - unauthenticated: true - -.. tab:: JSON - - .. code-block:: json - - { - "allow": { + { + "allow": { "unauthenticated": true - } } -.. [[[end]]] + } Allow keys act as an "or" mechanism. An actor will be able to execute the query if any of their JSON properties match any of the values in the corresponding lists in the ``allow`` block. The following block will allow users with either a ``role`` of ``"ops"`` OR users who have an ``id`` of ``"simon"`` or ``"cleopaws"``: -.. [[[cog - from metadata_doc import config_example - import textwrap - config_example(cog, textwrap.dedent( - """ - allow: - id: - - simon - - cleopaws - role: ops - """).strip(), - "YAML", "JSON" - ) -.. ]]] +.. code-block:: json -.. tab:: YAML - - .. code-block:: yaml - - allow: - id: - - simon - - cleopaws - role: ops - -.. tab:: JSON - - .. code-block:: json - - { - "allow": { - "id": [ - "simon", - "cleopaws" - ], + { + "allow": { + "id": ["simon", "cleopaws"], "role": "ops" - } } -.. [[[end]]] + } `Demo for cleopaws `__, `demo for ops role `__, `demo for an actor matching neither rule `__. @@ -417,18 +183,12 @@ The /-/allow-debug tool The ``/-/allow-debug`` tool lets you try out different ``"action"`` blocks against different ``"actor"`` JSON objects. You can try that out here: https://latest.datasette.io/-/allow-debug -.. _authentication_permissions_config: +.. _authentication_permissions_metadata: -Access permissions in ``datasette.yaml`` +Configuring permissions in metadata.json ======================================== -There are two ways to configure permissions using ``datasette.yaml`` (or ``datasette.json``). - -For simple visibility permissions you can use ``"allow"`` blocks in the root, database, table and query sections. - -For other permissions you can use a ``"permissions"`` block, described :ref:`in the next section `. - -You can limit who is allowed to view different parts of your Datasette instance using ``"allow"`` keys in your :ref:`configuration`. +You can limit who is allowed to view different parts of your Datasette instance using ``"allow"`` keys in your :ref:`metadata` configuration. You can control the following: @@ -437,167 +197,76 @@ You can control the following: * Access to specific tables and views * Access to specific :ref:`canned_queries` -If a user has permission to view a table they will be able to view that table, independent of if they have permission to view the database or instance that the table exists within. +If a user cannot access a specific database, they will not be able to access tables, views or queries within that database. If a user cannot access the instance they will not be able to access any of the databases, tables, views or queries. .. _authentication_permissions_instance: -Access to an instance ---------------------- +Controlling access to an instance +--------------------------------- Here's how to restrict access to your entire Datasette instance to just the ``"id": "root"`` user: -.. [[[cog - from metadata_doc import config_example - config_example(cog, """ - title: My private Datasette instance - allow: - id: root - """) -.. ]]] +.. code-block:: json -.. tab:: datasette.yaml - - .. code-block:: yaml - - - title: My private Datasette instance - allow: - id: root - - -.. tab:: datasette.json - - .. code-block:: json - - { - "title": "My private Datasette instance", - "allow": { + { + "title": "My private Datasette instance", + "allow": { "id": "root" - } } -.. [[[end]]] + } To deny access to all users, you can use ``"allow": false``: -.. [[[cog - config_example(cog, """ - title: My entirely inaccessible instance - allow: false - """) -.. ]]] +.. code-block:: json -.. tab:: datasette.yaml - - .. code-block:: yaml - - - title: My entirely inaccessible instance - allow: false - - -.. tab:: datasette.json - - .. code-block:: json - - { - "title": "My entirely inaccessible instance", - "allow": false - } -.. [[[end]]] + { + "title": "My entirely inaccessible instance", + "allow": false + } One reason to do this is if you are using a Datasette plugin - such as `datasette-permissions-sql `__ - to control permissions instead. .. _authentication_permissions_database: -Access to specific databases ----------------------------- +Controlling access to specific databases +---------------------------------------- To limit access to a specific ``private.db`` database to just authenticated users, use the ``"allow"`` block like this: -.. [[[cog - config_example(cog, """ - databases: - private: - allow: - id: "*" - """) -.. ]]] +.. code-block:: json -.. tab:: datasette.yaml - - .. code-block:: yaml - - - databases: - private: - allow: - id: "*" - - -.. tab:: datasette.json - - .. code-block:: json - - { - "databases": { + { + "databases": { "private": { - "allow": { - "id": "*" - } + "allow": { + "id": "*" + } } - } } -.. [[[end]]] + } .. _authentication_permissions_table: -Access to specific tables and views ------------------------------------ +Controlling access to specific tables and views +----------------------------------------------- To limit access to the ``users`` table in your ``bakery.db`` database: -.. [[[cog - config_example(cog, """ - databases: - bakery: - tables: - users: - allow: - id: '*' - """) -.. ]]] +.. code-block:: json -.. tab:: datasette.yaml - - .. code-block:: yaml - - - databases: - bakery: - tables: - users: - allow: - id: '*' - - -.. tab:: datasette.json - - .. code-block:: json - - { - "databases": { + { + "databases": { "bakery": { - "tables": { - "users": { - "allow": { - "id": "*" - } + "tables": { + "users": { + "allow": { + "id": "*" + } + } } - } } - } } -.. [[[end]]] + } This works for SQL views as well - you can list their names in the ``"tables"`` block above in the same way as regular tables. @@ -608,65 +277,30 @@ This works for SQL views as well - you can list their names in the ``"tables"`` .. _authentication_permissions_query: -Access to specific canned queries ---------------------------------- +Controlling access to specific canned queries +--------------------------------------------- -:ref:`canned_queries` allow you to configure named SQL queries in your ``datasette.yaml`` that can be executed by users. These queries can be set up to both read and write to the database, so controlling who can execute them can be important. +:ref:`canned_queries` allow you to configure named SQL queries in your ``metadata.json`` that can be executed by users. These queries can be set up to both read and write to the database, so controlling who can execute them can be important. To limit access to the ``add_name`` canned query in your ``dogs.db`` database to just the :ref:`root user`: -.. [[[cog - config_example(cog, """ - databases: - dogs: - queries: - add_name: - sql: INSERT INTO names (name) VALUES (:name) - write: true - allow: - id: - - root - """) -.. ]]] +.. code-block:: json -.. tab:: datasette.yaml - - .. code-block:: yaml - - - databases: - dogs: - queries: - add_name: - sql: INSERT INTO names (name) VALUES (:name) - write: true - allow: - id: - - root - - -.. tab:: datasette.json - - .. code-block:: json - - { - "databases": { + { + "databases": { "dogs": { - "queries": { - "add_name": { - "sql": "INSERT INTO names (name) VALUES (:name)", - "write": true, - "allow": { - "id": [ - "root" - ] - } + "queries": { + "add_name": { + "sql": "INSERT INTO names (name) VALUES (:name)", + "write": true, + "allow": { + "id": ["root"] + } + } } - } } - } } -.. [[[end]]] + } .. _authentication_permissions_execute_sql: @@ -675,7 +309,7 @@ Controlling the ability to execute arbitrary SQL Datasette defaults to allowing any site visitor to execute their own custom SQL queries, for example using the form on `the database page `__ or by appending a ``?_where=`` parameter to the table page `like this `__. -Access to this ability is controlled by the :ref:`actions_execute_sql` permission. +Access to this ability is controlled by the :ref:`permissions_execute_sql` permission. The easiest way to disable arbitrary SQL queries is using the :ref:`default_allow_sql setting ` when you first start Datasette running. @@ -683,387 +317,44 @@ You can alternatively use an ``"allow_sql"`` block to control who is allowed to To prevent any user from executing arbitrary SQL queries, use this: -.. [[[cog - config_example(cog, """ - allow_sql: false - """) -.. ]]] +.. code-block:: json -.. tab:: datasette.yaml - - .. code-block:: yaml - - - allow_sql: false - - -.. tab:: datasette.json - - .. code-block:: json - - { - "allow_sql": false - } -.. [[[end]]] + { + "allow_sql": false + } To enable just the :ref:`root user` to execute SQL for all databases in your instance, use the following: -.. [[[cog - config_example(cog, """ - allow_sql: - id: root - """) -.. ]]] - -.. tab:: datasette.yaml - - .. code-block:: yaml - - - allow_sql: - id: root - - -.. tab:: datasette.json - - .. code-block:: json - - { - "allow_sql": { - "id": "root" - } - } -.. [[[end]]] - -To limit this ability for just one specific database, use this: - -.. [[[cog - config_example(cog, """ - databases: - mydatabase: - allow_sql: - id: root - """) -.. ]]] - -.. tab:: datasette.yaml - - .. code-block:: yaml - - - databases: - mydatabase: - allow_sql: - id: root - - -.. tab:: datasette.json - - .. code-block:: json - - { - "databases": { - "mydatabase": { - "allow_sql": { - "id": "root" - } - } - } - } -.. [[[end]]] - -.. _authentication_permissions_other: - -Other permissions in ``datasette.yaml`` -======================================= - -For all other permissions, you can use one or more ``"permissions"`` blocks in your ``datasette.yaml`` configuration file. - -To grant access to the :ref:`permissions debug tool ` to all signed in users, you can grant ``permissions-debug`` to any actor with an ``id`` matching the wildcard ``*`` by adding this a the root of your configuration: - -.. [[[cog - config_example(cog, """ - permissions: - debug-menu: - id: '*' - """) -.. ]]] - -.. tab:: datasette.yaml - - .. code-block:: yaml - - - permissions: - debug-menu: - id: '*' - - -.. tab:: datasette.json - - .. code-block:: json - - { - "permissions": { - "debug-menu": { - "id": "*" - } - } - } -.. [[[end]]] - -To grant ``create-table`` to the user with ``id`` of ``editor`` for the ``docs`` database: - -.. [[[cog - config_example(cog, """ - databases: - docs: - permissions: - create-table: - id: editor - """) -.. ]]] - -.. tab:: datasette.yaml - - .. code-block:: yaml - - - databases: - docs: - permissions: - create-table: - id: editor - - -.. tab:: datasette.json - - .. code-block:: json - - { - "databases": { - "docs": { - "permissions": { - "create-table": { - "id": "editor" - } - } - } - } - } -.. [[[end]]] - -And for ``insert-row`` against the ``reports`` table in that ``docs`` database: - -.. [[[cog - config_example(cog, """ - databases: - docs: - tables: - reports: - permissions: - insert-row: - id: editor - """) -.. ]]] - -.. tab:: datasette.yaml - - .. code-block:: yaml - - - databases: - docs: - tables: - reports: - permissions: - insert-row: - id: editor - - -.. tab:: datasette.json - - .. code-block:: json - - { - "databases": { - "docs": { - "tables": { - "reports": { - "permissions": { - "insert-row": { - "id": "editor" - } - } - } - } - } - } - } -.. [[[end]]] - -The :ref:`permissions debug tool ` can be useful for helping test permissions that you have configured in this way. - -.. _CreateTokenView: - -API Tokens -========== - -Datasette includes a default mechanism for generating API tokens that can be used to authenticate requests. - -Authenticated users can create new API tokens using a form on the ``/-/create-token`` page. - -Tokens created in this way can be further restricted to only allow access to specific actions, or to limit those actions to specific databases, tables or queries. - -Created tokens can then be passed in the ``Authorization: Bearer $token`` header of HTTP requests to Datasette. - -A token created by a user will include that user's ``"id"`` in the token payload, so any permissions granted to that user based on their ID can be made available to the token as well. - -When one of these a token accompanies a request, the actor for that request will have the following shape: - .. code-block:: json { - "id": "user_id", - "token": "dstok", - "token_expires": 1667717426 + "allow_sql": { + "id": "root" + } } -The ``"id"`` field duplicates the ID of the actor who first created the token. +To limit this ability for just one specific database, use this: -The ``"token"`` field identifies that this actor was authenticated using a Datasette signed token (``dstok``). - -The ``"token_expires"`` field, if present, indicates that the token will expire after that integer timestamp. - -The ``/-/create-token`` page cannot be accessed by actors that are authenticated with a ``"token": "some-value"`` property. This is to prevent API tokens from being used to create more tokens. - -Datasette plugins that implement their own form of API token authentication should follow this convention. - -You can disable the signed token feature entirely using the :ref:`allow_signed_tokens ` setting. - -.. _authentication_cli_create_token: - -datasette create-token ----------------------- - -You can also create tokens on the command line using the ``datasette create-token`` command. - -This command takes one required argument - the ID of the actor to be associated with the created token. - -You can specify a ``-e/--expires-after`` option in seconds. If omitted, the token will never expire. - -The command will sign the token using the ``DATASETTE_SECRET`` environment variable, if available. You can also pass the secret using the ``--secret`` option. - -This means you can run the command locally to create tokens for use with a deployed Datasette instance, provided you know that instance's secret. - -To create a token for the ``root`` actor that will expire in one hour:: - - datasette create-token root --expires-after 3600 - -To create a token that never expires using a specific secret:: - - datasette create-token root --secret my-secret-goes-here - -.. _authentication_cli_create_token_restrict: - -Restricting the actions that a token can perform -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Tokens created using ``datasette create-token ACTOR_ID`` will inherit all of the permissions of the actor that they are associated with. - -You can pass additional options to create tokens that are restricted to a subset of that actor's permissions. - -To restrict the token to just specific permissions against all available databases, use the ``--all`` option:: - - datasette create-token root --all insert-row --all update-row - -This option can be passed as many times as you like. In the above example the token will only be allowed to insert and update rows. - -You can also restrict permissions such that they can only be used within specific databases:: - - datasette create-token root --database mydatabase insert-row - -The resulting token will only be able to insert rows, and only to tables in the ``mydatabase`` database. - -Finally, you can restrict permissions to individual resources - tables, SQL views and :ref:`named queries ` - within a specific database:: - - datasette create-token root --resource mydatabase mytable insert-row - -These options have short versions: ``-a`` for ``--all``, ``-d`` for ``--database`` and ``-r`` for ``--resource``. - -You can add ``--debug`` to see a JSON representation of the token that has been created. Here's a full example:: - - datasette create-token root \ - --secret mysecret \ - --all view-instance \ - --all view-table \ - --database docs view-query \ - --resource docs documents insert-row \ - --resource docs documents update-row \ - --debug - -This example outputs the following:: - - dstok_.eJxFizEKgDAMRe_y5w4qYrFXERGxDkVsMI0uxbubdjFL8l_ez1jhwEQCA6Fjjxp90qtkuHawzdjYrh8MFobLxZ_wBH0_gtnAF-hpS5VfmF8D_lnd97lHqUJgLd6sls4H1qwlhA.nH_7RecYHj5qSzvjhMU95iy0Xlc - - Decoded: +.. code-block:: json { - "a": "root", - "token": "dstok", - "t": 1670907246, - "_r": { - "a": [ - "vi", - "vt" - ], - "d": { - "docs": [ - "vq" - ] - }, - "r": { - "docs": { - "documents": [ - "ir", - "ur" - ] - } + "databases": { + "mydatabase": { + "allow_sql": { + "id": "root" + } + } } - } } -Restrictions act as an allowlist layered on top of the actor's existing -permissions. They can only remove access the actor would otherwise have—they -cannot grant new access. If the underlying actor is denied by ``allow`` rules in -``datasette.yaml`` or by a plugin, a token that lists that resource in its -``"_r"`` section will still be denied. - - .. _permissions_plugins: Checking permissions in plugins =============================== -Datasette plugins can check if an actor has permission to perform an action using :ref:`datasette_allowed`—for example:: +Datasette plugins can check if an actor has permission to perform an action using the :ref:`datasette.permission_allowed(...)` method. - from datasette.resources import TableResource - - can_edit = await datasette.allowed( - action="update-row", - resource=TableResource(database="fixtures", table="facetable"), - actor=request.actor, - ) - -Use :ref:`datasette_ensure_permission` when you need to enforce a permission and -raise a ``Forbidden`` error automatically. - -Plugins that define new operations should return :class:`~datasette.permissions.Action` -objects from :ref:`plugin_register_actions` and can supply additional allow/deny -rules by returning :class:`~datasette.permissions.PermissionSQL` objects from the -:ref:`plugin_hook_permission_resources_sql` hook. Those rules are merged with -configuration ``allow`` blocks and actor restrictions to determine the final -result for each check. +Datasette core performs a number of permission checks, :ref:`documented below `. Plugins can implement the :ref:`plugin_hook_permission_allowed` plugin hook to participate in decisions about whether an actor should be able to perform a specified action. .. _authentication_actor_matches_allow: @@ -1083,56 +374,15 @@ The currently authenticated actor is made available to plugins as ``request.acto .. _PermissionsDebugView: -Permissions debug tools -======================= +The permissions debug tool +========================== -The debug tool at ``/-/permissions`` is available to any actor with the ``permissions-debug`` permission. By default this is just the :ref:`authenticated root user ` but you can open it up to all users by starting Datasette like this:: +The debug tool at ``/-/permissions`` is only available to the :ref:`authenticated root user ` (or any actor granted the ``permissions-debug`` action according to a plugin). - datasette -s permissions.permissions-debug true data.db - -The page shows the permission checks that have been carried out by the Datasette instance. - -It also provides an interface for running hypothetical permission checks against a hypothetical actor. This is a useful way of confirming that your configured permissions work in the way you expect. +It shows the thirty most recent permission checks that have been carried out by the Datasette instance. This is designed to help administrators and plugin authors understand exactly how permission checks are being carried out, in order to effectively configure Datasette's permission system. -.. _AllowedResourcesView: - -Allowed resources view ----------------------- - -The ``/-/allowed`` endpoint displays resources that the current actor can access for a specified ``action``. - -This endpoint provides an interactive HTML form interface. Add ``.json`` to the URL path (e.g. ``/-/allowed.json``) to get the raw JSON response instead. - -Pass ``?action=view-table`` (or another action) to select the action. Optional ``parent=`` and ``child=`` query parameters can narrow the results to a specific database/table pair. - -This endpoint is publicly accessible to help users understand their own permissions. The potentially sensitive ``reason`` field is only shown to users with the ``permissions-debug`` permission - it shows the plugins and explanatory reasons that were responsible for each decision. - -.. _PermissionRulesView: - -Permission rules view ---------------------- - -The ``/-/rules`` endpoint displays all permission rules (both allow and deny) for each candidate resource for the requested action. - -This endpoint provides an interactive HTML form interface. Add ``.json`` to the URL path (e.g. ``/-/rules.json?action=view-table``) to get the raw JSON response instead. - -Pass ``?action=`` as a query parameter to specify which action to check. - -This endpoint requires the ``permissions-debug`` permission. - -.. _PermissionCheckView: - -Permission check view ---------------------- - -The ``/-/check`` endpoint evaluates a single action/resource pair and returns information indicating whether the access was allowed along with diagnostic information. - -This endpoint provides an interactive HTML form interface. Add ``.json`` to the URL path (e.g. ``/-/check.json?action=view-instance``) to get the raw JSON response instead. - -Pass ``?action=`` to specify the action to check, and optional ``?parent=`` and ``?child=`` parameters to specify the resource. - .. _authentication_ds_actor: The ds_actor cookie @@ -1145,25 +395,19 @@ Authentication plugins can set signed ``ds_actor`` cookies themselves like so: .. code-block:: python response = Response.redirect("/") - datasette.set_actor_cookie(response, {"id": "cleopaws"}) + response.set_cookie( + "ds_actor", + datasette.sign({"a": {"id": "cleopaws"}}, "actor"), + ) -The shape of data encoded in the cookie is as follows: +Note that you need to pass ``"actor"`` as the namespace to :ref:`datasette_sign`. -.. code-block:: json +The shape of data encoded in the cookie is as follows:: { - "a": { - "id": "cleopaws" - } + "a": {... actor ...} } -To implement logout in a plugin, use the ``delete_actor_cookie()`` method: - -.. code-block:: python - - response = Response.redirect("/") - datasette.delete_actor_cookie(response) - .. _authentication_ds_actor_expiry: Including an expiry time @@ -1171,13 +415,25 @@ Including an expiry time ``ds_actor`` cookies can optionally include a signed expiry timestamp, after which the cookies will no longer be valid. Authentication plugins may chose to use this mechanism to limit the lifetime of the cookie. For example, if a plugin implements single-sign-on against another source it may decide to set short-lived cookies so that if the user is removed from the SSO system their existing Datasette cookies will stop working shortly afterwards. -To include an expiry pass ``expire_after=`` to ``datasette.set_actor_cookie()`` with a number of seconds. For example, to expire in 24 hours: +To include an expiry, add a ``"e"`` key to the cookie value containing a base62-encoded integer representing the timestamp when the cookie should expire. For example, here's how to set a cookie that expires after 24 hours: .. code-block:: python + import time + from datasette.utils import baseconv + + expires_at = int(time.time()) + (24 * 60 * 60) + response = Response.redirect("/") - datasette.set_actor_cookie( - response, {"id": "cleopaws"}, expire_after=60 * 60 * 24 + response.set_cookie( + "ds_actor", + datasette.sign( + { + "a": {"id": "cleopaws"}, + "e": baseconv.base62.encode(expires_at), + }, + "actor", + ), ) The resulting cookie will encode data that looks something like this: @@ -1185,12 +441,13 @@ The resulting cookie will encode data that looks something like this: .. code-block:: json { - "a": { - "id": "cleopaws" - }, - "e": "1jjSji" + "a": { + "id": "cleopaws" + }, + "e": "1jjSji" } + .. _LogoutView: The /-/logout page @@ -1198,156 +455,96 @@ The /-/logout page The page at ``/-/logout`` provides the ability to log out of a ``ds_actor`` cookie authentication session. -.. _actions: +.. _permissions: -Built-in actions -================ +Built-in permissions +==================== This section lists all of the permission checks that are carried out by Datasette core, along with the ``resource`` if it was passed. -.. _actions_view_instance: +.. _permissions_view_instance: view-instance ------------- Top level permission - Actor is allowed to view any pages within this instance, starting at https://latest.datasette.io/ -.. _actions_view_database: +Default *allow*. + +.. _permissions_view_database: view-database ------------- Actor is allowed to view a database page, e.g. https://latest.datasette.io/fixtures -``resource`` - ``datasette.permissions.DatabaseResource(database)`` - ``database`` is the name of the database (string) +``resource`` - string + The name of the database -.. _actions_view_database_download: +Default *allow*. + +.. _permissions_view_database_download: view-database-download ----------------------- +----------------------- Actor is allowed to download a database, e.g. https://latest.datasette.io/fixtures.db -``resource`` - ``datasette.resources.DatabaseResource(database)`` - ``database`` is the name of the database (string) +``resource`` - string + The name of the database -.. _actions_view_table: +Default *allow*. + +.. _permissions_view_table: view-table ---------- Actor is allowed to view a table (or view) page, e.g. https://latest.datasette.io/fixtures/complex_foreign_keys -``resource`` - ``datasette.resources.TableResource(database, table)`` - ``database`` is the name of the database (string) +``resource`` - tuple: (string, string) + The name of the database, then the name of the table - ``table`` is the name of the table (string) +Default *allow*. -.. _actions_view_query: +.. _permissions_view_query: view-query ---------- Actor is allowed to view (and execute) a :ref:`canned query ` page, e.g. https://latest.datasette.io/fixtures/pragma_cache_size - this includes executing :ref:`canned_queries_writable`. -``resource`` - ``datasette.resources.QueryResource(database, query)`` - ``database`` is the name of the database (string) - - ``query`` is the name of the canned query (string) +``resource`` - tuple: (string, string) + The name of the database, then the name of the canned query -.. _actions_insert_row: +Default *allow*. -insert-row ----------- - -Actor is allowed to insert rows into a table. - -``resource`` - ``datasette.resources.TableResource(database, table)`` - ``database`` is the name of the database (string) - - ``table`` is the name of the table (string) - -.. _actions_delete_row: - -delete-row ----------- - -Actor is allowed to delete rows from a table. - -``resource`` - ``datasette.resources.TableResource(database, table)`` - ``database`` is the name of the database (string) - - ``table`` is the name of the table (string) - -.. _actions_update_row: - -update-row ----------- - -Actor is allowed to update rows in a table. - -``resource`` - ``datasette.resources.TableResource(database, table)`` - ``database`` is the name of the database (string) - - ``table`` is the name of the table (string) - -.. _actions_create_table: - -create-table ------------- - -Actor is allowed to create a database table. - -``resource`` - ``datasette.resources.DatabaseResource(database)`` - ``database`` is the name of the database (string) - -.. _actions_alter_table: - -alter-table ------------ - -Actor is allowed to alter a database table. - -``resource`` - ``datasette.resources.TableResource(database, table)`` - ``database`` is the name of the database (string) - - ``table`` is the name of the table (string) - -.. _actions_drop_table: - -drop-table ----------- - -Actor is allowed to drop a database table. - -``resource`` - ``datasette.resources.TableResource(database, table)`` - ``database`` is the name of the database (string) - - ``table`` is the name of the table (string) - -.. _actions_execute_sql: +.. _permissions_execute_sql: execute-sql ----------- -Actor is allowed to run arbitrary SQL queries against a specific database, e.g. https://latest.datasette.io/fixtures/-/query?sql=select+100 +Actor is allowed to run arbitrary SQL queries against a specific database, e.g. https://latest.datasette.io/fixtures?sql=select+100 -``resource`` - ``datasette.resources.DatabaseResource(database)`` - ``database`` is the name of the database (string) +``resource`` - string + The name of the database -See also :ref:`the default_allow_sql setting `. +Default *allow*. See also :ref:`the default_allow_sql setting `. -.. _actions_permissions_debug: +.. _permissions_permissions_debug: permissions-debug ----------------- -Actor is allowed to view the ``/-/permissions`` debug tools. +Actor is allowed to view the ``/-/permissions`` debug page. -.. _actions_debug_menu: +Default *deny*. + +.. _permissions_debug_menu: debug-menu ---------- Controls if the various debug pages are displayed in the navigation menu. + +Default *deny*. diff --git a/docs/changelog.rst b/docs/changelog.rst index dbcff8cb..f0485b4f 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog ========= + .. _v0_65_2: 0.65.2 (2025-11-05) @@ -14,102 +15,9 @@ Changelog * Fixed ``datasette publish cloudrun`` to work with changes to the underlying Cloud Run architecture. (:issue:`2511`) * Minor upgrades to fix warnings, including ``pkg_resources`` deprecation. -.. _v1_0_a20: - -1.0a20 (2025-11-03) -------------------- - -This alpha introduces a major breaking change prior to the 1.0 release of Datasette concerning how Datasette's permission system works. - -Permission system redesign -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Previously the permission system worked using ``datasette.permission_allowed()`` checks which consulted all available plugins in turn to determine whether a given actor was allowed to perform a given action on a given resource. - -This approach could become prohibitively expensive for large lists of items - for example to determine the list of tables that a user could view in a large Datasette instance each plugin implementation of that hook would be fired for every table. - -The new design uses SQL queries against Datasette's internal :ref:`catalog tables ` to derive the list of resources for which an actor has permission for a given action. This turns an N x M problem (N resources, M plugins) into a single SQL query. - -Plugins can use the new :ref:`plugin_hook_permission_resources_sql` hook to return SQL fragments which will be used as part of that query. - -Plugins that use any of the following features will need to be updated to work with this and following alphas (and Datasette 1.0 stable itself): - -- Checking permissions with ``datasette.permission_allowed()`` - this method has been replaced with :ref:`datasette.allowed() `. -- Implementing the ``permission_allowed()`` plugin hook - this hook has been removed in favor of :ref:`permission_resources_sql() `. -- Using ``register_permissions()`` to register permissions - this hook has been removed in favor of :ref:`register_actions() `. - -Consult the :ref:`v1.0a20 upgrade guide ` for further details on how to upgrade affected plugins. - -Plugins can now make use of two new internal methods to help resolve permission checks: - -- :ref:`datasette.allowed_resources() ` returns a ``PaginatedResources`` object with a ``.resources`` list of ``Resource`` instances that an actor is allowed to access for a given action (and a ``.next`` token for pagination). -- :ref:`datasette.allowed_resources_sql() ` returns the SQL and parameters that can be executed against the internal catalog tables to determine which resources an actor is allowed to access for a given action. This can be combined with further SQL to perform advanced custom filtering. - -Related changes: - -- The way ``datasette --root`` works has changed. Running Datasette with this flag now causes the root actor to pass *all* permission checks. (:issue:`2521`) - -- Permission debugging improvements: - - - The ``/-/allowed`` endpoint shows resources the user is allowed to interact with for different actions. - - - ``/-/rules`` shows the raw allow/deny rules that apply to different permission checks. - - - ``/-/actions`` lists every available action. - - - ``/-/check`` can be used to try out different permission checks for the current actor. - -Other changes -~~~~~~~~~~~~~ - -- The internal ``catalog_views`` table now tracks SQLite views alongside tables in the introspection database. (:issue:`2495`) - -- Hitting the ``/`` brings up a search interface for navigating to tables that the current user can view. A new ``/-/tables`` endpoint supports this functionality. (:issue:`2523`) - -- Datasette attempts to detect some configuration errors on startup. - -- Datasette now supports Python 3.14 and no longer tests against Python 3.9. - -.. _v1_0_a19: - -1.0a19 (2025-04-21) -------------------- - -- Tiny cosmetic bug fix for mobile display of table rows. (:issue:`2479`) - -.. _v1_0_a18: - -1.0a18 (2025-04-16) -------------------- - -- Fix for incorrect foreign key references in the internal database schema. (:issue:`2466`) -- The ``prepare_connection()`` hook no longer runs for the internal database. (:issue:`2468`) -- Fixed bug where ``link:`` HTTP headers used invalid syntax. (:issue:`2470`) -- No longer tested against Python 3.8. Now tests against Python 3.13. -- FTS tables are now hidden by default if they correspond to a content table. (:issue:`2477`) -- Fixed bug with foreign key links to rows in databases with filenames containing a special character. Thanks, `Jack Stratton `__. (`#2476 `__) - -.. _v1_0_a17: - -1.0a17 (2025-02-06) -------------------- - -- ``DATASETTE_SSL_KEYFILE`` and ``DATASETTE_SSL_CERTFILE`` environment variables as alternatives to ``--ssl-keyfile`` and ``--ssl-certfile``. Thanks, Alex Garcia. (:issue:`2422`) -- ``SQLITE_EXTENSIONS`` environment variable has been renamed to ``DATASETTE_LOAD_EXTENSION``. (:issue:`2424`) -- ``datasette serve`` environment variables are now :ref:`documented here `. -- The :ref:`plugin_hook_register_magic_parameters` plugin hook can now register async functions. (:issue:`2441`) -- Datasette is now tested against Python 3.13. -- Breadcrumbs on database and table pages now include a consistent self-link for resetting query string parameters. (:issue:`2454`) -- Fixed issue where Datasette could crash on ``metadata.json`` with nested values. (:issue:`2455`) -- New internal methods ``datasette.set_actor_cookie()`` and ``datasette.delete_actor_cookie()``, :ref:`described here `. (:issue:`1690`) -- ``/-/permissions`` page now shows a list of all permissions registered by plugins. (:issue:`1943`) -- If a table has a single unique text column Datasette now detects that as the foreign key label for that table. (:issue:`2458`) -- The ``/-/permissions`` page now includes options for filtering or exclude permission checks recorded against the current user. (:issue:`2460`) -- Fixed a bug where replacing a database with a new one with the same name did not pick up the new database correctly. (:issue:`2465`) - .. _v0_65_1: -0.65.1 (2024-11-28) +0.65.1 (2024-12-28) ------------------- - Fixed bug with upgraded HTTPX 0.28.0 dependency. (:issue:`2443`) @@ -122,66 +30,6 @@ Other changes - Upgrade for compatibility with Python 3.13 (by vendoring Pint dependency). (:issue:`2434`) - Dropped support for Python 3.8. -.. _v1_0_a16: - -1.0a16 (2024-09-05) -------------------- - -This release focuses on performance, in particular against large tables, and introduces some minor breaking changes for CSS styling in Datasette plugins. - -- Removed the unit conversions feature and its dependency, Pint. This means Datasette is now compatible with the upcoming Python 3.13. (:issue:`2400`, :issue:`2320`) -- The ``datasette --pdb`` option now uses the `ipdb `__ debugger if it is installed. You can install it using ``datasette install ipdb``. Thanks, `Tiago Ilieve `__. (`#2342 `__) -- Fixed a confusing error that occurred if ``metadata.json`` contained nested objects. (:issue:`2403`) -- Fixed a bug with ``?_trace=1`` where it returned a blank page if the response was larger than 256KB. (:issue:`2404`) -- Tracing mechanism now also displays SQL queries that returned errors or ran out of time. `datasette-pretty-traces 0.5 `__ includes support for displaying this new type of trace. (:issue:`2405`) -- Fixed a text spacing with table descriptions on the homepage. (:issue:`2399`) -- Performance improvements for large tables: - - Suggested facets now only consider the first 1000 rows. (:issue:`2406`) - - Improved performance of date facet suggestion against large tables. (:issue:`2407`) - - Row counts stop at 10,000 rows when listing tables. (:issue:`2398`) - - On table page the count stops at 10,000 rows too, with a "count all" button to execute the full count. (:issue:`2408`) -- New ``.dicts()`` internal method on :ref:`database_results` that returns a list of dictionaries representing the results from a SQL query: (:issue:`2414`) - - .. code-block:: bash - - rows = (await db.execute("select * from t")).dicts() - -- Default Datasette core CSS that styles inputs and buttons now requires a class of ``"core"`` on the element or a containing element, for example ``
``. (:issue:`2415`) -- Similarly, default table styles now only apply to ````. (:issue:`2420`) - -.. _v1_0_a15: - -1.0a15 (2024-08-15) -------------------- - -- Datasette now defaults to hiding SQLite "shadow" tables, as seen in extensions such as SQLite FTS and `sqlite-vec `__. Virtual tables that it makes sense to display, such as FTS core tables, are no longer hidden. Thanks, `Alex Garcia `__. (:issue:`2296`) -- Fixed bug where running Datasette with one or more ``-s/--setting`` options could over-ride settings that were present in ``datasette.yml``. (:issue:`2389`) -- The Datasette homepage is now duplicated at ``/-/``, using the default ``index.html`` template. This ensures that the information on that page is still accessible even if the Datasette homepage has been customized using a custom ``index.html`` template, for example on sites like `datasette.io `__. (:issue:`2393`) -- Failed CSRF checks now display a more user-friendly error page. (:issue:`2390`) -- Fixed a bug where the ``json1`` extension was not correctly detected on the ``/-/versions`` page. Thanks, `Seb Bacon `__. (:issue:`2326`) -- Fixed a bug where the Datasette write API did not correctly accept ``Content-Type: application/json; charset=utf-8``. (:issue:`2384`) -- Fixed a bug where Datasette would fail to start if ``metadata.yml`` contained a ``queries`` block. (`#2386 `__) - -.. _v1_0_a14: - -1.0a14 (2024-08-05) -------------------- - -This alpha introduces significant changes to Datasette's :ref:`metadata` system, some of which represent breaking changes in advance of the full 1.0 release. The new :ref:`upgrade_guide` document provides detailed coverage of those breaking changes and how they affect plugin authors and Datasette API consumers. - -- The ``/databasename?sql=`` interface and JSON API for executing arbitrary SQL queries can now be found at ``/databasename/-/query?sql=``. Requests with a ``?sql=`` parameter to the old endpoints will be redirected. Thanks, `Alex Garcia `__. (:issue:`2360`) -- Metadata about tables, databases, instances and columns is now stored in :ref:`internals_internal`. Thanks, Alex Garcia. (:issue:`2341`) -- Database write connections now execute using the ``IMMEDIATE`` isolation level for SQLite. This should help avoid a rare ``SQLITE_BUSY`` error that could occur when a transaction upgraded to a write mid-flight. (:issue:`2358`) -- Fix for a bug where canned queries with named parameters could fail against SQLite 3.46. (:issue:`2353`) -- Datasette now serves ``E-Tag`` headers for static files. Thanks, `Agustin Bacigalup `__. (`#2306 `__) -- Dropdown menus now use a ``z-index`` that should avoid them being hidden by plugins. (:issue:`2311`) -- Incorrect table and row names are no longer reflected back on the resulting 404 page. (:issue:`2359`) -- Improved documentation for async usage of the :ref:`plugin_hook_track_event` hook. (:issue:`2319`) -- Fixed some HTTPX deprecation warnings. (:issue:`2307`) -- Datasette now serves a ```` attribute. Thanks, `Charles Nepote `__. (:issue:`2348`) -- Datasette's automated tests now run against the maximum and minimum supported versions of SQLite: 3.25 (from September 2018) and 3.46 (from May 2024). Thanks, Alex Garcia. (`#2352 `__) -- Fixed an issue where clicking twice on the URL output by ``datasette --root`` produced a confusing error. (:issue:`2375`) - .. _v0_64_8: 0.64.8 (2024-06-21) @@ -197,180 +45,6 @@ This alpha introduces significant changes to Datasette's :ref:`metadata` system, - Fixed a bug where canned queries with named parameters threw an error when run against SQLite 3.46.0. (:issue:`2353`) -.. _v1_0_a13: - -1.0a13 (2024-03-12) -------------------- - -Each of the key concepts in Datasette now has an :ref:`actions menu `, which plugins can use to add additional functionality targeting that entity. - -- Plugin hook: :ref:`view_actions() ` for actions that can be applied to a SQL view. (:issue:`2297`) -- Plugin hook: :ref:`homepage_actions() ` for actions that apply to the instance homepage. (:issue:`2298`) -- Plugin hook: :ref:`row_actions() ` for actions that apply to the row page. (:issue:`2299`) -- Action menu items for all of the ``*_actions()`` plugin hooks can now return an optional ``"description"`` key, which will be displayed in the menu below the action label. (:issue:`2294`) -- :ref:`Plugin hooks ` documentation page is now organized with additional headings. (:issue:`2300`) -- Improved the display of action buttons on pages that also display metadata. (:issue:`2286`) -- The header and footer of the page now uses a subtle gradient effect, and options in the navigation menu are better visually defined. (:issue:`2302`) -- Table names that start with an underscore now default to hidden. (:issue:`2104`) -- ``pragma_table_list`` has been added to the allow-list of SQLite pragma functions supported by Datasette. ``select * from pragma_table_list()`` is no longer blocked. (`#2104 `__) - -.. _v1_0_a12: - -1.0a12 (2024-02-29) -------------------- - -- New :ref:`query_actions() ` plugin hook, similar to :ref:`table_actions() ` and :ref:`database_actions() `. Can be used to add a menu of actions to the canned query or arbitrary SQL query page. (:issue:`2283`) -- New design for the button that opens the query, table and database actions menu. (:issue:`2281`) -- "does not contain" table filter for finding rows that do not contain a string. (:issue:`2287`) -- Fixed a bug in the :ref:`javascript_plugins_makeColumnActions` JavaScript plugin mechanism where the column action menu was not fully reset in between each interaction. (:issue:`2289`) - -.. _v1_0_a11: - -1.0a11 (2024-02-19) -------------------- - -- The ``"replace": true`` argument to the ``/db/table/-/insert`` API now requires the actor to have the ``update-row`` permission. (:issue:`2279`) -- Fixed some UI bugs in the interactive permissions debugging tool. (:issue:`2278`) -- The column action menu now aligns better with the cog icon, and positions itself taking into account the width of the browser window. (:issue:`2263`) - -.. _v1_0_a10: - -1.0a10 (2024-02-17) -------------------- - -The only changes in this alpha correspond to the way Datasette handles database transactions. (:issue:`2277`) - -- The :ref:`database.execute_write_fn() ` method has a new ``transaction=True`` parameter. This defaults to ``True`` which means all functions executed using this method are now automatically wrapped in a transaction - previously the functions needed to roll transaction handling on their own, and many did not. -- Pass ``transaction=False`` to ``execute_write_fn()`` if you want to manually handle transactions in your function. -- Several internal Datasette features, including parts of the :ref:`JSON write API `, had been failing to wrap their operations in a transaction. This has been fixed by the new ``transaction=True`` default. - -.. _v1_0_a9: - -1.0a9 (2024-02-16) ------------------- - -This alpha release adds basic alter table support to the Datasette Write API and fixes a permissions bug relating to the ``/upsert`` API endpoint. - -Alter table support for create, insert, upsert and update -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The :ref:`JSON write API ` can now be used to apply simple alter table schema changes, provided the acting actor has the new :ref:`actions_alter_table` permission. (:issue:`2101`) - -The only alter operation supported so far is adding new columns to an existing table. - -* The :ref:`/db/-/create ` API now adds new columns during large operations to create a table based on incoming example ``"rows"``, in the case where one of the later rows includes columns that were not present in the earlier batches. This requires the ``create-table`` but not the ``alter-table`` permission. -* When ``/db/-/create`` is called with rows in a situation where the table may have been already created, an ``"alter": true`` key can be included to indicate that any missing columns from the new rows should be added to the table. This requires the ``alter-table`` permission. -* :ref:`/db/table/-/insert ` and :ref:`/db/table/-/upsert ` and :ref:`/db/table/row-pks/-/update ` all now also accept ``"alter": true``, depending on the ``alter-table`` permission. - -Operations that alter a table now fire the new :ref:`alter-table event `. - -Permissions fix for the upsert API -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The :ref:`/database/table/-/upsert API ` had a minor permissions bug, only affecting Datasette instances that had configured the ``insert-row`` and ``update-row`` permissions to apply to a specific table rather than the database or instance as a whole. Full details in issue :issue:`2262`. - -To avoid similar mistakes in the future the ``datasette.permission_allowed()`` method now specifies ``default=`` as a keyword-only argument. - -Permission checks now consider opinions from every plugin -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``datasette.permission_allowed()`` method previously consulted every plugin that implemented the :ref:`permission_allowed() ` plugin hook and obeyed the opinion of the last plugin to return a value. (:issue:`2275`) - -Datasette now consults every plugin and checks to see if any of them returned ``False`` (the veto rule), and if none of them did, it then checks to see if any of them returned ``True``. - -This is explained at length in the new documentation covering :ref:`authentication_permissions_explained`. - -Other changes -~~~~~~~~~~~~~ - -- The new :ref:`DATASETTE_TRACE_PLUGINS=1 environment variable ` turns on detailed trace output for every executed plugin hook, useful for debugging and understanding how the plugin system works at a low level. (:issue:`2274`) -- Datasette on Python 3.9 or above marks its non-cryptographic uses of the MD5 hash function as ``usedforsecurity=False``, for compatibility with FIPS systems. (:issue:`2270`) -- SQL relating to :ref:`internals_internal` now executes inside a transaction, avoiding a potential database locked error. (:issue:`2273`) -- The ``/-/threads`` debug page now identifies the database in the name associated with each dedicated write thread. (:issue:`2265`) -- The ``/db/-/create`` API now fires a ``insert-rows`` event if rows were inserted after the table was created. (:issue:`2260`) - -.. _v1_0_a8: - -1.0a8 (2024-02-07) ------------------- - -This alpha release continues the migration of Datasette's configuration from ``metadata.yaml`` to the new ``datasette.yaml`` configuration file, introduces a new system for JavaScript plugins and adds several new plugin hooks. - -See `Datasette 1.0a8: JavaScript plugins, new plugin hooks and plugin configuration in datasette.yaml `__ for an annotated version of these release notes. - -Configuration -~~~~~~~~~~~~~ - -- Plugin configuration now lives in the :ref:`datasette.yaml configuration file `, passed to Datasette using the ``-c/--config`` option. Thanks, Alex Garcia. (:issue:`2093`) - - .. code-block:: bash - - datasette -c datasette.yaml - - Where ``datasette.yaml`` contains configuration that looks like this: - - .. code-block:: yaml - - plugins: - datasette-cluster-map: - latitude_column: xlat - longitude_column: xlon - - Previously plugins were configured in ``metadata.yaml``, which was confusing as plugin settings were unrelated to database and table metadata. -- The ``-s/--setting`` option can now be used to set plugin configuration as well. See :ref:`configuration_cli` for details. (:issue:`2252`) - - The above YAML configuration example using ``-s/--setting`` looks like this: - - .. code-block:: bash - - datasette mydatabase.db \ - -s plugins.datasette-cluster-map.latitude_column xlat \ - -s plugins.datasette-cluster-map.longitude_column xlon - -- The new ``/-/config`` page shows the current instance configuration, after redacting keys that could contain sensitive data such as API keys or passwords. (:issue:`2254`) - -- Existing Datasette installations may already have configuration set in ``metadata.yaml`` that should be migrated to ``datasette.yaml``. To avoid breaking these installations, Datasette will silently treat table configuration, plugin configuration and allow blocks in metadata as if they had been specified in configuration instead. (:issue:`2247`) (:issue:`2248`) (:issue:`2249`) - -Note that the ``datasette publish`` command has not yet been updated to accept a ``datasette.yaml`` configuration file. This will be addressed in :issue:`2195` but for the moment you can include those settings in ``metadata.yaml`` instead. - -JavaScript plugins -~~~~~~~~~~~~~~~~~~ - -Datasette now includes a :ref:`JavaScript plugins mechanism `, allowing JavaScript to customize Datasette in a way that can collaborate with other plugins. - -This provides two initial hooks, with more to come in the future: - -- :ref:`makeAboveTablePanelConfigs() ` can add additional panels to the top of the table page. -- :ref:`makeColumnActions() ` can add additional actions to the column menu. - -Thanks `Cameron Yick `__ for contributing this feature. (`#2052 `__) - -Plugin hooks -~~~~~~~~~~~~ - -- New :ref:`plugin_hook_jinja2_environment_from_request` plugin hook, which can be used to customize the current Jinja environment based on the incoming request. This can be used to modify the template lookup path based on the incoming request hostname, among other things. (:issue:`2225`) -- New :ref:`family of template slot plugin hooks `: ``top_homepage``, ``top_database``, ``top_table``, ``top_row``, ``top_query``, ``top_canned_query``. Plugins can use these to provide additional HTML to be injected at the top of the corresponding pages. (:issue:`1191`) -- New :ref:`track_event() mechanism ` for plugins to emit and receive events when certain events occur within Datasette. (:issue:`2240`) - - Plugins can register additional event classes using :ref:`plugin_hook_register_events`. - - They can then trigger those events with the :ref:`datasette.track_event(event) ` internal method. - - Plugins can subscribe to notifications of events using the :ref:`plugin_hook_track_event` plugin hook. - - Datasette core now emits ``login``, ``logout``, ``create-token``, ``create-table``, ``drop-table``, ``insert-rows``, ``upsert-rows``, ``update-row``, ``delete-row`` events, :ref:`documented here `. -- New internal function for plugin authors: :ref:`database_execute_isolated_fn`, for creating a new SQLite connection, executing code and then closing that connection, all while preventing other code from writing to that particular database. This connection will not have the :ref:`prepare_connection() ` plugin hook executed against it, allowing plugins to perform actions that might otherwise be blocked by existing connection configuration. (:issue:`2218`) - -Documentation -~~~~~~~~~~~~~ - -- Documentation describing :ref:`how to write tests that use signed actor cookies ` using ``datasette.client.actor_cookie()``. (:issue:`1830`) -- Documentation on how to :ref:`register a plugin for the duration of a test `. (:issue:`2234`) -- The :ref:`configuration documentation ` now shows examples of both YAML and JSON for each setting. - -Minor fixes -~~~~~~~~~~~ - -- Datasette no longer attempts to run SQL queries in parallel when rendering a table page, as this was leading to some rare crashing bugs. (:issue:`2189`) -- Fixed warning: ``DeprecationWarning: pkg_resources is deprecated as an API`` (:issue:`2057`) -- Fixed bug where ``?_extra=columns`` parameter returned an incorrectly shaped response. (:issue:`2230`) - .. _v0_64_6: 0.64.6 (2023-12-22) @@ -385,13 +59,6 @@ Minor fixes - Dropped dependency on ``click-default-group-wheel``, which could cause a dependency conflict. (:issue:`2197`) -.. _v1_0_a7: - -1.0a7 (2023-09-21) ------------------- - -- Fix for a crashing bug caused by viewing the table page for a named in-memory database. (:issue:`2189`) - .. _v0_64_4: 0.64.4 (2023-09-21) @@ -399,96 +66,12 @@ Minor fixes - Fix for a crashing bug caused by viewing the table page for a named in-memory database. (:issue:`2189`) -.. _v1_0_a6: +.. _v0_64_3: -1.0a6 (2023-09-07) ------------------- +0.64.3 (2023-04-27) +------------------- -- New plugin hook: :ref:`plugin_hook_actors_from_ids` and an internal method to accompany it, :ref:`datasette_actors_from_ids`. This mechanism is intended to be used by plugins that may need to display the actor who was responsible for something managed by that plugin: they can now resolve the recorded IDs of actors into the full actor objects. (:issue:`2181`) -- ``DATASETTE_LOAD_PLUGINS`` environment variable for :ref:`controlling which plugins ` are loaded by Datasette. (:issue:`2164`) -- Datasette now checks if the user has permission to view a table linked to by a foreign key before turning that foreign key into a clickable link. (:issue:`2178`) -- The ``execute-sql`` permission now implies that the actor can also view the database and instance. (:issue:`2169`) -- Documentation describing a pattern for building plugins that themselves :ref:`define further hooks ` for other plugins. (:issue:`1765`) -- Datasette is now tested against the Python 3.12 preview. (`#2175 `__) - -.. _v1_0_a5: - -1.0a5 (2023-08-29) ------------------- - -- When restrictions are applied to :ref:`API tokens `, those restrictions now behave slightly differently: applying the ``view-table`` restriction will imply the ability to ``view-database`` for the database containing that table, and both ``view-table`` and ``view-database`` will imply ``view-instance``. Previously you needed to create a token with restrictions that explicitly listed ``view-instance`` and ``view-database`` and ``view-table`` in order to view a table without getting a permission denied error. (:issue:`2102`) -- New ``datasette.yaml`` (or ``.json``) configuration file, which can be specified using ``datasette -c path-to-file``. The goal here to consolidate settings, plugin configuration, permissions, canned queries, and other Datasette configuration into a single single file, separate from ``metadata.yaml``. The legacy ``settings.json`` config file used for :ref:`config_dir` has been removed, and ``datasette.yaml`` has a ``"settings"`` section where the same settings key/value pairs can be included. In the next future alpha release, more configuration such as plugins/permissions/canned queries will be moved to the ``datasette.yaml`` file. See :issue:`2093` for more details. Thanks, Alex Garcia. -- The ``-s/--setting`` option can now take dotted paths to nested settings. These will then be used to set or over-ride the same options as are present in the new configuration file. (:issue:`2156`) -- New ``--actor '{"id": "json-goes-here"}'`` option for use with ``datasette --get`` to treat the simulated request as being made by a specific actor, see :ref:`cli_datasette_get`. (:issue:`2153`) -- The Datasette ``_internal`` database has had some changes. It no longer shows up in the ``datasette.databases`` list by default, and is now instead available to plugins using the ``datasette.get_internal_database()``. Plugins are invited to use this as a private database to store configuration and settings and secrets that should not be made visible through the default Datasette interface. Users can pass the new ``--internal internal.db`` option to persist that internal database to disk. Thanks, Alex Garcia. (:issue:`2157`). - -.. _v1_0_a4: - -1.0a4 (2023-08-21) ------------------- - -This alpha fixes a security issue with the ``/-/api`` API explorer. On authenticated Datasette instances (instances protected using plugins such as `datasette-auth-passwords `__) the API explorer interface could reveal the names of databases and tables within the protected instance. The data stored in those tables was not revealed. - -For more information and workarounds, read `the security advisory `__. The issue has been present in every previous alpha version of Datasette 1.0: versions 1.0a0, 1.0a1, 1.0a2 and 1.0a3. - -Also in this alpha: - -- The new ``datasette plugins --requirements`` option outputs a list of currently installed plugins in Python ``requirements.txt`` format, useful for duplicating that installation elsewhere. (:issue:`2133`) -- :ref:`canned_queries_writable` can now define a ``on_success_message_sql`` field in their configuration, containing a SQL query that should be executed upon successful completion of the write operation in order to generate a message to be shown to the user. (:issue:`2138`) -- The automatically generated border color for a database is now shown in more places around the application. (:issue:`2119`) -- Every instance of example shell script code in the documentation should now include a working copy button, free from additional syntax. (:issue:`2140`) - -.. _v1_0_a3: - -1.0a3 (2023-08-09) ------------------- - -This alpha release previews the updated design for Datasette's default JSON API. (:issue:`782`) - -The new :ref:`default JSON representation ` for both table pages (``/dbname/table.json``) and arbitrary SQL queries (``/dbname.json?sql=...``) is now shaped like this: - -.. code-block:: json - - { - "ok": true, - "rows": [ - { - "id": 3, - "name": "Detroit" - }, - { - "id": 2, - "name": "Los Angeles" - }, - { - "id": 4, - "name": "Memnonia" - }, - { - "id": 1, - "name": "San Francisco" - } - ], - "truncated": false - } - -Tables will include an additional ``"next"`` key for pagination, which can be passed to ``?_next=`` to fetch the next page of results. - -The various ``?_shape=`` options continue to work as before - see :ref:`json_api_shapes` for details. - -A new ``?_extra=`` mechanism is available for tables, but has not yet been stabilized or documented. Details on that are available in :issue:`262`. - -Smaller changes -~~~~~~~~~~~~~~~ - -- Datasette documentation now shows YAML examples for :ref:`metadata` by default, with a tab interface for switching to JSON. (:issue:`1153`) -- :ref:`plugin_register_output_renderer` plugins now have access to ``error`` and ``truncated`` arguments, allowing them to display error messages and take into account truncated results. (:issue:`2130`) -- ``render_cell()`` plugin hook now also supports an optional ``request`` argument. (:issue:`2007`) -- New ``Justfile`` to support development workflows for Datasette using `Just `__. -- ``datasette.render_template()`` can now accepts a ``datasette.views.Context`` subclass as an alternative to a dictionary. (:issue:`2127`) -- ``datasette install -e path`` option for editable installations, useful while developing plugins. (:issue:`2106`) -- When started with the ``--cors`` option Datasette now serves an ``Access-Control-Max-Age: 3600`` header, ensuring CORS OPTIONS requests are repeated no more than once an hour. (:issue:`2079`) -- Fixed a bug where the ``_internal`` database could display ``None`` instead of ``null`` for in-memory databases. (:issue:`1970`) +- Added ``pip`` and ``setuptools`` as explicit dependencies. This fixes a bug where Datasette could not be installed using `Rye `__. (:issue:`2065`) .. _v0_64_2: @@ -521,68 +104,9 @@ Smaller changes 0.63.3 (2022-12-17) ------------------- -- Fixed a bug where ``datasette --root``, when running in Docker, would only output the URL to sign in root when the server shut down, not when it started up. (:issue:`1958`) +- Fixed a bug where ``datasette --root``, when running in Docker, would only output the URL to sign in as root when the server shut down, not when it started up. (:issue:`1958`) - You no longer need to ensure ``await datasette.invoke_startup()`` has been called in order for Datasette to start correctly serving requests - this is now handled automatically the first time the server receives a request. This fixes a bug experienced when Datasette is served directly by an ASGI application server such as Uvicorn or Gunicorn. It also fixes a bug with the `datasette-gunicorn `__ plugin. (:issue:`1955`) -.. _v1_0_a2: - -1.0a2 (2022-12-14) ------------------- - -The third Datasette 1.0 alpha release adds upsert support to the JSON API, plus the ability to specify finely grained permissions when creating an API token. - -See `Datasette 1.0a2: Upserts and finely grained permissions `__ for an extended, annotated version of these release notes. - -- New ``/db/table/-/upsert`` API, :ref:`documented here `. upsert is an update-or-insert: existing rows will have specified keys updated, but if no row matches the incoming primary key a brand new row will be inserted instead. (:issue:`1878`) -- New ``register_permissions()`` plugin hook. Plugins can now register named permissions, which will then be listed in various interfaces that show available permissions. (:issue:`1940`) -- The ``/db/-/create`` API for :ref:`creating a table ` now accepts ``"ignore": true`` and ``"replace": true`` options when called with the ``"rows"`` property that creates a new table based on an example set of rows. This means the API can be called multiple times with different rows, setting rules for what should happen if a primary key collides with an existing row. (:issue:`1927`) -- Arbitrary permissions can now be configured at the instance, database and resource (table, SQL view or canned query) level in Datasette's :ref:`metadata` JSON and YAML files. The new ``"permissions"`` key can be used to specify which actors should have which permissions. See :ref:`authentication_permissions_other` for details. (:issue:`1636`) -- The ``/-/create-token`` page can now be used to create API tokens which are restricted to just a subset of actions, including against specific databases or resources. See :ref:`CreateTokenView` for details. (:issue:`1947`) -- Likewise, the ``datasette create-token`` CLI command can now create tokens with :ref:`a subset of permissions `. (:issue:`1855`) -- New :ref:`datasette.create_token() API method ` for programmatically creating signed API tokens. (:issue:`1951`) -- ``/db/-/create`` API now requires actor to have ``insert-row`` permission in order to use the ``"row"`` or ``"rows"`` properties. (:issue:`1937`) - -.. _v1_0_a1: - -1.0a1 (2022-12-01) ------------------- - -- Write APIs now serve correct CORS headers if Datasette is started in ``--cors`` mode. See the full list of :ref:`CORS headers ` in the documentation. (:issue:`1922`) -- Fixed a bug where the ``_memory`` database could be written to even though writes were not persisted. (:issue:`1917`) -- The https://latest.datasette.io/ demo instance now includes an ``ephemeral`` database which can be used to test Datasette's write APIs, using the new `datasette-ephemeral-tables `_ plugin to drop any created tables after five minutes. This database is only available if you sign in as the root user using the link on the homepage. (:issue:`1915`) -- Fixed a bug where hitting the write endpoints with a ``GET`` request returned a 500 error. It now returns a 405 (method not allowed) error instead. (:issue:`1916`) -- The list of endpoints in the API explorer now lists mutable databases first. (:issue:`1918`) -- The ``"ignore": true`` and ``"replace": true`` options for the insert API are :ref:`now documented `. (:issue:`1924`) - -.. _v1_0_a0: - -1.0a0 (2022-11-29) ------------------- - -This first alpha release of Datasette 1.0 introduces a brand new collection of APIs for writing to the database (:issue:`1850`), as well as a new API token mechanism baked into Datasette core. Previously, API tokens have only been supported by installing additional plugins. - -This is very much a preview: expect many more backwards incompatible API changes prior to the full 1.0 release. - -Feedback enthusiastically welcomed, either through `issue comments `__ or via the `Datasette Discord `__ community. - -Signed API tokens -~~~~~~~~~~~~~~~~~ - -- New ``/-/create-token`` page allowing authenticated users to create signed API tokens that can act on their behalf, see :ref:`CreateTokenView`. (:issue:`1852`) -- New ``datasette create-token`` command for creating tokens from the command line: :ref:`authentication_cli_create_token`. -- New :ref:`setting_allow_signed_tokens` setting which can be used to turn off signed token support. (:issue:`1856`) -- New :ref:`setting_max_signed_tokens_ttl` setting for restricting the maximum allowed duration of a signed token. (:issue:`1858`) - -Write API -~~~~~~~~~ - -- New API explorer at ``/-/api`` for trying out the API. (:issue:`1871`) -- ``/db/-/create`` API for :ref:`TableCreateView`. (:issue:`1882`) -- ``/db/table/-/insert`` API for :ref:`TableInsertView`. (:issue:`1851`) -- ``/db/table/-/drop`` API for :ref:`TableDropView`. (:issue:`1874`) -- ``/db/table/pk/-/update`` API for :ref:`RowUpdateView`. (:issue:`1863`) -- ``/db/table/pk/-/delete`` API for :ref:`RowDeleteView`. (:issue:`1864`) - .. _v0_63_2: 0.63.2 (2022-11-18) @@ -643,11 +167,11 @@ Documentation .. _v0_62: 0.62 (2022-08-14) ------------------ +------------------- Datasette can now run entirely in your browser using WebAssembly. Try out `Datasette Lite `__, take a look `at the code `__ or read more about it in `Datasette Lite: a server-side Python web application running in a browser `__. -Datasette now has a `Discord community `__ for questions and discussions about Datasette and its ecosystem of projects. +Datasette now has a `Discord community `__ for questions and discussions about Datasette and its ecosystem of projects. Features ~~~~~~~~ @@ -709,7 +233,7 @@ Datasette also now requires Python 3.7 or higher. - Datasette is now covered by a `Code of Conduct `__. (:issue:`1654`) - Python 3.6 is no longer supported. (:issue:`1577`) - Tests now run against Python 3.11-dev. (:issue:`1621`) -- New ``datasette.ensure_permissions(actor, permissions)`` internal method for checking multiple permissions at once. (:issue:`1675`) +- New :ref:`datasette.ensure_permissions(actor, permissions) ` internal method for checking multiple permissions at once. (:issue:`1675`) - New :ref:`datasette.check_visibility(actor, action, resource=None) ` internal method for checking if a user can see a resource that would otherwise be invisible to unauthenticated users. (:issue:`1678`) - Table and row HTML pages now include a ```` element and return a ``Link: URL; rel="alternate"; type="application/json+datasette"`` HTTP header pointing to the JSON version of those pages. (:issue:`1533`) - ``Access-Control-Expose-Headers: Link`` is now added to the CORS headers, allowing remote JavaScript to access that header. @@ -849,7 +373,7 @@ Other small fixes - New ``datasette --uds /tmp/datasette.sock`` option for binding Datasette to a Unix domain socket, see :ref:`proxy documentation ` (:issue:`1388`) - ``"searchmode": "raw"`` table metadata option for defaulting a table to executing SQLite full-text search syntax without first escaping it, see :ref:`full_text_search_advanced_queries`. (:issue:`1389`) -- New plugin hook: ``get_metadata()``, for returning custom metadata for an instance, database or table. Thanks, Brandon Roberts! (:issue:`1384`) +- New plugin hook: :ref:`plugin_hook_get_metadata`, for returning custom metadata for an instance, database or table. Thanks, Brandon Roberts! (:issue:`1384`) - New plugin hook: :ref:`plugin_hook_skip_csrf`, for opting out of CSRF protection based on the incoming request. (:issue:`1377`) - The :ref:`menu_links() `, :ref:`table_actions() ` and :ref:`database_actions() ` plugin hooks all gained a new optional ``request`` argument providing access to the current request. (:issue:`1371`) - Major performance improvement for Datasette faceting. (:issue:`1394`) @@ -977,7 +501,7 @@ JavaScript modules To use modules, JavaScript needs to be included in `` + +You can also specify a SRI (subresource integrity hash) for these assets: + +.. code-block:: json + + { + "extra_css_urls": [ + { + "url": "https://simonwillison.net/static/css/all.bf8cd891642c.css", + "sri": "sha384-9qIZekWUyjCyDIf2YK1FRoKiPJq4PHt6tp/ulnuuyRBvazd0hG7pWbE99zvwSznI" + } + ], + "extra_js_urls": [ + { + "url": "https://code.jquery.com/jquery-3.2.1.slim.min.js", + "sri": "sha256-k2WSCIexGzOj3Euiig+TlR8gA0EmPjuc79OEeY5L45g=" + } + ] + } + +This will produce: + +.. code-block:: html + + + + +Modern browsers will only execute the stylesheet or JavaScript if the SRI hash +matches the content served. You can generate hashes using `www.srihash.org `_ + +Items in ``"extra_js_urls"`` can specify ``"module": true`` if they reference JavaScript that uses `JavaScript modules `__. This configuration: + +.. code-block:: json + + { + "extra_js_urls": [ + { + "url": "https://example.datasette.io/module.js", + "module": true + } + ] + } + +Will produce this HTML: + +.. code-block:: html + + + CSS classes on the ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -83,15 +164,6 @@ database column they are representing, for example:
-.. _customization_css: - -Writing custom CSS -~~~~~~~~~~~~~~~~~~ - -Custom templates need to take Datasette's default CSS into account. The pattern portfolio at ``/-/patterns`` (`example here `__) is a useful reference for understanding the available CSS classes. - -The ``core`` class is particularly useful - you can apply this directly to a ```` or ``