Skip to content

Commit

Permalink
Show actor username if available on /-/secrets, closes #11
Browse files Browse the repository at this point in the history
  • Loading branch information
simonw committed Apr 24, 2024
1 parent 96536e9 commit 42d4980
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 1 deletion.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ datasette data.db --internal internal.db \

users with the `manage-secrets` permission will see a new "Manage secrets" link in the Datasette navigation menu. This interface can also be accessed at `/-/secrets`.

The page with the list of secrets will show the user who last updated each secret. This will use the [actors_from_ids()](https://docs.datasette.io/en/latest/plugin_hooks.html#actors-from-ids-datasette-actor-ids) mechanism, displaying the actor's `username` if available, otherwise the `name`, otherwise the `id`.

## For plugin authors

Plugins can depend on this plugin if they want to implement secrets.
Expand Down
10 changes: 10 additions & 0 deletions datasette_secrets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,16 @@ async def secrets_index(datasette, request):
list(environment_secrets_names),
)
existing_secrets = {row["name"]: dict(row) for row in existing_secrets_result.rows}
# Try to turn updated_by into actors
actors = await datasette.actors_from_ids(
{row["updated_by"] for row in existing_secrets.values() if row["updated_by"]}
)
for secret in existing_secrets.values():
if secret["updated_by"]:
actor = actors.get(secret["updated_by"])
if actor:
display = actor.get("username") or actor.get("name") or actor.get("id")
secret["updated_by"] = display
unset_secrets = [
secret
for secret in all_secrets
Expand Down
31 changes: 30 additions & 1 deletion tests/test_secrets.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from click.testing import CliRunner
from cryptography.fernet import Fernet
from datasette import hookimpl
from datasette.app import Datasette
from datasette.cli import cli
from datasette.plugins import pm
from datasette_secrets import get_secret
import pytest
from unittest.mock import ANY
Expand All @@ -21,6 +23,26 @@ def test_generate_command():
assert key.decrypt(key.encrypt(message)) == message


@pytest.fixture
def use_actors_plugin():
class ActorPlugin:
__name__ = "ActorPlugin"

@hookimpl
def actors_from_ids(self, actor_ids):
return {
id: {
"id": id,
"username": id.upper(),
}
for id in actor_ids
}

pm.register(ActorPlugin(), name="undo")
yield
pm.unregister(name="undo")


@pytest.fixture
def ds():
return Datasette(
Expand Down Expand Up @@ -65,7 +87,7 @@ async def test_permissions(ds, path, verb, data, user):


@pytest.mark.asyncio
async def test_set_secret(ds):
async def test_set_secret(ds, use_actors_plugin):
cookies = {"ds_actor": ds.client.actor_cookie({"id": "admin"})}
get_response = await ds.client.get("/-/secrets/EXAMPLE_SECRET", cookies=cookies)
csrftoken = get_response.cookies["ds_csrftoken"]
Expand Down Expand Up @@ -104,6 +126,13 @@ async def test_set_secret(ds):
decrypted = key.decrypt(encrypted)
assert decrypted == b"new-secret-value"

# Check that the listing is as expected, including showing the actor username
response = await ds.client.get("/-/secrets", cookies=cookies)
assert response.status_code == 200
assert "EXAMPLE_SECRET" in response.text
assert "new-note" in response.text
assert "<td>ADMIN</td>" in response.text

# Now let's edit it
post_response2 = await ds.client.post(
"/-/secrets/EXAMPLE_SECRET",
Expand Down

0 comments on commit 42d4980

Please sign in to comment.