supython 0.1.2__tar.gz → 0.1.4__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 (254) hide show
  1. {supython-0.1.2 → supython-0.1.4}/PKG-INFO +66 -3
  2. {supython-0.1.2 → supython-0.1.4}/README.md +65 -2
  3. {supython-0.1.2 → supython-0.1.4}/pyproject.toml +1 -1
  4. {supython-0.1.2 → supython-0.1.4}/src/supython/app.py +2 -0
  5. supython-0.1.4/src/supython/auth/__init__.py +4 -0
  6. supython-0.1.4/src/supython/auth/claims.py +72 -0
  7. supython-0.1.4/src/supython/auth/deps.py +57 -0
  8. {supython-0.1.2 → supython-0.1.4}/src/supython/auth/service.py +9 -2
  9. supython-0.1.2/src/supython/auth/__init__.py +0 -3
  10. {supython-0.1.2 → supython-0.1.4}/.gitignore +0 -0
  11. {supython-0.1.2 → supython-0.1.4}/CHANGELOG.md +0 -0
  12. {supython-0.1.2 → supython-0.1.4}/LICENSE +0 -0
  13. {supython-0.1.2 → supython-0.1.4}/SECURITY.md +0 -0
  14. {supython-0.1.2 → supython-0.1.4}/src/supython/__init__.py +0 -0
  15. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/__init__.py +0 -0
  16. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/__init__.py +0 -0
  17. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/auth.py +0 -0
  18. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/auth_templates.py +0 -0
  19. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/auth_users.py +0 -0
  20. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/db.py +0 -0
  21. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/functions.py +0 -0
  22. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/jobs.py +0 -0
  23. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/ops.py +0 -0
  24. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/realtime.py +0 -0
  25. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/service_auth.py +0 -0
  26. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/service_auth_templates.py +0 -0
  27. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/service_auth_users.py +0 -0
  28. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/service_db.py +0 -0
  29. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/service_functions.py +0 -0
  30. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/service_jobs.py +0 -0
  31. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/service_ops.py +0 -0
  32. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/service_realtime.py +0 -0
  33. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/service_storage.py +0 -0
  34. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/storage.py +0 -0
  35. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/api/system.py +0 -0
  36. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/audit.py +0 -0
  37. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/deps.py +0 -0
  38. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/errors.py +0 -0
  39. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/schemas.py +0 -0
  40. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/session.py +0 -0
  41. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/spa.py +0 -0
  42. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Alert-dluGVkos.js +0 -0
  43. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Alert-dluGVkos.js.map +0 -0
  44. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Audit-Njung3HI.js +0 -0
  45. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Audit-Njung3HI.js.map +0 -0
  46. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Backups-DzPlFgrm.js +0 -0
  47. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Backups-DzPlFgrm.js.map +0 -0
  48. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Buckets-ByacGkU1.js +0 -0
  49. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Buckets-ByacGkU1.js.map +0 -0
  50. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Channels-BoIuTtam.js +0 -0
  51. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Channels-BoIuTtam.js.map +0 -0
  52. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/ChevronRight-CtQH1EQ1.js +0 -0
  53. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/ChevronRight-CtQH1EQ1.js.map +0 -0
  54. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/CodeViewer-Bqy7-wvH.js +0 -0
  55. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/CodeViewer-Bqy7-wvH.js.map +0 -0
  56. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Crons-B67vc39F.js +0 -0
  57. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Crons-B67vc39F.js.map +0 -0
  58. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/DashboardView-CUTFVL6k.js +0 -0
  59. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/DashboardView-CUTFVL6k.js.map +0 -0
  60. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/DataTable-COAAWEft.js +0 -0
  61. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/DataTable-COAAWEft.js.map +0 -0
  62. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/DescriptionsItem-P8JUDaBs.js +0 -0
  63. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/DescriptionsItem-P8JUDaBs.js.map +0 -0
  64. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/DrawerContent-TpYTFgF1.js +0 -0
  65. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/DrawerContent-TpYTFgF1.js.map +0 -0
  66. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Empty-cr2r7e2u.js +0 -0
  67. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Empty-cr2r7e2u.js.map +0 -0
  68. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/EmptyState-DeDck-OL.js +0 -0
  69. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/EmptyState-DeDck-OL.js.map +0 -0
  70. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Grid-hFkp9F4P.js +0 -0
  71. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Grid-hFkp9F4P.js.map +0 -0
  72. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Input-DppYTq9C.js +0 -0
  73. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Input-DppYTq9C.js.map +0 -0
  74. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Invoke-DW3Nveeh.js +0 -0
  75. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Invoke-DW3Nveeh.js.map +0 -0
  76. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/JsonField-DibyJgun.js +0 -0
  77. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/JsonField-DibyJgun.js.map +0 -0
  78. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/LoginView-BjLyE3Ds.css +0 -0
  79. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/LoginView-CoOjECT_.js +0 -0
  80. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/LoginView-CoOjECT_.js.map +0 -0
  81. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Logs-D9WYrnIT.js +0 -0
  82. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Logs-D9WYrnIT.js.map +0 -0
  83. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Logs-DS1XPa0h.css +0 -0
  84. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Migrations-DOSC2ddQ.js +0 -0
  85. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Migrations-DOSC2ddQ.js.map +0 -0
  86. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/ObjectBrowser-_5w8vOX8.js +0 -0
  87. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/ObjectBrowser-_5w8vOX8.js.map +0 -0
  88. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Queue-CywZs6vI.js +0 -0
  89. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Queue-CywZs6vI.js.map +0 -0
  90. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/RefreshTokens-Ccjr53jg.js +0 -0
  91. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/RefreshTokens-Ccjr53jg.js.map +0 -0
  92. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/RlsEditor-BSlH9vSc.js +0 -0
  93. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/RlsEditor-BSlH9vSc.js.map +0 -0
  94. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Routes-BiLXE49D.js +0 -0
  95. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Routes-BiLXE49D.js.map +0 -0
  96. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Routes-C-ianIGD.css +0 -0
  97. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/SchemaBrowser-DKy2_KQi.css +0 -0
  98. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/SchemaBrowser-XFvFbtDB.js +0 -0
  99. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/SchemaBrowser-XFvFbtDB.js.map +0 -0
  100. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Select-DIzZyRZb.js +0 -0
  101. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Select-DIzZyRZb.js.map +0 -0
  102. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Space-n5-XcguU.js +0 -0
  103. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Space-n5-XcguU.js.map +0 -0
  104. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/SqlEditor-b8pTsILY.js +0 -0
  105. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/SqlEditor-b8pTsILY.js.map +0 -0
  106. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/SqlWorkspace-BUS7IntH.js +0 -0
  107. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/SqlWorkspace-BUS7IntH.js.map +0 -0
  108. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/TableData-CQIagLKn.js +0 -0
  109. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/TableData-CQIagLKn.js.map +0 -0
  110. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Tag-D1fOKpTH.js +0 -0
  111. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Tag-D1fOKpTH.js.map +0 -0
  112. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Templates-BS-ugkdq.js +0 -0
  113. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Templates-BS-ugkdq.js.map +0 -0
  114. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Thing-CEAniuMg.js +0 -0
  115. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Thing-CEAniuMg.js.map +0 -0
  116. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Users-wzwajhlh.js +0 -0
  117. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/Users-wzwajhlh.js.map +0 -0
  118. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/_plugin-vue_export-helper-DGA9ry_j.js +0 -0
  119. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/dist-VXIJLCYq.js +0 -0
  120. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/dist-VXIJLCYq.js.map +0 -0
  121. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/format-length-CGCY1rMh.js +0 -0
  122. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/format-length-CGCY1rMh.js.map +0 -0
  123. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/get-Ca6unauB.js +0 -0
  124. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/get-Ca6unauB.js.map +0 -0
  125. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/index-CeE6v959.js +0 -0
  126. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/index-CeE6v959.js.map +0 -0
  127. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/pinia-COXwfrOX.js +0 -0
  128. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/pinia-COXwfrOX.js.map +0 -0
  129. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/resources-Bt6thQCD.js +0 -0
  130. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/resources-Bt6thQCD.js.map +0 -0
  131. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/use-locale-mtgM0a3a.js +0 -0
  132. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/use-locale-mtgM0a3a.js.map +0 -0
  133. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/use-merged-state-BvhkaHNX.js +0 -0
  134. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/use-merged-state-BvhkaHNX.js.map +0 -0
  135. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/useConfirm-tMjvBFXR.js +0 -0
  136. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/useConfirm-tMjvBFXR.js.map +0 -0
  137. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/useResource-C_rJCY8C.js +0 -0
  138. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/useResource-C_rJCY8C.js.map +0 -0
  139. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/useTable-CnZc5zhi.js +0 -0
  140. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/useTable-CnZc5zhi.js.map +0 -0
  141. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/useTable-Dg0XlRlq.css +0 -0
  142. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/useToast-DsZKx0IX.js +0 -0
  143. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/useToast-DsZKx0IX.js.map +0 -0
  144. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/utils-sbXoq7Ir.js +0 -0
  145. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/assets/utils-sbXoq7Ir.js.map +0 -0
  146. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/favicon.svg +0 -0
  147. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/icons.svg +0 -0
  148. {supython-0.1.2 → supython-0.1.4}/src/supython/admin/static/index.html +0 -0
  149. {supython-0.1.2 → supython-0.1.4}/src/supython/auth/_email_job.py +0 -0
  150. {supython-0.1.2 → supython-0.1.4}/src/supython/auth/providers/__init__.py +0 -0
  151. {supython-0.1.2 → supython-0.1.4}/src/supython/auth/providers/github.py +0 -0
  152. {supython-0.1.2 → supython-0.1.4}/src/supython/auth/providers/google.py +0 -0
  153. {supython-0.1.2 → supython-0.1.4}/src/supython/auth/providers/oauth.py +0 -0
  154. {supython-0.1.2 → supython-0.1.4}/src/supython/auth/providers/registry.py +0 -0
  155. {supython-0.1.2 → supython-0.1.4}/src/supython/auth/ratelimit.py +0 -0
  156. {supython-0.1.2 → supython-0.1.4}/src/supython/auth/router.py +0 -0
  157. {supython-0.1.2 → supython-0.1.4}/src/supython/auth/schemas.py +0 -0
  158. {supython-0.1.2 → supython-0.1.4}/src/supython/backups/__init__.py +0 -0
  159. {supython-0.1.2 → supython-0.1.4}/src/supython/backups/_backup_job.py +0 -0
  160. {supython-0.1.2 → supython-0.1.4}/src/supython/backups/schemas.py +0 -0
  161. {supython-0.1.2 → supython-0.1.4}/src/supython/backups/service.py +0 -0
  162. {supython-0.1.2 → supython-0.1.4}/src/supython/body_size.py +0 -0
  163. {supython-0.1.2 → supython-0.1.4}/src/supython/cli.py +0 -0
  164. {supython-0.1.2 → supython-0.1.4}/src/supython/client/__init__.py +0 -0
  165. {supython-0.1.2 → supython-0.1.4}/src/supython/client/_auth.py +0 -0
  166. {supython-0.1.2 → supython-0.1.4}/src/supython/client/_client.py +0 -0
  167. {supython-0.1.2 → supython-0.1.4}/src/supython/client/_config.py +0 -0
  168. {supython-0.1.2 → supython-0.1.4}/src/supython/client/_functions.py +0 -0
  169. {supython-0.1.2 → supython-0.1.4}/src/supython/client/_storage.py +0 -0
  170. {supython-0.1.2 → supython-0.1.4}/src/supython/client/py.typed +0 -0
  171. {supython-0.1.2 → supython-0.1.4}/src/supython/db.py +0 -0
  172. {supython-0.1.2 → supython-0.1.4}/src/supython/db_admin.py +0 -0
  173. {supython-0.1.2 → supython-0.1.4}/src/supython/extensions.py +0 -0
  174. {supython-0.1.2 → supython-0.1.4}/src/supython/functions/__init__.py +0 -0
  175. {supython-0.1.2 → supython-0.1.4}/src/supython/functions/context.py +0 -0
  176. {supython-0.1.2 → supython-0.1.4}/src/supython/functions/loader.py +0 -0
  177. {supython-0.1.2 → supython-0.1.4}/src/supython/functions/router.py +0 -0
  178. {supython-0.1.2 → supython-0.1.4}/src/supython/functions/schemas.py +0 -0
  179. {supython-0.1.2 → supython-0.1.4}/src/supython/gen/__init__.py +0 -0
  180. {supython-0.1.2 → supython-0.1.4}/src/supython/gen/_introspect.py +0 -0
  181. {supython-0.1.2 → supython-0.1.4}/src/supython/gen/types_py.py +0 -0
  182. {supython-0.1.2 → supython-0.1.4}/src/supython/gen/types_ts.py +0 -0
  183. {supython-0.1.2 → supython-0.1.4}/src/supython/health.py +0 -0
  184. {supython-0.1.2 → supython-0.1.4}/src/supython/hooks.py +0 -0
  185. {supython-0.1.2 → supython-0.1.4}/src/supython/jobs/__init__.py +0 -0
  186. {supython-0.1.2 → supython-0.1.4}/src/supython/jobs/backends.py +0 -0
  187. {supython-0.1.2 → supython-0.1.4}/src/supython/jobs/context.py +0 -0
  188. {supython-0.1.2 → supython-0.1.4}/src/supython/jobs/cron.py +0 -0
  189. {supython-0.1.2 → supython-0.1.4}/src/supython/jobs/cron_inproc.py +0 -0
  190. {supython-0.1.2 → supython-0.1.4}/src/supython/jobs/decorators.py +0 -0
  191. {supython-0.1.2 → supython-0.1.4}/src/supython/jobs/registry.py +0 -0
  192. {supython-0.1.2 → supython-0.1.4}/src/supython/jobs/router.py +0 -0
  193. {supython-0.1.2 → supython-0.1.4}/src/supython/jobs/schemas.py +0 -0
  194. {supython-0.1.2 → supython-0.1.4}/src/supython/jobs/service.py +0 -0
  195. {supython-0.1.2 → supython-0.1.4}/src/supython/jobs/worker.py +0 -0
  196. {supython-0.1.2 → supython-0.1.4}/src/supython/jwks.py +0 -0
  197. {supython-0.1.2 → supython-0.1.4}/src/supython/keyset.py +0 -0
  198. {supython-0.1.2 → supython-0.1.4}/src/supython/logging_config.py +0 -0
  199. {supython-0.1.2 → supython-0.1.4}/src/supython/mail.py +0 -0
  200. {supython-0.1.2 → supython-0.1.4}/src/supython/mailer.py +0 -0
  201. {supython-0.1.2 → supython-0.1.4}/src/supython/migrate.py +0 -0
  202. {supython-0.1.2 → supython-0.1.4}/src/supython/migrations/0001_extensions_and_roles.sql +0 -0
  203. {supython-0.1.2 → supython-0.1.4}/src/supython/migrations/0002_auth_schema.sql +0 -0
  204. {supython-0.1.2 → supython-0.1.4}/src/supython/migrations/0003_demo_todos.sql +0 -0
  205. {supython-0.1.2 → supython-0.1.4}/src/supython/migrations/0004_auth_v0_2.sql +0 -0
  206. {supython-0.1.2 → supython-0.1.4}/src/supython/migrations/0005_storage_schema.sql +0 -0
  207. {supython-0.1.2 → supython-0.1.4}/src/supython/migrations/0006_realtime_schema.sql +0 -0
  208. {supython-0.1.2 → supython-0.1.4}/src/supython/migrations/0007_jobs_schema.sql +0 -0
  209. {supython-0.1.2 → supython-0.1.4}/src/supython/migrations/0008_jobs_last_error.sql +0 -0
  210. {supython-0.1.2 → supython-0.1.4}/src/supython/migrations/0009_auth_rate_limits.sql +0 -0
  211. {supython-0.1.2 → supython-0.1.4}/src/supython/migrations/0010_worker_heartbeat.sql +0 -0
  212. {supython-0.1.2 → supython-0.1.4}/src/supython/migrations/0011_admin_schema.sql +0 -0
  213. {supython-0.1.2 → supython-0.1.4}/src/supython/migrations/0012_auth_banned_until.sql +0 -0
  214. {supython-0.1.2 → supython-0.1.4}/src/supython/migrations/0013_email_templates.sql +0 -0
  215. {supython-0.1.2 → supython-0.1.4}/src/supython/migrations/0014_realtime_payload_warning.sql +0 -0
  216. {supython-0.1.2 → supython-0.1.4}/src/supython/migrations/0015_backups_schema.sql +0 -0
  217. {supython-0.1.2 → supython-0.1.4}/src/supython/passwords.py +0 -0
  218. {supython-0.1.2 → supython-0.1.4}/src/supython/realtime/__init__.py +0 -0
  219. {supython-0.1.2 → supython-0.1.4}/src/supython/realtime/broker.py +0 -0
  220. {supython-0.1.2 → supython-0.1.4}/src/supython/realtime/protocol.py +0 -0
  221. {supython-0.1.2 → supython-0.1.4}/src/supython/realtime/router.py +0 -0
  222. {supython-0.1.2 → supython-0.1.4}/src/supython/realtime/schemas.py +0 -0
  223. {supython-0.1.2 → supython-0.1.4}/src/supython/realtime/service.py +0 -0
  224. {supython-0.1.2 → supython-0.1.4}/src/supython/realtime/topics.py +0 -0
  225. {supython-0.1.2 → supython-0.1.4}/src/supython/realtime/websocket.py +0 -0
  226. {supython-0.1.2 → supython-0.1.4}/src/supython/scaffold/__init__.py +0 -0
  227. {supython-0.1.2 → supython-0.1.4}/src/supython/scaffold/init_project.py +0 -0
  228. {supython-0.1.2 → supython-0.1.4}/src/supython/scaffold/templates/Caddyfile.tmpl +0 -0
  229. {supython-0.1.2 → supython-0.1.4}/src/supython/scaffold/templates/README.md.tmpl +0 -0
  230. {supython-0.1.2 → supython-0.1.4}/src/supython/scaffold/templates/apps_hooks.py.tmpl +0 -0
  231. {supython-0.1.2 → supython-0.1.4}/src/supython/scaffold/templates/apps_jobs.py.tmpl +0 -0
  232. {supython-0.1.2 → supython-0.1.4}/src/supython/scaffold/templates/asgi.py.tmpl +0 -0
  233. {supython-0.1.2 → supython-0.1.4}/src/supython/scaffold/templates/docker-compose.prod.yml.tmpl +0 -0
  234. {supython-0.1.2 → supython-0.1.4}/src/supython/scaffold/templates/docker-compose.yml.tmpl +0 -0
  235. {supython-0.1.2 → supython-0.1.4}/src/supython/scaffold/templates/docker_postgres_Dockerfile.tmpl +0 -0
  236. {supython-0.1.2 → supython-0.1.4}/src/supython/scaffold/templates/docker_postgres_postgresql.conf.tmpl +0 -0
  237. {supython-0.1.2 → supython-0.1.4}/src/supython/scaffold/templates/env.example.tmpl +0 -0
  238. {supython-0.1.2 → supython-0.1.4}/src/supython/scaffold/templates/functions_README.md.tmpl +0 -0
  239. {supython-0.1.2 → supython-0.1.4}/src/supython/scaffold/templates/gitignore.tmpl +0 -0
  240. {supython-0.1.2 → supython-0.1.4}/src/supython/scaffold/templates/manage.py.tmpl +0 -0
  241. {supython-0.1.2 → supython-0.1.4}/src/supython/scaffold/templates/migrations/.gitkeep +0 -0
  242. {supython-0.1.2 → supython-0.1.4}/src/supython/scaffold/templates/package_init.py.tmpl +0 -0
  243. {supython-0.1.2 → supython-0.1.4}/src/supython/scaffold/templates/settings.py.tmpl +0 -0
  244. {supython-0.1.2 → supython-0.1.4}/src/supython/secretset.py +0 -0
  245. {supython-0.1.2 → supython-0.1.4}/src/supython/security_headers.py +0 -0
  246. {supython-0.1.2 → supython-0.1.4}/src/supython/settings.py +0 -0
  247. {supython-0.1.2 → supython-0.1.4}/src/supython/settings_module.py +0 -0
  248. {supython-0.1.2 → supython-0.1.4}/src/supython/storage/__init__.py +0 -0
  249. {supython-0.1.2 → supython-0.1.4}/src/supython/storage/backends.py +0 -0
  250. {supython-0.1.2 → supython-0.1.4}/src/supython/storage/router.py +0 -0
  251. {supython-0.1.2 → supython-0.1.4}/src/supython/storage/schemas.py +0 -0
  252. {supython-0.1.2 → supython-0.1.4}/src/supython/storage/service.py +0 -0
  253. {supython-0.1.2 → supython-0.1.4}/src/supython/storage/signing.py +0 -0
  254. {supython-0.1.2 → supython-0.1.4}/src/supython/tokens.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supython
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: A lightweight Postgres-first BaaS framework for Python
5
5
  Project-URL: Homepage, https://github.com/Tkeby/supython
