supython 0.1.13__tar.gz → 0.1.14__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 (258) hide show
  1. {supython-0.1.13 → supython-0.1.14}/CHANGELOG.md +24 -0
  2. {supython-0.1.13 → supython-0.1.14}/PKG-INFO +1 -1
  3. {supython-0.1.13 → supython-0.1.14}/pyproject.toml +1 -1
  4. {supython-0.1.13 → supython-0.1.14}/src/supython/auth/service.py +85 -4
  5. {supython-0.1.13 → supython-0.1.14}/src/supython/cli.py +10 -5
  6. supython-0.1.14/src/supython/migrations/0017_auth_activated_at.sql +37 -0
  7. {supython-0.1.13 → supython-0.1.14}/.gitignore +0 -0
  8. {supython-0.1.13 → supython-0.1.14}/LICENSE +0 -0
  9. {supython-0.1.13 → supython-0.1.14}/README.md +0 -0
  10. {supython-0.1.13 → supython-0.1.14}/SECURITY.md +0 -0
  11. {supython-0.1.13 → supython-0.1.14}/src/supython/__init__.py +0 -0
  12. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/__init__.py +0 -0
  13. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/__init__.py +0 -0
  14. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/auth.py +0 -0
  15. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/auth_templates.py +0 -0
  16. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/auth_users.py +0 -0
  17. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/db.py +0 -0
  18. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/functions.py +0 -0
  19. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/jobs.py +0 -0
  20. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/ops.py +0 -0
  21. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/realtime.py +0 -0
  22. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/service_auth.py +0 -0
  23. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/service_auth_templates.py +0 -0
  24. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/service_auth_users.py +0 -0
  25. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/service_db.py +0 -0
  26. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/service_functions.py +0 -0
  27. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/service_jobs.py +0 -0
  28. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/service_ops.py +0 -0
  29. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/service_realtime.py +0 -0
  30. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/service_storage.py +0 -0
  31. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/storage.py +0 -0
  32. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/api/system.py +0 -0
  33. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/audit.py +0 -0
  34. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/deps.py +0 -0
  35. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/errors.py +0 -0
  36. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/schemas.py +0 -0
  37. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/session.py +0 -0
  38. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/spa.py +0 -0
  39. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Alert-dluGVkos.js +0 -0
  40. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Alert-dluGVkos.js.map +0 -0
  41. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Audit-Njung3HI.js +0 -0
  42. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Audit-Njung3HI.js.map +0 -0
  43. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Backups-DzPlFgrm.js +0 -0
  44. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Backups-DzPlFgrm.js.map +0 -0
  45. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Buckets-ByacGkU1.js +0 -0
  46. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Buckets-ByacGkU1.js.map +0 -0
  47. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Channels-BoIuTtam.js +0 -0
  48. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Channels-BoIuTtam.js.map +0 -0
  49. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/ChevronRight-CtQH1EQ1.js +0 -0
  50. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/ChevronRight-CtQH1EQ1.js.map +0 -0
  51. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/CodeViewer-Bqy7-wvH.js +0 -0
  52. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/CodeViewer-Bqy7-wvH.js.map +0 -0
  53. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Crons-B67vc39F.js +0 -0
  54. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Crons-B67vc39F.js.map +0 -0
  55. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/DashboardView-CUTFVL6k.js +0 -0
  56. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/DashboardView-CUTFVL6k.js.map +0 -0
  57. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/DataTable-COAAWEft.js +0 -0
  58. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/DataTable-COAAWEft.js.map +0 -0
  59. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/DescriptionsItem-P8JUDaBs.js +0 -0
  60. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/DescriptionsItem-P8JUDaBs.js.map +0 -0
  61. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/DrawerContent-TpYTFgF1.js +0 -0
  62. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/DrawerContent-TpYTFgF1.js.map +0 -0
  63. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Empty-cr2r7e2u.js +0 -0
  64. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Empty-cr2r7e2u.js.map +0 -0
  65. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/EmptyState-DeDck-OL.js +0 -0
  66. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/EmptyState-DeDck-OL.js.map +0 -0
  67. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Grid-hFkp9F4P.js +0 -0
  68. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Grid-hFkp9F4P.js.map +0 -0
  69. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Input-DppYTq9C.js +0 -0
  70. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Input-DppYTq9C.js.map +0 -0
  71. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Invoke-DW3Nveeh.js +0 -0
  72. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Invoke-DW3Nveeh.js.map +0 -0
  73. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/JsonField-DibyJgun.js +0 -0
  74. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/JsonField-DibyJgun.js.map +0 -0
  75. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/LoginView-BjLyE3Ds.css +0 -0
  76. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/LoginView-CoOjECT_.js +0 -0
  77. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/LoginView-CoOjECT_.js.map +0 -0
  78. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Logs-D9WYrnIT.js +0 -0
  79. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Logs-D9WYrnIT.js.map +0 -0
  80. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Logs-DS1XPa0h.css +0 -0
  81. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Migrations-DOSC2ddQ.js +0 -0
  82. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Migrations-DOSC2ddQ.js.map +0 -0
  83. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/ObjectBrowser-_5w8vOX8.js +0 -0
  84. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/ObjectBrowser-_5w8vOX8.js.map +0 -0
  85. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Queue-CywZs6vI.js +0 -0
  86. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Queue-CywZs6vI.js.map +0 -0
  87. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/RefreshTokens-Ccjr53jg.js +0 -0
  88. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/RefreshTokens-Ccjr53jg.js.map +0 -0
  89. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/RlsEditor-BSlH9vSc.js +0 -0
  90. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/RlsEditor-BSlH9vSc.js.map +0 -0
  91. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Routes-BiLXE49D.js +0 -0
  92. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Routes-BiLXE49D.js.map +0 -0
  93. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Routes-C-ianIGD.css +0 -0
  94. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/SchemaBrowser-DKy2_KQi.css +0 -0
  95. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/SchemaBrowser-XFvFbtDB.js +0 -0
  96. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/SchemaBrowser-XFvFbtDB.js.map +0 -0
  97. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Select-DIzZyRZb.js +0 -0
  98. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Select-DIzZyRZb.js.map +0 -0
  99. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Space-n5-XcguU.js +0 -0
  100. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Space-n5-XcguU.js.map +0 -0
  101. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/SqlEditor-b8pTsILY.js +0 -0
  102. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/SqlEditor-b8pTsILY.js.map +0 -0
  103. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/SqlWorkspace-BUS7IntH.js +0 -0
  104. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/SqlWorkspace-BUS7IntH.js.map +0 -0
  105. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/TableData-CQIagLKn.js +0 -0
  106. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/TableData-CQIagLKn.js.map +0 -0
  107. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Tag-D1fOKpTH.js +0 -0
  108. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Tag-D1fOKpTH.js.map +0 -0
  109. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Templates-BS-ugkdq.js +0 -0
  110. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Templates-BS-ugkdq.js.map +0 -0
  111. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Thing-CEAniuMg.js +0 -0
  112. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Thing-CEAniuMg.js.map +0 -0
  113. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Users-wzwajhlh.js +0 -0
  114. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/Users-wzwajhlh.js.map +0 -0
  115. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/_plugin-vue_export-helper-DGA9ry_j.js +0 -0
  116. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/dist-VXIJLCYq.js +0 -0
  117. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/dist-VXIJLCYq.js.map +0 -0
  118. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/format-length-CGCY1rMh.js +0 -0
  119. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/format-length-CGCY1rMh.js.map +0 -0
  120. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/get-Ca6unauB.js +0 -0
  121. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/get-Ca6unauB.js.map +0 -0
  122. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/index-CeE6v959.js +0 -0
  123. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/index-CeE6v959.js.map +0 -0
  124. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/pinia-COXwfrOX.js +0 -0
  125. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/pinia-COXwfrOX.js.map +0 -0
  126. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/resources-Bt6thQCD.js +0 -0
  127. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/resources-Bt6thQCD.js.map +0 -0
  128. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/use-locale-mtgM0a3a.js +0 -0
  129. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/use-locale-mtgM0a3a.js.map +0 -0
  130. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/use-merged-state-BvhkaHNX.js +0 -0
  131. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/use-merged-state-BvhkaHNX.js.map +0 -0
  132. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/useConfirm-tMjvBFXR.js +0 -0
  133. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/useConfirm-tMjvBFXR.js.map +0 -0
  134. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/useResource-C_rJCY8C.js +0 -0
  135. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/useResource-C_rJCY8C.js.map +0 -0
  136. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/useTable-CnZc5zhi.js +0 -0
  137. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/useTable-CnZc5zhi.js.map +0 -0
  138. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/useTable-Dg0XlRlq.css +0 -0
  139. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/useToast-DsZKx0IX.js +0 -0
  140. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/useToast-DsZKx0IX.js.map +0 -0
  141. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/utils-sbXoq7Ir.js +0 -0
  142. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/assets/utils-sbXoq7Ir.js.map +0 -0
  143. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/favicon.svg +0 -0
  144. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/icons.svg +0 -0
  145. {supython-0.1.13 → supython-0.1.14}/src/supython/admin/static/index.html +0 -0
  146. {supython-0.1.13 → supython-0.1.14}/src/supython/app.py +0 -0
  147. {supython-0.1.13 → supython-0.1.14}/src/supython/auth/__init__.py +0 -0
  148. {supython-0.1.13 → supython-0.1.14}/src/supython/auth/_email_job.py +0 -0
  149. {supython-0.1.13 → supython-0.1.14}/src/supython/auth/claims.py +0 -0
  150. {supython-0.1.13 → supython-0.1.14}/src/supython/auth/deps.py +0 -0
  151. {supython-0.1.13 → supython-0.1.14}/src/supython/auth/providers/__init__.py +0 -0
  152. {supython-0.1.13 → supython-0.1.14}/src/supython/auth/providers/github.py +0 -0
  153. {supython-0.1.13 → supython-0.1.14}/src/supython/auth/providers/google.py +0 -0
  154. {supython-0.1.13 → supython-0.1.14}/src/supython/auth/providers/oauth.py +0 -0
  155. {supython-0.1.13 → supython-0.1.14}/src/supython/auth/providers/registry.py +0 -0
  156. {supython-0.1.13 → supython-0.1.14}/src/supython/auth/ratelimit.py +0 -0
  157. {supython-0.1.13 → supython-0.1.14}/src/supython/auth/router.py +0 -0
  158. {supython-0.1.13 → supython-0.1.14}/src/supython/auth/schemas.py +0 -0
  159. {supython-0.1.13 → supython-0.1.14}/src/supython/backups/__init__.py +0 -0
  160. {supython-0.1.13 → supython-0.1.14}/src/supython/backups/_backup_job.py +0 -0
  161. {supython-0.1.13 → supython-0.1.14}/src/supython/backups/schemas.py +0 -0
  162. {supython-0.1.13 → supython-0.1.14}/src/supython/backups/service.py +0 -0
  163. {supython-0.1.13 → supython-0.1.14}/src/supython/body_size.py +0 -0
  164. {supython-0.1.13 → supython-0.1.14}/src/supython/client/__init__.py +0 -0
  165. {supython-0.1.13 → supython-0.1.14}/src/supython/client/_auth.py +0 -0
  166. {supython-0.1.13 → supython-0.1.14}/src/supython/client/_client.py +0 -0
  167. {supython-0.1.13 → supython-0.1.14}/src/supython/client/_config.py +0 -0
  168. {supython-0.1.13 → supython-0.1.14}/src/supython/client/_functions.py +0 -0
  169. {supython-0.1.13 → supython-0.1.14}/src/supython/client/_storage.py +0 -0
  170. {supython-0.1.13 → supython-0.1.14}/src/supython/client/py.typed +0 -0
  171. {supython-0.1.13 → supython-0.1.14}/src/supython/db.py +0 -0
  172. {supython-0.1.13 → supython-0.1.14}/src/supython/db_admin.py +0 -0
  173. {supython-0.1.13 → supython-0.1.14}/src/supython/extensions.py +0 -0
  174. {supython-0.1.13 → supython-0.1.14}/src/supython/functions/__init__.py +0 -0
  175. {supython-0.1.13 → supython-0.1.14}/src/supython/functions/context.py +0 -0
  176. {supython-0.1.13 → supython-0.1.14}/src/supython/functions/loader.py +0 -0
  177. {supython-0.1.13 → supython-0.1.14}/src/supython/functions/router.py +0 -0
  178. {supython-0.1.13 → supython-0.1.14}/src/supython/functions/schemas.py +0 -0
  179. {supython-0.1.13 → supython-0.1.14}/src/supython/gen/__init__.py +0 -0
  180. {supython-0.1.13 → supython-0.1.14}/src/supython/gen/_introspect.py +0 -0
  181. {supython-0.1.13 → supython-0.1.14}/src/supython/gen/types_py.py +0 -0
  182. {supython-0.1.13 → supython-0.1.14}/src/supython/gen/types_ts.py +0 -0
  183. {supython-0.1.13 → supython-0.1.14}/src/supython/health.py +0 -0
  184. {supython-0.1.13 → supython-0.1.14}/src/supython/hooks.py +0 -0
  185. {supython-0.1.13 → supython-0.1.14}/src/supython/jobs/__init__.py +0 -0
  186. {supython-0.1.13 → supython-0.1.14}/src/supython/jobs/backends.py +0 -0
  187. {supython-0.1.13 → supython-0.1.14}/src/supython/jobs/context.py +0 -0
  188. {supython-0.1.13 → supython-0.1.14}/src/supython/jobs/cron.py +0 -0
  189. {supython-0.1.13 → supython-0.1.14}/src/supython/jobs/cron_inproc.py +0 -0
  190. {supython-0.1.13 → supython-0.1.14}/src/supython/jobs/decorators.py +0 -0
  191. {supython-0.1.13 → supython-0.1.14}/src/supython/jobs/registry.py +0 -0
  192. {supython-0.1.13 → supython-0.1.14}/src/supython/jobs/router.py +0 -0
  193. {supython-0.1.13 → supython-0.1.14}/src/supython/jobs/schemas.py +0 -0
  194. {supython-0.1.13 → supython-0.1.14}/src/supython/jobs/service.py +0 -0
  195. {supython-0.1.13 → supython-0.1.14}/src/supython/jobs/worker.py +0 -0
  196. {supython-0.1.13 → supython-0.1.14}/src/supython/jwks.py +0 -0
  197. {supython-0.1.13 → supython-0.1.14}/src/supython/keyset.py +0 -0
  198. {supython-0.1.13 → supython-0.1.14}/src/supython/logging_config.py +0 -0
  199. {supython-0.1.13 → supython-0.1.14}/src/supython/mail.py +0 -0
  200. {supython-0.1.13 → supython-0.1.14}/src/supython/mailer.py +0 -0
  201. {supython-0.1.13 → supython-0.1.14}/src/supython/migrate.py +0 -0
  202. {supython-0.1.13 → supython-0.1.14}/src/supython/migrations/0001_extensions_and_roles.sql +0 -0
  203. {supython-0.1.13 → supython-0.1.14}/src/supython/migrations/0002_auth_schema.sql +0 -0
  204. {supython-0.1.13 → supython-0.1.14}/src/supython/migrations/0004_auth_v0_2.sql +0 -0
  205. {supython-0.1.13 → supython-0.1.14}/src/supython/migrations/0005_storage_schema.sql +0 -0
  206. {supython-0.1.13 → supython-0.1.14}/src/supython/migrations/0006_realtime_schema.sql +0 -0
  207. {supython-0.1.13 → supython-0.1.14}/src/supython/migrations/0007_jobs_schema.sql +0 -0
  208. {supython-0.1.13 → supython-0.1.14}/src/supython/migrations/0008_jobs_last_error.sql +0 -0
  209. {supython-0.1.13 → supython-0.1.14}/src/supython/migrations/0009_auth_rate_limits.sql +0 -0
  210. {supython-0.1.13 → supython-0.1.14}/src/supython/migrations/0010_worker_heartbeat.sql +0 -0
  211. {supython-0.1.13 → supython-0.1.14}/src/supython/migrations/0011_admin_schema.sql +0 -0
  212. {supython-0.1.13 → supython-0.1.14}/src/supython/migrations/0012_auth_banned_until.sql +0 -0
  213. {supython-0.1.13 → supython-0.1.14}/src/supython/migrations/0013_email_templates.sql +0 -0
  214. {supython-0.1.13 → supython-0.1.14}/src/supython/migrations/0014_realtime_payload_warning.sql +0 -0
  215. {supython-0.1.13 → supython-0.1.14}/src/supython/migrations/0015_backups_schema.sql +0 -0
  216. {supython-0.1.13 → supython-0.1.14}/src/supython/migrations/0016_magic_link_redirect.sql +0 -0
  217. {supython-0.1.13 → supython-0.1.14}/src/supython/passwords.py +0 -0
  218. {supython-0.1.13 → supython-0.1.14}/src/supython/realtime/__init__.py +0 -0
  219. {supython-0.1.13 → supython-0.1.14}/src/supython/realtime/broker.py +0 -0
  220. {supython-0.1.13 → supython-0.1.14}/src/supython/realtime/protocol.py +0 -0
  221. {supython-0.1.13 → supython-0.1.14}/src/supython/realtime/router.py +0 -0
  222. {supython-0.1.13 → supython-0.1.14}/src/supython/realtime/schemas.py +0 -0
  223. {supython-0.1.13 → supython-0.1.14}/src/supython/realtime/service.py +0 -0
  224. {supython-0.1.13 → supython-0.1.14}/src/supython/realtime/topics.py +0 -0
  225. {supython-0.1.13 → supython-0.1.14}/src/supython/realtime/websocket.py +0 -0
  226. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/__init__.py +0 -0
  227. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/init_project.py +0 -0
  228. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/Caddyfile.tmpl +0 -0
  229. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/README.md.tmpl +0 -0
  230. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/apps_hooks_welcome.py.tmpl +0 -0
  231. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/apps_jobs_example.py.tmpl +0 -0
  232. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/asgi.py.tmpl +0 -0
  233. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/docker-compose.prod.yml.tmpl +0 -0
  234. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/docker-compose.yml.tmpl +0 -0
  235. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/docker_postgres_Dockerfile.tmpl +0 -0
  236. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/docker_postgres_postgresql.conf.tmpl +0 -0
  237. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/env.example.tmpl +0 -0
  238. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/functions_README.md.tmpl +0 -0
  239. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/gitignore.tmpl +0 -0
  240. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/manage.py.tmpl +0 -0
  241. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/package_hooks_init.py.tmpl +0 -0
  242. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/package_init.py.tmpl +0 -0
  243. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/package_jobs_init.py.tmpl +0 -0
  244. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/pyproject.toml.tmpl +0 -0
  245. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/settings.py.tmpl +0 -0
  246. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/static/0001_create_todos.sql +0 -0
  247. {supython-0.1.13 → supython-0.1.14}/src/supython/scaffold/templates/static/functions_hello.py +0 -0
  248. {supython-0.1.13 → supython-0.1.14}/src/supython/secretset.py +0 -0
  249. {supython-0.1.13 → supython-0.1.14}/src/supython/security_headers.py +0 -0
  250. {supython-0.1.13 → supython-0.1.14}/src/supython/settings.py +0 -0
  251. {supython-0.1.13 → supython-0.1.14}/src/supython/settings_module.py +0 -0
  252. {supython-0.1.13 → supython-0.1.14}/src/supython/storage/__init__.py +0 -0
  253. {supython-0.1.13 → supython-0.1.14}/src/supython/storage/backends.py +0 -0
  254. {supython-0.1.13 → supython-0.1.14}/src/supython/storage/router.py +0 -0
  255. {supython-0.1.13 → supython-0.1.14}/src/supython/storage/schemas.py +0 -0
  256. {supython-0.1.13 → supython-0.1.14}/src/supython/storage/service.py +0 -0
  257. {supython-0.1.13 → supython-0.1.14}/src/supython/storage/signing.py +0 -0
  258. {supython-0.1.13 → supython-0.1.14}/src/supython/tokens.py +0 -0
