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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: datasette-secrets
3
- Version: 0.1a0
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
- secret = await get_secret(datasette, "OPENAI_API_KEY")
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
- secret = await get_secret(datasette, "OPENAI_API_KEY")
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
- encrypted = (
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 encrypted:
33
+ if not db_secret:
34
34
  return None
35
35
  key = Fernet(config["encryption_key"].encode("utf-8"))
36
- decrypted = key.decrypt(encrypted["encrypted"])
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.1a0
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
- secret = await get_secret(datasette, "OPENAI_API_KEY")
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
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "datasette-secrets"
3
- version = "0.1a0"
3
+ version = "0.1a1"
4
4
  description = "Manage secrets such as API keys for use with other Datasette plugins"
5
5
  readme = "README.md"
6
6
  authors = [{name = "Datasette"}]
@@ -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")