6
6
  Project-URL: Repository, https://github.com/Tkeby/supython
@@ -58,7 +58,7 @@ Description-Content-Type: text/markdown
58
58
 
59
59
  # supython
60
60
 
61
- > A lightweight, Postgres-first BaaS framework for Python. **v0.1.2 release**
61
+ > A lightweight, Postgres-first BaaS framework for Python. **v0.1.4 release**
62
62
 
63
63
  **the database owns the schema, Python owns the things SQL is bad at**.
64
64
  It leans on [PostgREST](https://postgrest.org)
@@ -95,7 +95,7 @@ Shipped [v0.1.2]:
95
95
  **Jobs & cron**
96
96
  - **Job queue** — Postgres-backed (`SELECT FOR UPDATE SKIP LOCKED`), idempotent enqueue, retry with backoff
97
97
  - **Cron scheduling** — `pg_cron` (primary) or in-process `croniter` fallback
98
- - **Generic hooks** — `@app.on_signup` / `@app.on_login` lifecycle hooks
98
+ - **Generic hooks** — `@app.on_signup` / `@app.on_login` lifecycle hooks; `@app.claims_provider` for custom JWT claims
99
99
  - **`supython worker run`** — long-running worker with graceful SIGTERM drain
100
100
 
101
101
  **Operations & security**
@@ -418,6 +418,69 @@ SMTP_PASSWORD`.
418
418
  **OAuth** — add `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` (and/or GitHub
419
419
  equivalents) to `.env`. Providers without credentials are silently disabled.
420
420
 
421
+ ### Custom JWT claims [shipped v0.1.3]
422
+
423
+ Register a `claims_provider` to inject application-specific claims into every
424
+ access token minted by the auth endpoints. Each provider is an async callable
425
+ `(user, conn) -> dict` whose return value is merged into the token payload —
426
+ on signup, password login, refresh, magic-link, OTP, and OAuth callbacks.
427
+
428
+ ```python
429
+ from supython.app import app
430
+
431
+ @app.claims_provider
432
+ async def add_org(user, conn):
433
+ org_id = await conn.fetchval(
434
+ "select org_id from public.memberships where user_id = $1", user.id
435
+ )
436
+ return {"org_id": str(org_id)} if org_id else {}
437
+ ```
438
+
439
+ Notes:
440
+
441
+ - Reserved JWT claims (`sub`, `email`, `role`, `aud`, `iat`, `exp`, `jti`)
442
+ cannot be overridden — they are filtered out automatically.
443
+ - Providers run on the **service-role** connection used by the auth flow, so
444
+ they can read tables that RLS would block during issuance. Treat the
445
+ function as privileged code (same posture as a Postgres `security definer`
446
+ routine — sanitize any user-supplied input).
447
+ - A provider that raises aborts issuance: a missing claim is a silent authz
448
+ bug, not a missing welcome email.
449
+ - Refresh re-collects claims, so a token rotated via `/auth/v1/refresh`
450
+ reflects current state.
451
+
452
+ ### Reading the caller in your routes [shipped v0.1.4]
453
+
454
+ Application code mounts its own routers alongside supython's. To gate an
455
+ endpoint on a valid bearer token — or to read custom claims out of it —
456
+ use the public dependencies re-exported from `supython.auth`:
457
+
458
+ ```python
459
+ from typing import Annotated
460
+ from uuid import UUID
461
+
462
+ from fastapi import APIRouter, Depends
463
+ from supython.auth import current_claims, current_user_id
464
+
465
+ router = APIRouter(prefix="/api/v1")
466
+
467
+
468
+ @router.get("/me")
469
+ async def me(user_id: Annotated[UUID, Depends(current_user_id)]) -> dict:
470
+ return {"user_id": str(user_id)}
471
+
472
+
473
+ @router.get("/whoami")
474
+ async def whoami(claims: Annotated[dict, Depends(current_claims)]) -> dict:
475
+ # Read whatever your `claims_provider` injected — e.g. org_id.
476
+ return {"user_id": claims["sub"], "org_id": claims.get("org_id")}
477
+ ```
478
+
479
+ Both dependencies raise `401 Unauthorized` when the `Authorization` header
480
+ is missing, malformed, or carries an invalid/expired token. `current_user_id`
481
+ is a thin convenience over `current_claims` — pick the dict version when you
482
+ need any claim beyond the user UUID.
483
+
421
484
  ### Auth hardening settings (`.env`)
422
485
 
423
486
  | Variable | Default | Purpose |
@@ -1,6 +1,6 @@
1
1
  # supython
2
2
 
3
- > A lightweight, Postgres-first BaaS framework for Python. **v0.1.2 release**
3
+ > A lightweight, Postgres-first BaaS framework for Python. **v0.1.4 release**
4
4
 
5
5
  **the database owns the schema, Python owns the things SQL is bad at**.
6
6
  It leans on [PostgREST](https://postgrest.org)
@@ -37,7 +37,7 @@ Shipped [v0.1.2]:
37
37
  **Jobs & cron**
38
38
  - **Job queue** — Postgres-backed (`SELECT FOR UPDATE SKIP LOCKED`), idempotent enqueue, retry with backoff
39
39
  - **Cron scheduling** — `pg_cron` (primary) or in-process `croniter` fallback
40
- - **Generic hooks** — `@app.on_signup` / `@app.on_login` lifecycle hooks
40
+ - **Generic hooks** — `@app.on_signup` / `@app.on_login` lifecycle hooks; `@app.claims_provider` for custom JWT claims
41
41
  - **`supython worker run`** — long-running worker with graceful SIGTERM drain
42
42
 
43
43
  **Operations & security**
@@ -360,6 +360,69 @@ SMTP_PASSWORD`.
360
360
  **OAuth** — add `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` (and/or GitHub
361
361
  equivalents) to `.env`. Providers without credentials are silently disabled.
362
362
 
363
+ ### Custom JWT claims [shipped v0.1.3]
364
+
365
+ Register a `claims_provider` to inject application-specific claims into every
366
+ access token minted by the auth endpoints. Each provider is an async callable
367
+ `(user, conn) -> dict` whose return value is merged into the token payload —
368
+ on signup, password login, refresh, magic-link, OTP, and OAuth callbacks.
369
+
370
+ ```python
371
+ from supython.app import app
372
+
373
+ @app.claims_provider
374
+ async def add_org(user, conn):
375
+ org_id = await conn.fetchval(
376
+ "select org_id from public.memberships where user_id = $1", user.id
377
+ )
378
+ return {"org_id": str(org_id)} if org_id else {}
379
+ ```
380
+
381
+ Notes:
382
+
383
+ - Reserved JWT claims (`sub`, `email`, `role`, `aud`, `iat`, `exp`, `jti`)
384
+ cannot be overridden — they are filtered out automatically.
385
+ - Providers run on the **service-role** connection used by the auth flow, so
386
+ they can read tables that RLS would block during issuance. Treat the
387
+ function as privileged code (same posture as a Postgres `security definer`
388
+ routine — sanitize any user-supplied input).
389
+ - A provider that raises aborts issuance: a missing claim is a silent authz
390
+ bug, not a missing welcome email.
391
+ - Refresh re-collects claims, so a token rotated via `/auth/v1/refresh`
392
+ reflects current state.
393
+
394
+ ### Reading the caller in your routes [shipped v0.1.4]
395
+
396
+ Application code mounts its own routers alongside supython's. To gate an
397
+ endpoint on a valid bearer token — or to read custom claims out of it —
398
+ use the public dependencies re-exported from `supython.auth`:
399
+
400
+ ```python
401
+ from typing import Annotated
402
+ from uuid import UUID
403
+
404
+ from fastapi import APIRouter, Depends
405
+ from supython.auth import current_claims, current_user_id
406
+
407
+ router = APIRouter(prefix="/api/v1")
408
+
409
+
410
+ @router.get("/me")
411
+ async def me(user_id: Annotated[UUID, Depends(current_user_id)]) -> dict:
412
+ return {"user_id": str(user_id)}
413
+
414
+
415
+ @router.get("/whoami")
416
+ async def whoami(claims: Annotated[dict, Depends(current_claims)]) -> dict:
417
+ # Read whatever your `claims_provider` injected — e.g. org_id.
418
+ return {"user_id": claims["sub"], "org_id": claims.get("org_id")}
419
+ ```
420
+
421
+ Both dependencies raise `401 Unauthorized` when the `Authorization` header
422
+ is missing, malformed, or carries an invalid/expired token. `current_user_id`
423
+ is a thin convenience over `current_claims` — pick the dict version when you
424
+ need any claim beyond the user UUID.
425
+
363
426
  ### Auth hardening settings (`.env`)
364
427
 
365
428
  | Variable | Default | Purpose |
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "supython"
7
- version = "0.1.2"
7
+ version = "0.1.4"
8
8
  description = "A lightweight Postgres-first BaaS framework for Python"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -11,6 +11,7 @@ from fastapi.middleware.cors import CORSMiddleware
11
11
  from . import __version__, db, jwks
12
12
  from .admin import router as admin_api_router
13
13
  from .admin import spa as admin_spa
14
+ from .auth import claims as auth_claims
14
15
  from .auth.router import router as auth_router
15
16
  from .extensions import load_extensions
16
17
  from .settings_module import UserSettings, load_user_settings
@@ -155,6 +156,7 @@ def create_app() -> FastAPI:
155
156
  app.on_signup = _make_hook_decorator("signup")
156
157
  app.on_login = _make_hook_decorator("login")
157
158
  app.on_logout = _make_hook_decorator("logout")
159
+ app.claims_provider = auth_claims.register
158
160
 
159
161
  return app
160
162
 
@@ -0,0 +1,4 @@
1
+ from . import _email_job, router
2
+ from .deps import current_claims, current_user_id
3
+
4
+ __all__ = ["current_claims", "current_user_id", "router"]
@@ -0,0 +1,72 @@
1
+ """Custom-claim providers for access-token issuance.
2
+
3
+ Library users register an async callable via ``@app.claims_provider`` (or
4
+ ``claims.register``) and the auth service invokes it inside ``_issue_pair``
5
+ just before minting the JWT. Each provider returns a ``dict`` that is
6
+ merged into the access token's payload.
7
+
8
+ Contract (intentionally narrower than ``hooks.fire``):
9
+
10
+ - Providers run on the **service-role** connection used by the auth flow,
11
+ so they can read tables that RLS would block during issuance (e.g. a
12
+ ``user_roles`` lookup keyed by a brand-new user). Treat the function as
13
+ privileged — same posture as a Postgres ``security definer`` routine.
14
+ - A provider raising propagates: a missing claim is a silent authz bug,
15
+ not a missing welcome email. ``hooks.fire`` swallows on purpose;
16
+ ``collect`` does not.
17
+ - Reserved JWT claims (``sub``, ``email``, ``role``, ``aud``, ``iat``,
18
+ ``exp``, ``jti``) cannot be overridden — they are filtered out of every
19
+ provider's return value so a misbehaving provider can't mint a token
20
+ that contradicts its own header or expiry.
21
+ """
22
+
23
+ import logging
24
+ from collections.abc import Awaitable, Callable
25
+ from typing import Any
26
+
27
+ import asyncpg
28
+
29
+ from .schemas import UserResponse
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+ ClaimsProvider = Callable[[UserResponse, asyncpg.Connection], Awaitable[dict[str, Any]]]
34
+
35
+ _RESERVED: frozenset[str] = frozenset(
36
+ {"sub", "email", "role", "aud", "iat", "exp", "jti"}
37
+ )
38
+
39
+ _providers: list[ClaimsProvider] = []
40
+
41
+
42
+ def register(fn: ClaimsProvider) -> ClaimsProvider:
43
+ """Register *fn* as a claims provider. Usable as a decorator."""
44
+ _providers.append(fn)
45
+ return fn
46
+
47
+
48
+ async def collect(user: UserResponse, conn: asyncpg.Connection) -> dict[str, Any]:
49
+ """Run every registered provider and return the merged claim dict.
50
+
51
+ Providers run in registration order; later providers win on key
52
+ collisions. Reserved JWT claims are stripped from each return value
53
+ before merging.
54
+ """
55
+ merged: dict[str, Any] = {}
56
+ for fn in _providers:
57
+ out = await fn(user, conn)
58
+ if not out:
59
+ continue
60
+ for key in _RESERVED.intersection(out):
61
+ logger.warning(
62
+ "claims provider %r returned reserved claim %r; dropping",
63
+ getattr(fn, "__qualname__", fn),
64
+ key,
65
+ )
66
+ merged.update({k: v for k, v in out.items() if k not in _RESERVED})
67
+ return merged
68
+
69
+
70
+ def reset() -> None:
71
+ """Clear all registered providers. Test-only."""
72
+ _providers.clear()
@@ -0,0 +1,57 @@
1
+ """Public FastAPI dependencies for downstream apps.
2
+
3
+ These are the supported way for application code to read the authenticated
4
+ caller out of an incoming request — equivalent to the auth router's private
5
+ ``_current_user_id`` but stable, re-exported, and documented.
6
+
7
+ Usage::
8
+
9
+ from typing import Annotated
10
+ from uuid import UUID
11
+
12
+ from fastapi import Depends
13
+ from supython.auth import current_user_id, current_claims
14
+
15
+ @router.get("/me")
16
+ async def me(user_id: Annotated[UUID, Depends(current_user_id)]):
17
+ ...
18
+
19
+ @router.get("/whoami")
20
+ async def whoami(claims: Annotated[dict, Depends(current_claims)]):
21
+ return {"org_id": claims.get("org_id")}
22
+ """
23
+
24
+ from typing import Annotated, Any
25
+ from uuid import UUID
26
+
27
+ import jwt
28
+ from fastapi import Depends, Header, HTTPException, status
29
+
30
+ from .. import tokens
31
+
32
+
33
+ async def current_claims(
34
+ authorization: Annotated[str | None, Header()] = None,
35
+ ) -> dict[str, Any]:
36
+ """Return the decoded JWT claims of the bearer token, or 401."""
37
+ if not authorization or not authorization.lower().startswith("bearer "):
38
+ raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Missing bearer token")
39
+ token = authorization.split(" ", 1)[1]
40
+ try:
41
+ return tokens.decode_access_token(token)
42
+ except jwt.PyJWTError as exc:
43
+ raise HTTPException(
44
+ status.HTTP_401_UNAUTHORIZED, f"Invalid token: {exc}"
45
+ ) from exc
46
+
47
+
48
+ async def current_user_id(
49
+ claims: Annotated[dict[str, Any], Depends(current_claims)],
50
+ ) -> UUID:
51
+ """Return the authenticated user's UUID from the bearer token, or 401."""
52
+ try:
53
+ return UUID(claims["sub"])
54
+ except (KeyError, ValueError) as exc:
55
+ raise HTTPException(
56
+ status.HTTP_401_UNAUTHORIZED, "Token missing valid sub claim"
57
+ ) from exc
@@ -14,6 +14,7 @@ from itsdangerous import BadSignature, URLSafeTimedSerializer
14
14
  from .. import mail, passwords, tokens
15
15
  from ..mailer import EmailMessage
16
16
  from ..settings import get_settings
17
+ from . import claims
17
18
  from .schemas import UserResponse
18
19
 
19
20
  logger = logging.getLogger(__name__)
@@ -63,7 +64,10 @@ async def _audit_log(
63
64
  async def _issue_pair(
64
65
  conn: asyncpg.Connection, user: UserResponse
65
66
  ) -> tuple[str, str, int]:
66
- access, ttl = tokens.issue_access_token(user.id, user.email)
67
+ extra = await claims.collect(user, conn)
68
+ access, ttl = tokens.issue_access_token(
69
+ user.id, user.email, extra_claims=extra or None
70
+ )
67
71
  refresh = tokens.issue_refresh_token()
68
72
  await conn.execute(
69
73
  "insert into auth.refresh_tokens (user_id, token) values ($1, $2)",
@@ -273,7 +277,10 @@ async def refresh_grant(
273
277
  new_refresh,
274
278
  refresh_token,
275
279
  )
276
- access, ttl = tokens.issue_access_token(user.id, user.email)
280
+ extra = await claims.collect(user, conn)
281
+ access, ttl = tokens.issue_access_token(
282
+ user.id, user.email, extra_claims=extra or None
283
+ )
277
284
  return user, access, new_refresh, ttl
278
285
 
279
286
 
@@ -1,3 +0,0 @@
1
- from . import _email_job, router
2
-
3
- __all__ = ["router"]
File without changes
File without changes
File without changes
File without changes