@@ -30,6 +30,30 @@ Each entry links the relevant `PROJECT.md` section and decision-log row
30
30
 
31
31
  ---
32
32
 
33
+ ## [0.1.14] — 2026-07-17
34
+
35
+ ### Added
36
+ - Account activation gate for pre-created users (GHSA-27m9-35j7-7g5f B). New
37
+ `auth.users.activated_at` column (migration `0017`) plus
38
+ `supython.auth.service.activate_user(conn, user_id)`: a consumer that
39
+ pre-creates an `auth.users` row (e.g. an invite flow that provisions the user
40
+ plus a role/membership up front) can keep it from authenticating through the
41
+ passwordless endpoints until an explicit activation step. Self-serve signup
42
+ and OAuth sign-in activate inline; existing rows are backfilled to
43
+ `created_at` so live users are unaffected.
44
+
45
+ ### Security
46
+ - Enforce account eligibility at session issuance (GHSA-27m9-35j7-7g5f). Every
47
+ grant type — password, refresh, magic-link, OTP, recover, OAuth — now checks
48
+ account eligibility before minting a token pair, at the `_issue_pair` funnel
49
+ and in `refresh_grant` (which mints its own pair), so an in-flight session
50
+ also dies at its next refresh. A banned account (`banned_until` in the future)
51
+ gets `403 account_disabled`; a not-yet-activated account (`activated_at is
52
+ null`) gets `403 account_inactive`. The `request_*` endpoints stay
53
+ enumeration-resistant (still `202` for a banned or inactive email).
54
+
55
+ ---
56
+
33
57
  ## [0.1.13] — 2026-07-16
