mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
datasette.sign() and datasette.unsign() methods, refs #785
This commit is contained in:
parent
1fc6ceefb9
commit
fa27e44fe0
7 changed files with 61 additions and 0 deletions
|
|
@ -14,6 +14,7 @@ from pathlib import Path
|
|||
|
||||
import click
|
||||
from markupsafe import Markup
|
||||
from itsdangerous import URLSafeSerializer
|
||||
import jinja2
|
||||
from jinja2 import ChoiceLoader, Environment, FileSystemLoader, PrefixLoader, escape
|
||||
from jinja2.environment import Template
|
||||
|
|
@ -163,12 +164,14 @@ class Datasette:
|
|||
static_mounts=None,
|
||||
memory=False,
|
||||
config=None,
|
||||
secret=None,
|
||||
version_note=None,
|
||||
config_dir=None,
|
||||
):
|
||||
assert config_dir is None or isinstance(
|
||||
config_dir, Path
|
||||
), "config_dir= should be a pathlib.Path"
|
||||
self._secret = secret or os.urandom(32).hex()
|
||||
self.files = tuple(files) + tuple(immutables or [])
|
||||
if config_dir:
|
||||
self.files += tuple([str(p) for p in config_dir.glob("*.db")])
|
||||
|
|
@ -281,6 +284,12 @@ class Datasette:
|
|||
|
||||
self._register_renderers()
|
||||
|
||||
def sign(self, value, namespace="default"):
|
||||
return URLSafeSerializer(self._secret, namespace).dumps(value)
|
||||
|
||||
def unsign(self, signed, namespace="default"):
|
||||
return URLSafeSerializer(self._secret, namespace).loads(signed)
|
||||
|
||||
def get_database(self, name=None):
|
||||
if name is None:
|
||||
return next(iter(self.databases.values()))
|
||||
|
|
|
|||
|
|
@ -299,6 +299,11 @@ def package(
|
|||
help="Set config option using configname:value datasette.readthedocs.io/en/latest/config.html",
|
||||
multiple=True,
|
||||
)
|
||||
@click.option(
|
||||
"--secret",
|
||||
help="Secret used for signing secure values, such as signed cookies",
|
||||
envvar="DATASETTE_SECRET",
|
||||
)
|
||||
@click.option("--version-note", help="Additional note to show on /-/versions")
|
||||
@click.option("--help-config", is_flag=True, help="Show available config options")
|
||||
def serve(
|
||||
|
|
@ -317,6 +322,7 @@ def serve(
|
|||
static,
|
||||
memory,
|
||||
config,
|
||||
secret,
|
||||
version_note,
|
||||
help_config,
|
||||
return_instance=False,
|
||||
|
|
@ -362,6 +368,7 @@ def serve(
|
|||
static_mounts=static,
|
||||
config=dict(config),
|
||||
memory=memory,
|
||||
secret=secret,
|
||||
version_note=version_note,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ Options:
|
|||
--config CONFIG Set config option using configname:value
|
||||
datasette.readthedocs.io/en/latest/config.html
|
||||
|
||||
--secret TEXT Secret used for signing secure values, such as signed
|
||||
cookies
|
||||
|
||||
--version-note TEXT Additional note to show on /-/versions
|
||||
--help-config Show available config options
|
||||
--help Show this message and exit.
|
||||
|
|
|
|||
|
|
@ -183,6 +183,34 @@ Use ``is_memory`` if the connection is to an in-memory SQLite database.
|
|||
|
||||
This removes a database that has been previously added. ``name=`` is the unique name of that database, also used in the URL for it.
|
||||
|
||||
.. _datasette_sign:
|
||||
|
||||
.sign(value, namespace="default")
|
||||
---------------------------------
|
||||
|
||||
``value`` - any serializable type
|
||||
The value to be signed.
|
||||
|
||||
``namespace`` - string, optional
|
||||
An alternative namespace, see the `itsdangerous salt documentation <https://itsdangerous.palletsprojects.com/en/1.1.x/serializer/#the-salt>`__.
|
||||
|
||||
Utility method for signing values, such that you can safely pass data to and from an untrusted environment. This is a wrapper around the `itsdangerous <https://itsdangerous.palletsprojects.com/>`__ library.
|
||||
|
||||
This method returns a signed string, which can be decoded and verified using :ref:`datasette_unsign`.
|
||||
|
||||
.. _datasette_unsign:
|
||||
|
||||
.unsign(value, namespace="default")
|
||||
-----------------------------------
|
||||
|
||||
``signed`` - any serializable type
|
||||
The signed string that was created using :ref:`datasette_sign`.
|
||||
|
||||
``namespace`` - string, optional
|
||||
The alternative namespace, if one was used.
|
||||
|
||||
Returns the original, decoded object that was passed to :ref:`datasette_sign`. If the signature is not valid this raises a ``itsdangerous.BadSignature`` exception.
|
||||
|
||||
.. _internals_database:
|
||||
|
||||
Database class
|
||||
|
|
|
|||
1
setup.py
1
setup.py
|
|
@ -55,6 +55,7 @@ setup(
|
|||
"janus>=0.4,<0.6",
|
||||
"PyYAML~=5.3",
|
||||
"mergedeep>=1.1.1,<1.4.0",
|
||||
"itsdangerous~=1.1",
|
||||
],
|
||||
entry_points="""
|
||||
[console_scripts]
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ def test_metadata_yaml():
|
|||
static=[],
|
||||
memory=False,
|
||||
config=[],
|
||||
secret=None,
|
||||
version_note=None,
|
||||
help_config=False,
|
||||
return_instance=True,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"""
|
||||
Tests for the datasette.app.Datasette class
|
||||
"""
|
||||
from itsdangerous import BadSignature
|
||||
from .fixtures import app_client
|
||||
import pytest
|
||||
|
||||
|
|
@ -21,3 +22,14 @@ def test_get_database_no_argument(datasette):
|
|||
# Returns the first available database:
|
||||
db = datasette.get_database()
|
||||
assert "fixtures" == db.name
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value", ["hello", 123, {"key": "value"}])
|
||||
@pytest.mark.parametrize("namespace", [None, "two"])
|
||||
def test_sign_unsign(datasette, value, namespace):
|
||||
extra_args = [namespace] if namespace else []
|
||||
signed = datasette.sign(value, *extra_args)
|
||||
assert value != signed
|
||||
assert value == datasette.unsign(signed, *extra_args)
|
||||
with pytest.raises(BadSignature):
|
||||
datasette.unsign(signed[:-1] + ("!" if signed[-1] != "!" else ":"))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue