datasette-secrets 0.1a4__tar.gz → 0.3a0__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.
@@ -1,23 +1,24 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: datasette-secrets
3
- Version: 0.1a4
3
+ Version: 0.3a0
4
4
  Summary: Manage secrets such as API keys for use with other Datasette plugins
5
5
  Author: Datasette
6
- License: Apache-2.0
6
+ License-Expression: Apache-2.0
7
7
  Project-URL: Homepage, https://github.com/datasette/datasette-secrets
8
8
  Project-URL: Changelog, https://github.com/datasette/datasette-secrets/releases
9
9
  Project-URL: Issues, https://github.com/datasette/datasette-secrets/issues
10
10
  Project-URL: CI, https://github.com/datasette/datasette-secrets/actions
11
11
  Classifier: Framework :: Datasette
12
- Classifier: License :: OSI Approved :: Apache Software License
13
- Requires-Python: >=3.8
12
+ Requires-Python: >=3.10
14
13
  Description-Content-Type: text/markdown
15
14
  License-File: LICENSE
16
- Requires-Dist: datasette>=1.0a13
15
+ Requires-Dist: datasette>=1.0a21
17
16
  Requires-Dist: cryptography
18
17
  Provides-Extra: test
19
18
  Requires-Dist: pytest; extra == "test"
20
19
  Requires-Dist: pytest-asyncio; extra == "test"
20
+ Requires-Dist: datasette-test>=0.3.2; extra == "test"
21
+ Dynamic: license-file
21
22
 
22
23
  # datasette-secrets
23
24
 
@@ -57,7 +58,7 @@ Configure the plugin with these these two plugin settings:
57
58
  ```yaml
58
59
  plugins:
59
60
  datasette-secrets:
60
- encryption_key:
61
+ encryption-key:
61
62
  $env: DATASETTE_SECRETS_ENCRYPTION_KEY
62
63
  database: name_of_database
63
64
  ```
@@ -36,7 +36,7 @@ Configure the plugin with these these two plugin settings:
36
36
  ```yaml
37
37
  plugins:
38
38
  datasette-secrets:
39
- encryption_key:
39
+ encryption-key:
40
40
  $env: DATASETTE_SECRETS_ENCRYPTION_KEY
41
41
  database: name_of_database
42
42
  ```
@@ -1,7 +1,8 @@
1
1
  import click
2
2
  from cryptography.fernet import Fernet
3
3
  import dataclasses
4
- from datasette import hookimpl, Forbidden, Permission, Response
4
+ from datasette import hookimpl, Forbidden, Response
5
+ from datasette.permissions import Action
5
6
  from datasette.plugins import pm
6
7
  from datasette.utils import await_me_maybe, sqlite3
7
8
  import os
@@ -89,7 +90,7 @@ create table if not exists datasette_secrets (
89
90
  def get_database(datasette):
90
91
  plugin_config = datasette.plugin_config("datasette-secrets") or {}
91
92
  database = plugin_config.get("database") or "_internal"
92
- if database == "_internal":
93
+ if database == "_internal" and hasattr(datasette, "get_internal_database"):
93
94
  return datasette.get_internal_database()
94
95
  return datasette.get_database(database)
95
96
 
@@ -107,15 +108,11 @@ def get_config(datasette):
107
108
 
108
109
 
109
110
  @hookimpl
110
- def register_permissions(datasette):
111
+ def register_actions(datasette):
111
112
  return [
112
- Permission(
113
+ Action(
113
114
  name="manage-secrets",
114
- abbr=None,
115
115
  description="Manage Datasette secrets",
116
- takes_database=False,
117
- takes_resource=False,
118
- default=False,
119
116
  )
120
117
  ]
121
118
 
@@ -163,7 +160,10 @@ def startup(datasette):
163
160
 
164
161
 
165
162
  async def secrets_index(datasette, request):
166
- if not await datasette.permission_allowed(request.actor, "manage-secrets"):
163
+ if not await datasette.allowed(
164
+ action="manage-secrets",
165
+ actor=request.actor,
166
+ ):
167
167
  raise Forbidden("Permission denied")
168
168
  all_secrets = await get_secrets(datasette)
169
169
 
@@ -187,15 +187,22 @@ async def secrets_index(datasette, request):
187
187
  )
188
188
  existing_secrets = {row["name"]: dict(row) for row in existing_secrets_result.rows}
189
189
  # Try to turn updated_by into actors
190
- actors = await datasette.actors_from_ids(
191
- {row["updated_by"] for row in existing_secrets.values() if row["updated_by"]}
192
- )
193
- for secret in existing_secrets.values():
194
- if secret["updated_by"]:
195
- actor = actors.get(secret["updated_by"])
196
- if actor:
197
- display = actor.get("username") or actor.get("name") or actor.get("id")
198
- secret["updated_by"] = display
190
+ if hasattr(datasette, "actors_from_ids"):
191
+ actors = await datasette.actors_from_ids(
192
+ {
193
+ row["updated_by"]
194
+ for row in existing_secrets.values()
195
+ if row["updated_by"]
196
+ }
197
+ )
198
+ for secret in existing_secrets.values():
199
+ if secret["updated_by"]:
200
+ actor = actors.get(secret["updated_by"])
201
+ if actor:
202
+ display = (
203
+ actor.get("username") or actor.get("name") or actor.get("id")
204
+ )
205
+ secret["updated_by"] = display
199
206
  unset_secrets = [
200
207
  secret
201
208
  for secret in all_secrets
@@ -216,7 +223,10 @@ async def secrets_index(datasette, request):
216
223
 
217
224
 
218
225
  async def secrets_update(datasette, request):
219
- if not await datasette.permission_allowed(request.actor, "manage-secrets"):
226
+ if not await datasette.allowed(
227
+ action="manage-secrets",
228
+ actor=request.actor,
229
+ ):
220
230
  raise Forbidden("Permission denied")
221
231
  plugin_config = get_config(datasette)
222
232
  if not plugin_config:
@@ -340,7 +350,10 @@ def menu_links(datasette, actor):
340
350
  return
341
351
 
342
352
  async def inner():
343
- if not await datasette.permission_allowed(actor, "manage-secrets"):
353
+ if not await datasette.allowed(
354
+ action="manage-secrets",
355
+ actor=actor,
356
+ ):
344
357
  return
345
358
  return [
346
359
  {"href": datasette.urls.path("/-/secrets"), "label": "Manage secrets"},
@@ -1,23 +1,24 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: datasette-secrets
3
- Version: 0.1a4
3
+ Version: 0.3a0
4
4
  Summary: Manage secrets such as API keys for use with other Datasette plugins
5
5
  Author: Datasette
6
- License: Apache-2.0
6
+ License-Expression: Apache-2.0
7
7
  Project-URL: Homepage, https://github.com/datasette/datasette-secrets
8
8
  Project-URL: Changelog, https://github.com/datasette/datasette-secrets/releases
9
9
  Project-URL: Issues, https://github.com/datasette/datasette-secrets/issues
10
10
  Project-URL: CI, https://github.com/datasette/datasette-secrets/actions
11
11
  Classifier: Framework :: Datasette
12
- Classifier: License :: OSI Approved :: Apache Software License
13
- Requires-Python: >=3.8
12
+ Requires-Python: >=3.10
14
13
  Description-Content-Type: text/markdown
15
14
  License-File: LICENSE
16
- Requires-Dist: datasette>=1.0a13
15
+ Requires-Dist: datasette>=1.0a21
17
16
  Requires-Dist: cryptography
18
17
  Provides-Extra: test
19
18
  Requires-Dist: pytest; extra == "test"
20
19
  Requires-Dist: pytest-asyncio; extra == "test"
20
+ Requires-Dist: datasette-test>=0.3.2; extra == "test"
21
+ Dynamic: license-file
21
22
 
22
23
  # datasette-secrets
23
24
 
@@ -57,7 +58,7 @@ Configure the plugin with these these two plugin settings:
57
58
  ```yaml
58
59
  plugins:
59
60
  datasette-secrets:
60
- encryption_key:
61
+ encryption-key:
61
62
  $env: DATASETTE_SECRETS_ENCRYPTION_KEY
62
63
  database: name_of_database
63
64
  ```
@@ -1,6 +1,7 @@
1
- datasette>=1.0a13
1
+ datasette>=1.0a21
2
2
  cryptography
3
3
 
4
4
  [test]
5
5
  pytest
6
6
  pytest-asyncio
7
+ datasette-test>=0.3.2
@@ -1,17 +1,16 @@
1
1
  [project]
2
2
  name = "datasette-secrets"
3
- version = "0.1a4"
3
+ version = "0.3a0"
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"}]
7
- license = {text = "Apache-2.0"}
7
+ license = "Apache-2.0"
8
8
  classifiers=[
9
- "Framework :: Datasette",
10
- "License :: OSI Approved :: Apache Software License"
9
+ "Framework :: Datasette"
11
10
  ]
12
- requires-python = ">=3.8"
11
+ requires-python = ">=3.10"
13
12
  dependencies = [
14
- "datasette>=1.0a13",
13
+ "datasette>=1.0a21",
15
14
  "cryptography"
16
15
  ]
17
16
 
@@ -25,7 +24,7 @@ CI = "https://github.com/datasette/datasette-secrets/actions"
25
24
  secrets = "datasette_secrets"
26
25
 
27
26
  [project.optional-dependencies]
28
- test = ["pytest", "pytest-asyncio"]
27
+ test = ["pytest", "pytest-asyncio", "datasette-test>=0.3.2"]
29
28
 
30
29
  [tool.pytest.ini_options]
31
30
  asyncio_mode = "strict"
@@ -1,9 +1,9 @@
1
1
  from click.testing import CliRunner
2
2
  from cryptography.fernet import Fernet
3
3
  from datasette import hookimpl
4
- from datasette.app import Datasette
5
4
  from datasette.cli import cli
6
5
  from datasette.plugins import pm
6
+ from datasette_test import Datasette, actor_cookie
7
7
  from datasette_secrets import get_secret, Secret, startup, get_config
8
8
  import pytest
9
9
  from unittest.mock import ANY
@@ -11,6 +11,13 @@ from unittest.mock import ANY
11
11
  TEST_ENCRYPTION_KEY = "-LujHtwFWGaBpznrV1zduoZBmCnMOW7J0H5hmeXgAVo="
12
12
 
13
13
 
14
+ def get_internal_database(ds):
15
+ if hasattr(ds, "get_internal_database"):
16
+ return ds.get_internal_database()
17
+ else:
18
+ return ds.get_database("_internal")
19
+
20
+
14
21
  def test_generate_command():
15
22
  runner = CliRunner()
16
23
  result = runner.invoke(cli, ["secrets", "generate-encryption-key"])
@@ -89,15 +96,13 @@ def register_multiple_secrets():
89
96
  @pytest.fixture
90
97
  def ds():
91
98
  return Datasette(
92
- config={
93
- "plugins": {
94
- "datasette-secrets": {
95
- "database": "_internal",
96
- "encryption-key": TEST_ENCRYPTION_KEY,
97
- }
98
- },
99
- "permissions": {"manage-secrets": {"id": "admin"}},
100
- }
99
+ plugin_config={
100
+ "datasette-secrets": {
101
+ "database": "_internal",
102
+ "encryption-key": TEST_ENCRYPTION_KEY,
103
+ }
104
+ },
105
+ permissions={"manage-secrets": {"id": "admin"}},
101
106
  )
102
107
 
103
108
 
@@ -115,7 +120,7 @@ async def test_permissions(ds, path, verb, data, user):
115
120
  kwargs = {}
116
121
  if user:
117
122
  kwargs["cookies"] = {
118
- "ds_actor": ds.client.actor_cookie({"id": user}),
123
+ "ds_actor": actor_cookie(ds, {"id": user}),
119
124
  }
120
125
  if data:
121
126
  kwargs["data"] = data
@@ -131,7 +136,7 @@ async def test_permissions(ds, path, verb, data, user):
131
136
 
132
137
  @pytest.mark.asyncio
133
138
  async def test_set_secret(ds, use_actors_plugin):
134
- cookies = {"ds_actor": ds.client.actor_cookie({"id": "admin"})}
139
+ cookies = {"ds_actor": actor_cookie(ds, {"id": "admin"})}
135
140
  get_response = await ds.client.get("/-/secrets/EXAMPLE_SECRET", cookies=cookies)
136
141
  csrftoken = get_response.cookies["ds_csrftoken"]
137
142
  cookies["ds_csrftoken"] = csrftoken
@@ -142,7 +147,7 @@ async def test_set_secret(ds, use_actors_plugin):
142
147
  )
143
148
  assert post_response.status_code == 302
144
149
  assert post_response.headers["Location"] == "/-/secrets"
145
- internal_db = ds.get_internal_database()
150
+ internal_db = get_internal_database(ds)
146
151
  secrets = await internal_db.execute("select * from datasette_secrets")
147
152
  rows = [dict(r) for r in secrets.rows]
148
153
  assert rows == [
@@ -174,7 +179,12 @@ async def test_set_secret(ds, use_actors_plugin):
174
179
  assert response.status_code == 200
175
180
  assert "EXAMPLE_SECRET" in response.text
176
181
  assert "new-note" in response.text
177
- assert "<td>ADMIN</td>" in response.text
182
+
183
+ if hasattr(ds, "actors_from_ids"):
184
+ assert "<td>ADMIN</td>" in response.text
185
+ else:
186
+ # Pre 1.0, so can't use that mechanism
187
+ assert "<td>admin</td>" in response.text
178
188
 
179
189
  # Now let's edit it
180
190
  post_response2 = await ds.client.post(
@@ -213,11 +223,11 @@ async def test_set_secret(ds, use_actors_plugin):
213
223
  @pytest.mark.asyncio
214
224
  async def test_get_secret(ds, monkeypatch):
215
225
  # First set it manually
216
- cookies = {"ds_actor": ds.client.actor_cookie({"id": "admin"})}
226
+ cookies = {"ds_actor": actor_cookie(ds, {"id": "admin"})}
217
227
  get_response = await ds.client.get("/-/secrets/EXAMPLE_SECRET", cookies=cookies)
218
228
  csrftoken = get_response.cookies["ds_csrftoken"]
219
229
  cookies["ds_csrftoken"] = csrftoken
220
- db = ds.get_internal_database()
230
+ db = get_internal_database(ds)
221
231
  # Reset state
222
232
  await db.execute_write(
223
233
  "update datasette_secrets set last_used_at = null, last_used_by = null"
@@ -288,7 +298,7 @@ async def test_secret_index_page(ds, register_multiple_secrets):
288
298
  response = await ds.client.get(
289
299
  "/-/secrets",
290
300
  cookies={
291
- "ds_actor": ds.client.actor_cookie({"id": "admin"}),
301
+ "ds_actor": actor_cookie(ds, {"id": "admin"}),
292
302
  },
293
303
  )
294
304
  assert response.status_code == 200