datasette-libfec 0.0.1a5__tar.gz → 0.0.1a6__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.
Files changed (43) hide show
  1. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/PKG-INFO +1 -1
  2. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/__init__.py +14 -2
  3. datasette_libfec-0.0.1a6/datasette_libfec/router.py +24 -0
  4. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/routes_export.py +7 -4
  5. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/routes_exports.py +5 -3
  6. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/routes_pages.py +8 -3
  7. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/routes_rss.py +11 -6
  8. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/routes_search.py +3 -2
  9. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec.egg-info/PKG-INFO +1 -1
  10. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/pyproject.toml +1 -1
  11. datasette_libfec-0.0.1a5/datasette_libfec/router.py +0 -3
  12. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/LICENSE +0 -0
  13. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/README.md +0 -0
  14. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/libfec_client.py +0 -0
  15. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/libfec_export_rpc_client.py +0 -0
  16. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/libfec_rpc_client.py +0 -0
  17. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/libfec_search_rpc_client.py +0 -0
  18. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/manifest.json +0 -0
  19. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/page_data.py +0 -0
  20. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/state.py +0 -0
  21. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/static/gen/candidate-BEqDafKu.css +0 -0
  22. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/static/gen/candidate-tqxa29G-.js +0 -0
  23. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/static/gen/class-C5DDKbJD.js +0 -0
  24. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/static/gen/committee-Bmki9iKb.css +0 -0
  25. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/static/gen/committee-DY1GmylW.js +0 -0
  26. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/static/gen/contest-BbYrzKRg.js +0 -0
  27. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/static/gen/contest-D4Fj7kGA.css +0 -0
  28. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/static/gen/each-DkfQbqzj.js +0 -0
  29. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/static/gen/filing_detail-Ba6_iQwV.css +0 -0
  30. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/static/gen/filing_detail-D2ib3OM6.js +0 -0
  31. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/static/gen/index-AHqus2fd.js +0 -0
  32. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/static/gen/index-client-CDwZ_Ixa.js +0 -0
  33. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/static/gen/index-jv9_YIKt.css +0 -0
  34. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/static/gen/load-AXKAVXVj.js +0 -0
  35. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec/templates/libfec_base.html +0 -0
  36. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec.egg-info/SOURCES.txt +0 -0
  37. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec.egg-info/dependency_links.txt +0 -0
  38. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec.egg-info/entry_points.txt +0 -0
  39. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec.egg-info/requires.txt +0 -0
  40. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/datasette_libfec.egg-info/top_level.txt +0 -0
  41. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/scripts/typegen-pagedata.py +0 -0
  42. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/setup.cfg +0 -0
  43. {datasette_libfec-0.0.1a5 → datasette_libfec-0.0.1a6}/tests/test_libfec.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datasette-libfec
3
- Version: 0.0.1a5
3
+ Version: 0.0.1a6
4
4
  Author: Alex Garcia
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Homepage, https://github.com/datasette/datasette-libfec
@@ -1,5 +1,6 @@
1
1
  from pydantic import BaseModel
2
2
  from datasette import hookimpl
3
+ from datasette.permissions import Action
3
4
  from pathlib import Path
4
5
  from textwrap import dedent
5
6
  from typing import Optional
@@ -9,7 +10,7 @@ import os
9
10
  # Import route modules to trigger route registration on the shared router
10
11
  # pylint: disable=unused-import
11
12
  from . import routes_rss, routes_export, routes_search, routes_exports, routes_pages
12
- from .router import router
13
+ from .router import router, LIBFEC_ACCESS_NAME
13
14
  _ = routes_rss, routes_export, routes_search, routes_exports, routes_pages
14
15
 
15
16
  # https://vite.dev/guide/backend-integration.html
@@ -81,4 +82,15 @@ def extra_template_vars(datasette):
81
82
  return "\n".join(parts)
82
83
 
83
84
 
84
- return {"datasette_libfec_vite_entry": datasette_libfec_vite_entry}
85
+ return {"datasette_libfec_vite_entry": datasette_libfec_vite_entry}
86
+
87
+ @hookimpl
88
+ def register_actions(datasette):
89
+ return [
90
+ Action(
91
+ name=LIBFEC_ACCESS_NAME,
92
+ description=(
93
+ "Can access libfec pages, features"
94
+ ),
95
+ ),
96
+ ]
@@ -0,0 +1,24 @@
1
+ from datasette import Forbidden
2
+ from datasette_plugin_router import Router
3
+ from functools import wraps
4
+
5
+ router = Router()
6
+
7
+ LIBFEC_ACCESS_NAME = "datasette_libfec_access"
8
+
9
+ # decorator for routes, to ensure the proper permissions are checked
10
+ def check_permission():
11
+ def decorator(func):
12
+ @wraps(func)
13
+ async def wrapper(datasette, request, **kwargs):
14
+ result = await datasette.allowed(
15
+ action=LIBFEC_ACCESS_NAME, actor=request.actor
16
+ )
17
+ if not result:
18
+ raise Forbidden("Permission denied for datasette-libfec access")
19
+ return await func(datasette=datasette, request=request, **kwargs)
20
+
21
+ return wrapper
22
+
23
+ return decorator
24
+
@@ -5,7 +5,7 @@ from typing import Optional, List
5
5
  import asyncio