34
58
 
35
59
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supython
3
- Version: 0.1.13
3
+ Version: 0.1.14
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "supython"
7
- version = "0.1.13"
7
+ version = "0.1.14"
8
8
  description = "A lightweight Postgres-first BaaS framework for Python"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -62,9 +62,46 @@ async def _audit_log(
62
62
  )
63
63
 
64
64
 
65
+ async def _assert_can_authenticate(
66
+ conn: asyncpg.Connection, user_id: UUID
67
+ ) -> None:
68
+ """Refuse session issuance for an account that is not eligible to sign in.
69
+
70
+ Enforced at the issuance funnel so every grant type — password, refresh,
71
+ magic-link, OTP, recover, OAuth — is gated by the same rules:
72
+
73
+ - ``banned_until`` in the future ⇒ ``account_disabled`` (403), deliberately
74
+ distinct from ``invalid_credentials`` (401) so a caller can tell "wrong
75
+ password" apart from "correct credentials, but locked out".
76
+ - ``activated_at is null`` ⇒ ``account_inactive`` (403). A consumer can
77
+ pre-create an ``auth.users`` row (e.g. an invite flow that provisions the
78
+ user plus a role/membership up front) that must not authenticate until an
79
+ explicit ``activate_user`` call; signup and OAuth sign-in activate inline.
80
+
81
+ Gating at issuance (rather than only at password verification) means the
82
+ check also lands on the passwordless verifies and on ``refresh_grant``, so
83
+ an in-flight session dies at its next refresh — near-immediate revocation
84
+ with no token blocklist.
85
+ """
86
+ row = await conn.fetchrow(
87
+ "select banned_until, activated_at from auth.users where id = $1",
88
+ user_id,
89
+ )
90
+ if row is None:
91
+ # The row vanished between the caller's lookup and issuance (e.g. a
92
+ # concurrent delete). Fail closed rather than mint an orphan session.
93
+ raise AuthError("account_disabled", "Account is not eligible to sign in", 403)
94
+ banned_until = row["banned_until"]
95
+ if banned_until is not None and banned_until > datetime.now(UTC):
96
+ raise AuthError("account_disabled", "Account is disabled", 403)
97
+ if row["activated_at"] is None:
98
+ raise AuthError("account_inactive", "Account is not activated", 403)
99
+
100
+
65
101
  async def _issue_pair(
66
102
  conn: asyncpg.Connection, user: UserResponse
67
103
  ) -> tuple[str, str, int]:
