datasette-secrets 0.1a1__py3-none-any.whl → 0.1a3__py3-none-any.whl

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.

@@ -3,7 +3,7 @@ from cryptography.fernet import Fernet
3
3
  import dataclasses
4
4
  from datasette import hookimpl, Forbidden, Permission, Response
5
5
  from datasette.plugins import pm
6
- from datasette.utils import await_me_maybe
6
+ from datasette.utils import await_me_maybe, sqlite3
7
7
  import os
8
8
  from typing import Optional
9
9
  from . import hookspecs
@@ -24,12 +24,15 @@ async def get_secret(datasette, secret_name, actor_id=None):
24
24
  # Now look it up in the database
25
25
  config = get_config(datasette)
26
26
  db = get_database(datasette)
27
- db_secret = (
28
- await db.execute(
29
- "select id, encrypted from datasette_secrets where name = ? order by version desc limit 1",
30
- (secret_name,),
31
- )
32
- ).first()
27
+ try:
28
+ db_secret = (
29
+ await db.execute(
30
+ "select id, encrypted from datasette_secrets where name = ? order by version desc limit 1",
31
+ (secret_name,),
32
+ )
33
+ ).first()
34
+ except sqlite3.OperationalError:
35
+ return None
33
36
  if not db_secret:
34
37
  return None
35
38
  key = Fernet(config["encryption_key"].encode("utf-8"))
@@ -55,7 +58,9 @@ async def get_secret(datasette, secret_name, actor_id=None):
55
58
  @dataclasses.dataclass
56
59
  class Secret:
57
60
  name: str
58
- description_html: Optional[str] = None
61
+ description: Optional[str] = None
62
+ obtain_url: Optional[str] = None
63
+ obtain_label: Optional[str] = None
59
64
 
60
65
 
