mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
allow_signed_tokens setting, closes #1856
This commit is contained in:
parent
0f013ff497
commit
c23fa850e7
8 changed files with 48 additions and 5 deletions
|
|
@ -124,6 +124,11 @@ SETTINGS = (
|
|||
True,
|
||||
"Allow users to download the original SQLite database files",
|
||||
),
|
||||
Setting(
|
||||
"allow_signed_tokens",
|
||||
True,
|
||||
"Allow users to create and use signed API tokens",
|
||||
),
|
||||
Setting("suggest_facets", True, "Calculate and display suggested facets"),
|
||||
Setting(
|
||||
"default_cache_ttl",
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ def permission_allowed(datasette, actor, action, resource):
|
|||
@hookimpl
|
||||
def actor_from_request(datasette, request):
|
||||
prefix = "dstok_"
|
||||
if not datasette.setting("allow_signed_tokens"):
|
||||
return None
|
||||
authorization = request.headers.get("authorization")
|
||||
if not authorization:
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -171,6 +171,8 @@ class CreateTokenView(BaseView):
|
|||
has_json_alternate = False
|
||||
|
||||
def check_permission(self, request):
|
||||
if not self.ds.setting("allow_signed_tokens"):
|
||||
raise Forbidden("Signed tokens are not enabled for this Datasette instance")
|
||||
if not request.actor:
|
||||
raise Forbidden("You must be logged in to create a token")
|
||||
if not request.actor.get("id"):
|
||||
|
|
|
|||
|
|
@ -350,6 +350,8 @@ Coming soon: a mechanism for creating tokens that can only perform a subset of t
|
|||
|
||||
This page cannot be accessed by actors with a ``"token": "some-value"`` property. This is to prevent API tokens from being used to automatically create more tokens. Datasette plugins that implement their own form of API token authentication should follow this convention.
|
||||
|
||||
You can disable this feature using the :ref:`allow_signed_tokens <setting_allow_signed_tokens>` setting.
|
||||
|
||||
.. _permissions_plugins:
|
||||
|
||||
Checking permissions in plugins
|
||||
|
|
|
|||
|
|
@ -226,6 +226,8 @@ These can be passed to ``datasette serve`` using ``datasette serve --setting nam
|
|||
?_facet= parameter (default=True)
|
||||
allow_download Allow users to download the original SQLite
|
||||
database files (default=True)
|
||||
allow_signed_tokens Allow users to create and use signed API tokens
|
||||
(default=True)
|
||||
suggest_facets Calculate and display suggested facets
|
||||
(default=True)
|
||||
default_cache_ttl Default HTTP cache TTL (used in Cache-Control:
|
||||
|
|
|
|||
|
|
@ -151,6 +151,7 @@ If you run ``datasette plugins --all`` it will include default plugins that ship
|
|||
"templates": false,
|
||||
"version": null,
|
||||
"hooks": [
|
||||
"actor_from_request",
|
||||
"permission_allowed"
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -169,6 +169,19 @@ Should users be able to download the original SQLite database using a link on th
|
|||
|
||||
datasette mydatabase.db --setting allow_download off
|
||||
|
||||
.. _setting_allow_signed_tokens:
|
||||
|
||||
allow_signed_tokens
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Should users be able to create signed API tokens to access Datasette?
|
||||
|
||||
This is turned on by default. Use the following to turn it off::
|
||||
|
||||
datasette mydatabase.db --setting allow_signed_tokens off
|
||||
|
||||
Turning this setting off will disable the ``/-/create-token`` page, :ref:`described here <CreateTokenView>`. It will also cause any incoming ``Authorization: Bearer dstok_...`` API tokens to be ignored.
|
||||
|
||||
.. _setting_default_cache_ttl:
|
||||
|
||||
default_cache_ttl
|
||||
|
|
|
|||
|
|
@ -189,9 +189,20 @@ def test_auth_create_token_not_allowed_for_tokens(app_client):
|
|||
assert response.status == 403
|
||||
|
||||
|
||||
def test_auth_create_token_not_allowed_if_allow_signed_tokens_off(app_client):
|
||||
app_client.ds._settings["allow_signed_tokens"] = False
|
||||
try:
|
||||
ds_actor = app_client.actor_cookie({"id": "test"})
|
||||
response = app_client.get("/-/create-token", cookies={"ds_actor": ds_actor})
|
||||
assert response.status == 403
|
||||
finally:
|
||||
app_client.ds._settings["allow_signed_tokens"] = True
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"scenario,should_work",
|
||||
(
|
||||
("allow_signed_tokens_off", False),
|
||||
("no_token", False),
|
||||
("invalid_token", False),
|
||||
("expired_token", False),
|
||||
|
|
@ -201,7 +212,7 @@ def test_auth_create_token_not_allowed_for_tokens(app_client):
|
|||
)
|
||||
def test_auth_with_dstok_token(app_client, scenario, should_work):
|
||||
token = None
|
||||
if scenario == "valid_unlimited_token":
|
||||
if scenario in ("valid_unlimited_token", "allow_signed_tokens_off"):
|
||||
token = app_client.ds.sign({"a": "test"}, "token")
|
||||
elif scenario == "valid_expiring_token":
|
||||
token = app_client.ds.sign({"a": "test", "e": int(time.time()) + 1000}, "token")
|
||||
|
|
@ -211,11 +222,16 @@ def test_auth_with_dstok_token(app_client, scenario, should_work):
|
|||
token = "invalid"
|
||||
if token:
|
||||
token = "dstok_{}".format(token)
|
||||
if scenario == "allow_signed_tokens_off":
|
||||
app_client.ds._settings["allow_signed_tokens"] = False
|
||||
headers = {}
|
||||
if token:
|
||||
headers["Authorization"] = "Bearer {}".format(token)
|
||||
response = app_client.get("/-/actor.json", headers=headers)
|
||||
if should_work:
|
||||
assert response.json == {"actor": {"id": "test", "token": "dstok"}}
|
||||
else:
|
||||
assert response.json == {"actor": None}
|
||||
try:
|
||||
if should_work:
|
||||
assert response.json == {"actor": {"id": "test", "token": "dstok"}}
|
||||
else:
|
||||
assert response.json == {"actor": None}
|
||||
finally:
|
||||
app_client.ds._settings["allow_signed_tokens"] = True
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue