datasette-secrets 0.1a0__tar.gz → 0.1a1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of datasette-secrets might be problematic. Click here for more details.
- {datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/PKG-INFO +4 -2
- {datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/README.md +3 -1
- {datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/datasette_secrets/__init__.py +20 -6
- {datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/datasette_secrets.egg-info/PKG-INFO +4 -2
- {datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/pyproject.toml +1 -1
- {datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/tests/test_secrets.py +25 -2
- {datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/LICENSE +0 -0
- {datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/datasette_secrets/hookspecs.py +0 -0
- {datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/datasette_secrets/templates/secrets_index.html +0 -0
- {datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/datasette_secrets/templates/secrets_update.html +0 -0
- {datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/datasette_secrets.egg-info/SOURCES.txt +0 -0
- {datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/datasette_secrets.egg-info/dependency_links.txt +0 -0
- {datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/datasette_secrets.egg-info/entry_points.txt +0 -0
- {datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/datasette_secrets.egg-info/requires.txt +0 -0
- {datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/datasette_secrets.egg-info/top_level.txt +0 -0
- {datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: datasette-secrets
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.1a1
|
|
4
4
|
Summary: Manage secrets such as API keys for use with other Datasette plugins
|
|
5
5
|
Author: Datasette
|
|
6
6
|
License: Apache-2.0
|
|
@@ -135,12 +135,14 @@ To obtain the current value of the secret, use the `await get_secret()` method:
|
|
|
135
135
|
```python
|
|
136
136
|
from datasette_secrets import get_secret
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
# Third argument is the actor_id, optional
|
|
139
|
+
secret = await get_secret(datasette, "OPENAI_API_KEY", "root")
|
|
139
140
|
```
|
|
140
141
|
If the Datasette administrator set a `DATASETTE_SECRETS_OPENAI_API_KEY` environment variable, that will be returned.
|
|
141
142
|
|
|
142
143
|
Otherwise the encrypted value in the database table will be decrypted and returned - or `None` if there is no configured secret.
|
|
143
144
|
|
|
145
|
+
The `last_used_at` column is updated every time a secret is accessed. The `last_used_by` column will be set to the actor ID passed to `get_secret()`, or `null` if no actor ID was passed.
|
|
144
146
|
|
|
145
147
|
## Development
|
|
146
148
|
|
|
@@ -114,12 +114,14 @@ To obtain the current value of the secret, use the `await get_secret()` method:
|
|
|
114
114
|
```python
|
|
115
115
|
from datasette_secrets import get_secret
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
# Third argument is the actor_id, optional
|
|
118
|
+
secret = await get_secret(datasette, "OPENAI_API_KEY", "root")
|
|
118
119
|
```
|
|
119
120
|
If the Datasette administrator set a `DATASETTE_SECRETS_OPENAI_API_KEY` environment variable, that will be returned.
|
|
120
121
|
|
|
121
122
|
Otherwise the encrypted value in the database table will be decrypted and returned - or `None` if there is no configured secret.
|
|
122
123
|
|
|
124
|
+
The `last_used_at` column is updated every time a secret is accessed. The `last_used_by` column will be set to the actor ID passed to `get_secret()`, or `null` if no actor ID was passed.
|
|
123
125
|
|
|
124
126
|
## Development
|
|
125
127
|
|
|
@@ -13,7 +13,7 @@ MAX_NOTE_LENGTH = 100
|
|
|
13
13
|
pm.add_hookspecs(hookspecs)
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
async def get_secret(datasette, secret_name):
|
|
16
|
+
async def get_secret(datasette, secret_name, actor_id=None):
|
|
17
17
|
secrets_by_name = {secret.name: secret for secret in await get_secrets(datasette)}
|
|
18
18
|
if secret_name not in secrets_by_name:
|
|
19
19
|
return None
|
|
@@ -24,16 +24,31 @@ async def get_secret(datasette, secret_name):
|
|
|
24
24
|
# Now look it up in the database
|
|
25
25
|
config = get_config(datasette)
|
|
26
26
|
db = get_database(datasette)
|
|
27
|
-
|
|
27
|
+
db_secret = (
|
|
28
28
|
await db.execute(
|
|
29
|
-
"select encrypted from datasette_secrets where name = ? order by version desc limit 1",
|
|
29
|
+
"select id, encrypted from datasette_secrets where name = ? order by version desc limit 1",
|
|
30
30
|
(secret_name,),
|
|
31
31
|
)
|
|
32
32
|
).first()
|
|
33
|
-
if not
|
|
33
|
+
if not db_secret:
|
|
34
34
|
return None
|
|
35
35
|
key = Fernet(config["encryption_key"].encode("utf-8"))
|
|
36
|
-
decrypted = key.decrypt(
|
|
36
|
+
decrypted = key.decrypt(db_secret["encrypted"])
|
|
37
|
+
# Update the last used timestamp and actor_id
|
|
38
|
+
params = (actor_id, db_secret["id"])
|
|
39
|
+
if not actor_id:
|
|
40
|
+
params = (db_secret["id"],)
|
|
41
|
+
await db.execute_write(
|
|
42
|
+
"""
|
|
43
|
+
update datasette_secrets
|
|
44
|
+
set last_used_at = datetime('now'),
|
|
45
|
+
last_used_by = {}
|
|
46
|
+
where id = ?
|
|
47
|
+
""".format(
|
|
48
|
+
"?" if actor_id else "null"
|
|
49
|
+
),
|
|
50
|
+
params,
|
|
51
|
+
)
|
|
37
52
|
return decrypted.decode("utf-8")
|
|
38
53
|
|
|
39
54
|
|
|
@@ -51,7 +66,6 @@ create table if not exists datasette_secrets (
|
|
|
51
66
|
version integer not null default 1,
|
|
52
67
|
encrypted blob,
|
|
53
68
|
encryption_key_name text not null,
|
|
54
|
-
redacted text,
|
|
55
69
|
created_at text,
|
|
56
70
|
created_by text,
|
|
57
71
|
updated_at text,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: datasette-secrets
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.1a1
|
|
4
4
|
Summary: Manage secrets such as API keys for use with other Datasette plugins
|
|
5
5
|
Author: Datasette
|
|
6
6
|
License: Apache-2.0
|
|
@@ -135,12 +135,14 @@ To obtain the current value of the secret, use the `await get_secret()` method:
|
|
|
135
135
|
```python
|
|
136
136
|
from datasette_secrets import get_secret
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
# Third argument is the actor_id, optional
|
|
139
|
+
secret = await get_secret(datasette, "OPENAI_API_KEY", "root")
|
|
139
140
|
```
|
|
140
141
|
If the Datasette administrator set a `DATASETTE_SECRETS_OPENAI_API_KEY` environment variable, that will be returned.
|
|
141
142
|
|
|
142
143
|
Otherwise the encrypted value in the database table will be decrypted and returned - or `None` if there is no configured secret.
|
|
143
144
|
|
|
145
|
+
The `last_used_at` column is updated every time a secret is accessed. The `last_used_by` column will be set to the actor ID passed to `get_secret()`, or `null` if no actor ID was passed.
|
|
144
146
|
|
|
145
147
|
## Development
|
|
146
148
|
|
|
@@ -88,7 +88,6 @@ async def test_set_secret(ds):
|
|
|
88
88
|
"version": 1,
|
|
89
89
|
"encrypted": ANY,
|
|
90
90
|
"encryption_key_name": "default",
|
|
91
|
-
"redacted": None,
|
|
92
91
|
"created_at": ANY,
|
|
93
92
|
"created_by": "admin",
|
|
94
93
|
"updated_at": ANY,
|
|
@@ -128,7 +127,6 @@ async def test_set_secret(ds):
|
|
|
128
127
|
"version": 2,
|
|
129
128
|
"encrypted": ANY,
|
|
130
129
|
"encryption_key_name": "default",
|
|
131
|
-
"redacted": None,
|
|
132
130
|
"created_at": ANY,
|
|
133
131
|
"created_by": "admin",
|
|
134
132
|
"updated_at": ANY,
|
|
@@ -147,6 +145,11 @@ async def test_get_secret(ds, monkeypatch):
|
|
|
147
145
|
get_response = await ds.client.get("/-/secrets/EXAMPLE_SECRET", cookies=cookies)
|
|
148
146
|
csrftoken = get_response.cookies["ds_csrftoken"]
|
|
149
147
|
cookies["ds_csrftoken"] = csrftoken
|
|
148
|
+
db = ds.get_internal_database()
|
|
149
|
+
# Reset state
|
|
150
|
+
await db.execute_write(
|
|
151
|
+
"update datasette_secrets set last_used_at = null, last_used_by = null"
|
|
152
|
+
)
|
|
150
153
|
post_response = await ds.client.post(
|
|
151
154
|
"/-/secrets/EXAMPLE_SECRET",
|
|
152
155
|
cookies=cookies,
|
|
@@ -158,7 +161,27 @@ async def test_get_secret(ds, monkeypatch):
|
|
|
158
161
|
)
|
|
159
162
|
assert post_response.status_code == 302
|
|
160
163
|
|
|
164
|
+
assert await get_secret(ds, "EXAMPLE_SECRET", "actor") == "manually-set-secret"
|
|
165
|
+
|
|
166
|
+
# Should have updated last_used_at and last_used_by
|
|
167
|
+
secret = (
|
|
168
|
+
await db.execute(
|
|
169
|
+
"select * from datasette_secrets where name = ? order by version desc limit 1",
|
|
170
|
+
["EXAMPLE_SECRET"],
|
|
171
|
+
)
|
|
172
|
+
).first()
|
|
173
|
+
assert secret["last_used_by"] == "actor"
|
|
174
|
+
assert secret["last_used_at"] is not None
|
|
175
|
+
|
|
176
|
+
# Calling again without actor ID should set that to null
|
|
161
177
|
assert await get_secret(ds, "EXAMPLE_SECRET") == "manually-set-secret"
|
|
178
|
+
secret2 = (
|
|
179
|
+
await db.execute(
|
|
180
|
+
"select * from datasette_secrets where name = ? order by version desc limit 1",
|
|
181
|
+
["EXAMPLE_SECRET"],
|
|
182
|
+
)
|
|
183
|
+
).first()
|
|
184
|
+
assert secret2["last_used_by"] is None
|
|
162
185
|
|
|
163
186
|
# Now over-ride with an environment variable
|
|
164
187
|
monkeypatch.setenv("DATASETTE_SECRETS_EXAMPLE_SECRET", "from env")
|
|
File without changes
|
|
File without changes
|
{datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/datasette_secrets/templates/secrets_index.html
RENAMED
|
File without changes
|
{datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/datasette_secrets/templates/secrets_update.html
RENAMED
|
File without changes
|
|
File without changes
|
{datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/datasette_secrets.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/datasette_secrets.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
{datasette_secrets-0.1a0 → datasette_secrets-0.1a1}/datasette_secrets.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|