61
66
  SCHEMA = """
@@ -114,25 +119,20 @@ def register_permissions(datasette):
114
119
 
115
120
  async def get_secrets(datasette):
116
121
  secrets = []
122
+ seen = set()
117
123
  for result in pm.hook.register_secrets(datasette=datasette):
118
124
  result = await await_me_maybe(result)
119
- secrets.extend(result)
125
+ for secret in result:
126
+ if secret.name in seen:
127
+ continue # Skip duplicates
128
+ seen.add(secret.name)
129
+ secrets.append(secret)
120
130
  # if not secrets:
121
131
  secrets.append(Secret("EXAMPLE_SECRET", "An example secret"))
122
132
 
123
133
  return secrets
124
134
 
125
135
 
126
- @hookimpl
127
- def register_secrets():
128
- return [
129
- Secret(
130
- "OPENAI_API_KEY",
131
- 'An OpenAI API key. Get them from <a href="https://platform.openai.com/api-keys">here</a>.',
132
- ),
133
- ]
134
-
135
-
136
136
  @hookimpl
137
137
  def register_commands(cli):
138
138
  @cli.group()
@@ -183,6 +183,16 @@ async def secrets_index(datasette, request):
183
183
  list(environment_secrets_names),
184
184
  )
185
185
  existing_secrets = {row["name"]: dict(row) for row in existing_secrets_result.rows}
186
+ # Try to turn updated_by into actors
187
+ actors = await datasette.actors_from_ids(
188
+ {row["updated_by"] for row in existing_secrets.values() if row["updated_by"]}
189
+ )
190
+ for secret in existing_secrets.values():
191
+ if secret["updated_by"]:
192
+ actor = actors.get(secret["updated_by"])
193
+ if actor:
194
+ display = actor.get("username") or actor.get("name") or actor.get("id")
195
+ secret["updated_by"] = display
186
196
  unset_secrets = [
187
197
  secret
188
198
  for secret in all_secrets
@@ -25,7 +25,10 @@
25
25
  <ul>
26
26
  {% for secret in unset_secrets %}
27
27
  <li><strong><a href="{{ urls.path("/-/secrets/") }}{{ secret.name }}">{{ secret.name }}</a></strong>
28
- {% if secret.description_html %} - {{ secret.description_html|safe }}{% endif %}</li>
28
+ {% if secret.description or secret.obtain_label %}
29
+ - {{ secret.description or "" }}{% if secret.description and secret.obtain_label %}, {% endif %}
30
+ {% if secret.obtain_label %}<a href="{{ secret.obtain_url }}">{{ secret.obtain_label }}</a>{% endif %}
31
+ {% endif %}</li>
29
32
  {% endfor %}
30
33
  </ul>
31
34
  {% endif %}
@@ -34,7 +37,7 @@
34
37
  <p style="margin-top: 2em">The following secret{% if environment_secrets|length == 1 %} is{% else %}s are{% endif %} set using environment variables:</p>
35
38
  <ul>
36
39
  {% for secret in environment_secrets %}
37
- <li><strong>{{ secret.name }}</a></strong>- {{ secret.description_html|safe }}<br>
40
+ <li><strong>{{ secret.name }}</a></strong>{% if secret.description %} - {{ secret.description }}{% endif %}<br>
38
41
  <span style="font-size: 0.8 em">Set by <code>DATASETTE_SECRETS_{{ secret.name }}</code></span></li>
39
42
  {% endfor %}
40
43
  </ul>
@@ -9,8 +9,10 @@
9
9
  {% block content %}
10
10
  <h1>{% if current_secret %}Update{% else %}Add{% endif %} secret: {{ secret_name }}</h1>
11
11
 
12
- {% if secret_details and secret_details.description_html %}
13
- <p>{{ secret_details.description_html|safe }}</p>
12
+ {% if secret_details.description or secret_details.obtain_label %}
13
+ <p>{{ secret_details.description or "" }}{% if secret_details.description and secret_details.obtain_label %}. {% endif %}
14
+ {% if secret_details.obtain_label %}<a href="{{ secret_details.obtain_url }}">{{ secret_details.obtain_label }}</a>{% endif %}
15
+ </p>
14
16
  {% endif %}
15
17
 
16
18
  {% if error %}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: datasette-secrets
3
- Version: 0.1a1
3
+ Version: 0.1a3
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
@@ -105,6 +105,8 @@ datasette data.db --internal internal.db \
105
105
 
106
106
  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`.
107
107
 
108
+ 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`.
109
+
108
110
  ## For plugin authors
109
111
 
110
112
  Plugins can depend on this plugin if they want to implement secrets.
@@ -121,11 +123,24 @@ from datasette_secrets import Secret
121
123
  def register_secrets():
122
124
  return [
123
125
  Secret(
124
- "OPENAI_API_KEY",
125
- 'An OpenAI API key. Get them from <a href="https://platform.openai.com/api-keys">here</a>.',
126
+ name="OPENAI_API_KEY",
127
+ description="An OpenAI API key"
128
+ ),
129
+ ]
130
+ ```
131
+ You can also provide optional `obtain_url` and `obtain_label` fields to link to a page where a user can obtain an API key:
132
+ ```python
133
+ @hookimpl
134
+ def register_secrets():
135
+ return [
136
+ Secret(
137
+ name="OPENAI_API_KEY",
138
+ obtain_url="https://platform.openai.com/api-keys",
139
+ obtain_label="Get an OpenAI API key"
126
140
  ),
127
141
  ]
128
142
  ```
143
+
129
144
  The hook can take an optional `datasette` argument. It can return a list or an `async def` function that, when awaited, returns a list.
130
145
 
131
146
  The list should consist of `Secret()` instances, each with a name and an optional description. The description can contain HTML.
@@ -0,0 +1,10 @@
1
+ datasette_secrets/__init__.py,sha256=9OBdUnpRpA6nQOoh-gerQO4vpGclFwTevyprvSDaPn8,10508
2
+ datasette_secrets/hookspecs.py,sha256=57v14e2Y4o5eZyAgCLpuzp1KZn7CjwLXeKwfq6Zvux8,205
3
+ datasette_secrets/templates/secrets_index.html,sha256=ZgIy_huFZQfnI6GjO0qauWEkTWhkBCM5WrT73Nqq4BY,1737
4
+ datasette_secrets/templates/secrets_update.html,sha256=qMPLVCuKKslqw_In0aSCVobB3maPw9oaYZDQQImdRIU,1678
5
+ datasette_secrets-0.1a3.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
6
+ datasette_secrets-0.1a3.dist-info/METADATA,sha256=pVAqjHANB1qUe6ltVsV0-JEqFeTGbKsLO_jx17z7xlU,7040
7
+ datasette_secrets-0.1a3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
8
+ datasette_secrets-0.1a3.dist-info/entry_points.txt,sha256=2083uWbPpGntxRulh8_hVaelQO-xdtjedG6rGzwPUH0,40
9
+ datasette_secrets-0.1a3.dist-info/top_level.txt,sha256=ZBJKQk-DdDU9Vnwu4x79X9aaEulwGJMoLx62IZJPDaQ,18
10
+ datasette_secrets-0.1a3.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- datasette_secrets/__init__.py,sha256=u5Hu7mYtbszNmzTyBmsQoyg2V0aS9rJs_NdanZQmgGg,9923
2
- datasette_secrets/hookspecs.py,sha256=57v14e2Y4o5eZyAgCLpuzp1KZn7CjwLXeKwfq6Zvux8,205
3
- datasette_secrets/templates/secrets_index.html,sha256=Zr8wmVngxzABB69hM052W8IOHgGIlJLM9KKnDxAYmQI,1510
4
- datasette_secrets/templates/secrets_update.html,sha256=SQs_TmrCw-eanePGiEnRiqH6OKy_G5iJJcEfg_z6RFg,1463
5
- datasette_secrets-0.1a1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
6
- datasette_secrets-0.1a1.dist-info/METADATA,sha256=UneJmupjhs_jQAB20WaCsQvAWqfH0YIMombKkjQXN50,6417
7
- datasette_secrets-0.1a1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
8
- datasette_secrets-0.1a1.dist-info/entry_points.txt,sha256=2083uWbPpGntxRulh8_hVaelQO-xdtjedG6rGzwPUH0,40
9
- datasette_secrets-0.1a1.dist-info/top_level.txt,sha256=ZBJKQk-DdDU9Vnwu4x79X9aaEulwGJMoLx62IZJPDaQ,18
10
- datasette_secrets-0.1a1.dist-info/RECORD,,