6
6
  import uuid
7
7
 
8
- from .router import router
8
+ from .router import router, check_permission
9
9
  from .state import libfec_client, export_state
10
10
 
11
11
 
@@ -24,7 +24,8 @@ class ExportResponse(BaseModel):
24
24
 
25
25
 
26
26
  @router.POST("/-/api/libfec/export/start", output=ExportResponse)
27
- async def export_start(datasette, params: Body[ExportStartParams]):
27
+ @check_permission()
28
+ async def export_start(datasette, request, params: Body[ExportStartParams]):
28
29
  if export_state.running:
29
30
  return Response.json({
30
31
  "status": "error",
@@ -72,7 +73,8 @@ async def export_start(datasette, params: Body[ExportStartParams]):
72
73
 
73
74
 
74
75
  @router.GET("/-/api/libfec/export/status", output=ExportResponse)
75
- async def export_status(datasette):
76
+ @check_permission()
77
+ async def export_status(datasette, request):
76
78
  response_data = {
77
79
  "status": "success",
78
80
  "message": "Export status",
@@ -101,7 +103,8 @@ async def export_status(datasette):
101
103
 
102
104
 
103
105
  @router.POST("/-/api/libfec/export/cancel", output=ExportResponse)
104
- async def export_cancel(datasette):
106
+ @check_permission()
107
+ async def export_cancel(datasette, request):
105
108
  if not export_state.running:
106
109
  return Response.json({
107
110
  "status": "error",
@@ -2,7 +2,7 @@ from pydantic import BaseModel
2
2
  from datasette import Response
3
3
  from typing import Optional, List, Literal
4
4
 
5
- from .router import router
5
+ from .router import router, check_permission
6
6
 
7
7
 
8
8
  class ExportRecord(BaseModel):
@@ -44,7 +44,8 @@ class ApiExportsListResponse(BaseModel):
44
44
  message: Optional[str] = None
45
45
 
46
46
  @router.GET("/-/api/libfec/exports$", output=ApiExportsListResponse)
47
- async def list_exports(datasette):
47
+ @check_permission()
48
+ async def list_exports(datasette, request):
48
49
  """List all export operations from the metadata tables"""
49
50
  db = datasette.get_database()
50
51
 
@@ -103,7 +104,8 @@ async def list_exports(datasette):
103
104
 
104
105
 
105
106
  @router.GET("/-/api/libfec/exports/(?P<export_id>\\d+)")
106
- async def get_export_detail(datasette, export_id: str):
107
+ @check_permission()
108
+ async def get_export_detail(datasette, request, export_id: str):
107
109
  """Get detailed information about a specific export"""
108
110
  db = datasette.get_database()
109
111
  export_id_int = int(export_id)
@@ -3,7 +3,7 @@ Routes for contest, candidate, and committee pages.
3
3
  """
4
4
  from datasette import Response
5
5
 
6
- from .router import router
6
+ from .router import router, check_permission
7
7
  from .page_data import (
8
8
  Candidate,
9
9
  CandidatePageData,
@@ -15,7 +15,8 @@ from .page_data import (
15
15
 
16
16
 
17
17
  @router.GET("/-/libfec$")
18
- async def libfec_page(datasette):
18
+ @check_permission()
19
+ async def libfec_page(datasette, request):
19
20
  db = datasette.get_database()
20
21
  page_data = IndexPageData(database_name=db.name)
21
22
  return Response.html(
@@ -31,7 +32,8 @@ async def libfec_page(datasette):
31
32
 
32
33
 
33
34
  @router.GET("/-/libfec/filing/(?P<filing_id>[^/]+)")
34
- async def filing_detail_page(datasette, filing_id: str):
35
+ @check_permission()
36
+ async def filing_detail_page(datasette, request, filing_id: str):
35
37
  db = datasette.get_database()
36
38
  filing = None
37
39
  form_data = None
@@ -98,6 +100,7 @@ async def filing_detail_page(datasette, filing_id: str):
98
100
 
99
101
 
100
102
  @router.GET("/-/libfec/contest$")
103
+ @check_permission()
101
104
  async def contest_page(datasette, request):
102
105
  """
103
106
  Contest page showing candidates for a specific race.
@@ -179,6 +182,7 @@ async def contest_page(datasette, request):
179
182
 
180
183
 
181
184
  @router.GET("/-/libfec/candidate/(?P<candidate_id>[^/]+)$")
185
+ @check_permission()
182
186
  async def candidate_page(datasette, request, candidate_id: str):
183
187
  """
184
188
  Candidate detail page.
@@ -258,6 +262,7 @@ async def candidate_page(datasette, request, candidate_id: str):
258
262
 
259
263
 
260
264
  @router.GET("/-/libfec/committee/(?P<committee_id>[^/]+)$")
265
+ @check_permission()
261
266
  async def committee_page(datasette, request, committee_id: str):
262
267
  """
263
268
  Committee detail page.
@@ -4,7 +4,7 @@ from datasette_plugin_router import Body
4
4
  from typing import Optional, List, Literal
5
5
  import asyncio
6
6
 
7
- from .router import router
7
+ from .router import router, check_permission
8
8
  from .state import libfec_client, rss_watcher_state
9
9
 
10
10
 
@@ -95,7 +95,8 @@ async def rss_watch_loop(
95
95
 
96
96
 
97
97
  @router.POST("/-/api/libfec/rss/start", output=RssResponse)
98
- async def rss_start(datasette, params: Body[RssStartParams]):
98
+ @check_permission()
99
+ async def rss_start(datasette, request, params: Body[RssStartParams]):
99
100
  if rss_watcher_state.running:
100
101
  return Response.json({
101
102
  "status": "error",
@@ -157,7 +158,8 @@ async def rss_start(datasette, params: Body[RssStartParams]):
157
158
 
158
159
 
159
160
  @router.POST("/-/api/libfec/rss/stop", output=RssResponse)
160
- async def rss_stop(datasette):
161
+ @check_permission()
162
+ async def rss_stop(datasette, request):
161
163
  if not rss_watcher_state.running:
162
164
  return Response.json({
163
165
  "status": "error",
@@ -192,7 +194,8 @@ async def rss_stop(datasette):
192
194
 
193
195
 
194
196
  @router.GET("/-/api/libfec/rss/status", output=RssResponse)
195
- async def rss_status(datasette):
197
+ @check_permission()
198
+ async def rss_status(datasette, request):
196
199
  config = None
197
200
  if rss_watcher_state.running:
198
201
  config = {
@@ -226,7 +229,8 @@ async def rss_status(datasette):
226
229
 
227
230
 
228
231
  @router.GET("/-/api/libfec/rss/syncs$", output=ApiRssSyncsListResponse)
229
- async def list_rss_syncs(datasette):
232
+ @check_permission()
233
+ async def list_rss_syncs(datasette, request):
230
234
  """List all RSS sync operations from the metadata tables"""
231
235
  db = datasette.get_database()
232
236
 
@@ -304,7 +308,8 @@ async def list_rss_syncs(datasette):
304
308
 
305
309
 
306
310
  @router.GET("/-/api/libfec/rss/syncs/(?P<sync_id>\\d+)")
307
- async def get_rss_sync_detail(datasette, sync_id: str):
311
+ @check_permission()
312
+ async def get_rss_sync_detail(datasette, request, sync_id: str):
308
313
  """Get detailed information about a specific RSS sync"""
309
314
  db = datasette.get_database()
310
315
  sync_id_int = int(sync_id)
@@ -3,7 +3,7 @@ from datasette import Response
3
3
  from datasette_plugin_router import Body
4
4
  from typing import Optional, List
5
5
 
6
- from .router import router
6
+ from .router import router, check_permission
7
7
  from .state import libfec_client
8
8
 
9
9
  # Cache for search RPC clients (keyed by cycle)
@@ -27,7 +27,8 @@ class SearchResponse(BaseModel):
27
27
 
28
28
 
29
29
  @router.POST("/-/api/libfec/search", output=SearchResponse)
30
- async def search(datasette, params: Body[SearchParams]):
30
+ @check_permission()
31
+ async def search(datasette, request, params: Body[SearchParams]):
31
32
  """Search for candidates and committees using libfec search --rpc"""
32
33
  from .libfec_search_rpc_client import LibfecSearchRpcClient, RpcError
33
34
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datasette-libfec
3
- Version: 0.0.1a5
3
+ Version: 0.0.1a6
4
4
  Author: Alex Garcia
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Homepage, https://github.com/datasette/datasette-libfec
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "datasette-libfec"
3
- version = "0.0.1a5"
3
+ version = "0.0.1a6"
4
4
  description = ""
5
5
  readme = "README.md"
6
6
  authors = [{name = "Alex Garcia"}]
@@ -1,3 +0,0 @@
1
- from datasette_plugin_router import Router
2
-
3
- router = Router()