104
+ await _assert_can_authenticate(conn, user.id)
68
105
  extra = await claims.collect(user, conn)
69
106
  access, ttl = tokens.issue_access_token(
70
107
  user.id, user.email, extra_claims=extra or None
@@ -163,8 +200,8 @@ async def signup(
163
200
  pw_hash = passwords.hash_password(password)
164
201
  row = await conn.fetchrow(
165
202
  """
166
- insert into auth.users (email, encrypted_password, email_confirmed_at)
167
- values ($1, $2, now())
203
+ insert into auth.users (email, encrypted_password, email_confirmed_at, activated_at)
204
+ values ($1, $2, now(), now())
168
205
  returning id, email, created_at
169
206
  """,
170
207
  email,
@@ -205,12 +242,14 @@ async def password_grant(
205
242
  logger.warning("auth.password_grant: bad password for %s", email)
206
243
  raise AuthError("invalid_credentials", "Invalid email or password", 401)
207
244
 
245
+ user = _row_to_user(row)
246
+ # Issue first: _issue_pair raises account_disabled (403) for a banned user,
247
+ # so we don't stamp last_sign_in_at for a sign-in that was refused.
248
+ access, refresh, ttl = await _issue_pair(conn, user)
208
249
  await conn.execute(
209
250
  "update auth.users set last_sign_in_at = now() where id = $1",
210
251
  row["id"],
211
252
  )
212
- user = _row_to_user(row)
213
- access, refresh, ttl = await _issue_pair(conn, user)
214
253
  return user, access, refresh, ttl
215
254
 
216
255
 
@@ -272,6 +311,10 @@ async def refresh_grant(
272
311
  )
273
312
 
274
313
  user = _row_to_user(row)
314
+ # refresh_grant mints its own pair rather than routing through _issue_pair,
315
+ # so the eligibility gate has to be applied here too. Check before rotating:
316
+ # a banned user is refused without burning their current refresh token.
317
+ await _assert_can_authenticate(conn, user.id)
275
318
  new_refresh = tokens.issue_refresh_token()
276
319
  async with conn.transaction():
277
320
  await conn.execute(
@@ -309,6 +352,36 @@ async def get_user(
309
352
  return _row_to_user(row) if row else None
310
353
 
311
354
 
355
+ async def activate_user(conn: asyncpg.Connection, user_id: UUID) -> None:
356
+ """Mark a pre-created account eligible to authenticate.
357
+
358
+ A consumer that provisions an ``auth.users`` row up front (e.g. an invite
359
+ flow that creates the user plus a role/membership before the person has set
360
+ up credentials) calls this at its intended activation step. Until then the
361
+ row's ``activated_at`` is null and every session-issuing path refuses it with
362
+ ``account_inactive`` (403). Self-serve signup and OAuth sign-in activate
363
+ inline, so this is only needed for externally pre-created rows.
364
+
365
+ Idempotent: activating an already-active user is a no-op and does not move
366
+ the existing ``activated_at``. Raises ``AuthError('user_not_found', 404)``
367
+ when no such user exists, so a mistyped id surfaces instead of silently
368
+ doing nothing.
369
+ """
370
+ exists = await conn.fetchval(
371
+ "select 1 from auth.users where id = $1", user_id
372
+ )
373
+ if not exists:
374
+ raise AuthError("user_not_found", "User not found", 404)
375
+ await conn.execute(
376
+ """
377
+ update auth.users
378
+ set activated_at = now()
379
+ where id = $1 and activated_at is null
380
+ """,
381
+ user_id,
382
+ )
383
+
384
+
312
385
  async def request_recover(
313
386
  conn: asyncpg.Connection, email: str
314
387
  ) -> None:
@@ -668,5 +741,13 @@ async def oauth_finish(
668
741
  },
669
742
  )
670
743
 
744
+ # A successful OAuth exchange proves control of the external account, so it
745
+ # activates the user inline (no-op if already active). This also lets a
746
+ # consumer's pre-created invite row, matched here by email, come online via
747
+ # its first OAuth sign-in.
748
+ await conn.execute(
749
+ "update auth.users set activated_at = now() where id = $1 and activated_at is null",
750
+ user.id,
751
+ )
671
752
  access, refresh, ttl = await _issue_pair(conn, user)
672
753
  return user, access, refresh, ttl, redirect_uri
@@ -1487,7 +1487,7 @@ def _bootstrap_test_db(timeout: int) -> None:
1487
1487
  The named volume in `docker-compose.test.yml` makes the second call
1488
1488
  cheap — schema survives between pytest sessions.
1489
1489
  """
1490
- _compose_with(_TEST_COMPOSE_FILE, "up", "-d", "--wait", "db")
1490
+ _compose_with(_TEST_COMPOSE_FILE, "up", "-d", "--wait", "db", project_directory=".")
1491
1491
 
1492
1492
  prev = os.environ.get("DATABASE_URL")
1493
1493
  os.environ["DATABASE_URL"] = _TEST_DATABASE_URL
@@ -1531,7 +1531,7 @@ def test_up(
1531
1531
  @test_cli.command("down")
1532
1532
  def test_down() -> None:
1533
1533
  """Stop the test DB container. Keeps the volume (and migrations)."""
1534
- _compose_with(_TEST_COMPOSE_FILE, "down")
1534
+ _compose_with(_TEST_COMPOSE_FILE, "down", project_directory=".")
1535
1535
 
1536
1536
 
1537
1537
  @test_cli.command("reset")
@@ -1540,7 +1540,7 @@ def test_reset() -> None:
1540
1540
  confirm = typer.confirm("This will delete the test DB volume. Continue?")
1541
1541
  if not confirm:
1542
1542
  raise typer.Abort()
1543
- _compose_with(_TEST_COMPOSE_FILE, "down", "-v")
1543
+ _compose_with(_TEST_COMPOSE_FILE, "down", "-v", project_directory=".")
1544
1544
 
1545
1545
 
1546
1546
  @test_cli.command(
@@ -1667,7 +1667,7 @@ def _ensure_jwks_for_postgrest(s: Settings) -> None:
1667
1667
  typer.echo(f" JWT_JWKS_PATH={s.jwt_jwks_path}")
1668
1668
 
1669
1669
 
1670
- _TEST_COMPOSE_FILE = "docker-compose.test.yml"
1670
+ _TEST_COMPOSE_FILE = "tests/docker-compose.test.yml"
1671
1671
  _TEST_DATABASE_URL = "postgresql://supython:supython@localhost:54323/supython"
1672
1672
 
1673
1673
 
@@ -1676,12 +1676,17 @@ def _compose(*args: str) -> None:
1676
1676
 
1677
1677
 
1678
1678
  def _compose_with(
1679
- file: str | None, *args: str, profiles: tuple[str, ...] = ()
1679
+ file: str | None,
1680
+ *args: str,
1681
+ profiles: tuple[str, ...] = (),
1682
+ project_directory: str | None = None,
1680
1683
  ) -> None:
1681
1684
  """Run `docker compose [-f <file>] [--profile <p>]... <args...>`, surfacing common errors."""
1682
1685
  cmd = ["docker", "compose"]
1683
1686
  if file:
1684
1687
  cmd += ["-f", file]
1688
+ if project_directory:
1689
+ cmd += ["--project-directory", project_directory]
1685
1690
  for p in profiles:
1686
1691
  cmd += ["--profile", p]
1687
1692
  cmd += list(args)
@@ -0,0 +1,37 @@
1
+ -- Add a per-user activation gate. A consumer can pre-create an auth.users row
2
+ -- (e.g. an invite flow that provisions the user plus a role/membership before
3
+ -- the person has credentials); such a row must not authenticate through the
4
+ -- passwordless endpoints (magiclink / otp / recover) before the intended
5
+ -- activation step. `activated_at` is null while the account is inactive;
6
+ -- supython.auth.service._assert_can_authenticate refuses session issuance for
7
+ -- those rows with `account_inactive` (403).
8
+ --
9
+ -- Activation is explicit: supython.auth.service.activate_user(conn, user_id)
10
+ -- stamps it at the consumer's chosen step, while signup and OAuth sign-in stamp
11
+ -- it inline for self-serve users. Existing rows are backfilled to `created_at`
12
+ -- so live users keep authenticating across the migration.
13
+ --
14
+ -- The column-add and one-time backfill run together, guarded on the column not
15
+ -- yet existing, so a re-run can never re-activate a row that was intentionally
16
+ -- left inactive after the column was introduced.
17
+
18
+ do $$
19
+ begin
20
+ if not exists (
21
+ select 1
22
+ from information_schema.columns
23
+ where table_schema = 'auth'
24
+ and table_name = 'users'
25
+ and column_name = 'activated_at'
26
+ ) then
27
+ alter table auth.users
28
+ add column activated_at timestamptz;
29
+
30
+ update auth.users
31
+ set activated_at = created_at;
32
+ end if;
33
+ end $$;
34
+
35
+ create index if not exists users_activated_at_idx
36
+ on auth.users (activated_at)
37
+ where activated_at is null;
File without changes
File without changes
File without changes
File without changes