domainkit 0.6.1__tar.gz → 0.6.2__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 (220) hide show
  1. {domainkit-0.6.1 → domainkit-0.6.2}/PKG-INFO +1 -1
  2. {domainkit-0.6.1 → domainkit-0.6.2}/pyproject.toml +1 -1
  3. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/__init__.py +2 -0
  4. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/access.py +82 -22
  5. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/apps.py +6 -2
  6. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/middleware.py +18 -11
  7. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/repositories.py +10 -5
  8. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/idempotency/middleware.py +4 -4
  9. {domainkit-0.6.1 → domainkit-0.6.2}/LICENSE +0 -0
  10. {domainkit-0.6.1 → domainkit-0.6.2}/README.md +0 -0
  11. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/__init__.py +0 -0
  12. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/apps/__init__.py +0 -0
  13. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/apps/config.py +0 -0
  14. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/apps/registry.py +0 -0
  15. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/architecture.py +0 -0
  16. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/broker/__init__.py +0 -0
  17. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/broker/base.py +0 -0
  18. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/broker/handler.py +0 -0
  19. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/broker/memory.py +0 -0
  20. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/broker/rabbitmq.py +0 -0
  21. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/cache/__init__.py +0 -0
  22. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/cache/base.py +0 -0
  23. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/cache/core.py +0 -0
  24. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/cache/decorators.py +0 -0
  25. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/cache/handler.py +0 -0
  26. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/cache/memory.py +0 -0
  27. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/cache/metrics.py +0 -0
  28. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/cache/redis.py +0 -0
  29. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/conf/__init__.py +0 -0
  30. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/conf/env.py +0 -0
  31. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/conf/global_settings.py +0 -0
  32. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/conf/lazy.py +0 -0
  33. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/context.py +0 -0
  34. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/__init__.py +0 -0
  35. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/audit/__init__.py +0 -0
  36. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/audit/apps.py +0 -0
  37. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/audit/management/__init__.py +0 -0
  38. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/audit/management/commands/__init__.py +0 -0
  39. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/audit/management/commands/auditforget.py +0 -0
  40. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/audit/management/commands/auditlog.py +0 -0
  41. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/audit/management/commands/auditseal.py +0 -0
  42. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/audit/management/commands/auditverify.py +0 -0
  43. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/audit/personal.py +0 -0
  44. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/audit/protection.py +0 -0
  45. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/audit/recorder.py +0 -0
  46. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/audit/seals.py +0 -0
  47. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/audit/settings.py +0 -0
  48. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/audit/tables.py +0 -0
  49. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/audit/tasks.py +0 -0
  50. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/admin.py +0 -0
  51. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/api.py +0 -0
  52. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/authentication.py +0 -0
  53. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/backends.py +0 -0
  54. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/common-passwords.NOTICE +0 -0
  55. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/common-passwords.txt.gz +0 -0
  56. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/events.py +0 -0
  57. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/exceptions.py +0 -0
  58. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/hashers.py +0 -0
  59. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/management/__init__.py +0 -0
  60. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/management/commands/__init__.py +0 -0
  61. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/management/commands/changepassword.py +0 -0
  62. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/management/commands/cleartokens.py +0 -0
  63. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/management/commands/createsuperuser.py +0 -0
  64. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/management/commands/createtoken.py +0 -0
  65. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/models.py +0 -0
  66. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/permissions.py +0 -0
  67. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/settings.py +0 -0
  68. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/tables.py +0 -0
  69. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/tasks.py +0 -0
  70. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/tokens.py +0 -0
  71. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/uow.py +0 -0
  72. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/users.py +0 -0
  73. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/auth/validators.py +0 -0
  74. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/contenttypes/__init__.py +0 -0
  75. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/contenttypes/apps.py +0 -0
  76. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/contenttypes/models.py +0 -0
  77. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/contenttypes/registry.py +0 -0
  78. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/contenttypes/sync.py +0 -0
  79. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/contenttypes/tables.py +0 -0
  80. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/evidence/__init__.py +0 -0
  81. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/evidence/apps.py +0 -0
  82. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/evidence/management/__init__.py +0 -0
  83. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/evidence/management/commands/__init__.py +0 -0
  84. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/evidence/management/commands/evidence.py +0 -0
  85. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/evidence/model.py +0 -0
  86. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/evidence/settings.py +0 -0
  87. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/evidence/store.py +0 -0
  88. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/evidence/tasks.py +0 -0
  89. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/idempotency/__init__.py +0 -0
  90. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/idempotency/apps.py +0 -0
  91. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/idempotency/operations.py +0 -0
  92. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/idempotency/settings.py +0 -0
  93. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/idempotency/tables.py +0 -0
  94. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/idempotency/tasks.py +0 -0
  95. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/messaging/__init__.py +0 -0
  96. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/messaging/admin.py +0 -0
  97. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/messaging/apps.py +0 -0
  98. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/messaging/dispatch.py +0 -0
  99. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/messaging/management/__init__.py +0 -0
  100. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/messaging/management/commands/__init__.py +0 -0
  101. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/messaging/management/commands/messages.py +0 -0
  102. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/messaging/management/commands/runworker.py +0 -0
  103. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/messaging/metrics.py +0 -0
  104. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/messaging/outbox.py +0 -0
  105. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/messaging/registry.py +0 -0
  106. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/messaging/settings.py +0 -0
  107. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/messaging/tables.py +0 -0
  108. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/contrib/messaging/worker.py +0 -0
  109. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/db/__init__.py +0 -0
  110. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/db/connections.py +0 -0
  111. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/db/drivers.py +0 -0
  112. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/db/errors.py +0 -0
  113. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/db/migration_env/__init__.py +0 -0
  114. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/db/migration_env/env.py +0 -0
  115. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/db/migration_env/script.py.mako +0 -0
  116. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/db/migrations.py +0 -0
  117. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/db/orm.py +0 -0
  118. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/db/partitioning.py +0 -0
  119. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/db/repository.py +0 -0
  120. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/db/routing.py +0 -0
  121. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/db/snapshots.py +0 -0
  122. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/db/types.py +0 -0
  123. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/db/uow.py +0 -0
  124. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/ddd/__init__.py +0 -0
  125. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/ddd/_dataclass.py +0 -0
  126. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/ddd/events.py +0 -0
  127. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/ddd/exceptions.py +0 -0
  128. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/ddd/messages.py +0 -0
  129. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/ddd/model.py +0 -0
  130. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/ddd/pagination.py +0 -0
  131. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/ddd/personal.py +0 -0
  132. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/ddd/repository.py +0 -0
  133. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/ddd/uow.py +0 -0
  134. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/deprecation.py +0 -0
  135. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/exceptions.py +0 -0
  136. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/http/__init__.py +0 -0
  137. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/http/application.py +0 -0
  138. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/http/errors.py +0 -0
  139. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/http/health.py +0 -0
  140. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/http/metrics.py +0 -0
  141. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/http/middleware.py +0 -0
  142. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/http/pagination.py +0 -0
  143. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/http/ratelimit.py +0 -0
  144. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/ledger/__init__.py +0 -0
  145. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/ledger/base.py +0 -0
  146. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/ledger/handler.py +0 -0
  147. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/ledger/immudb.py +0 -0
  148. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/ledger/memory.py +0 -0
  149. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/lifecycle.py +0 -0
  150. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/log.py +0 -0
  151. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/__init__.py +0 -0
  152. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/base.py +0 -0
  153. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/commands/__init__.py +0 -0
  154. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/commands/broker.py +0 -0
  155. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/commands/cache.py +0 -0
  156. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/commands/check.py +0 -0
  157. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/commands/init.py +0 -0
  158. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/commands/makemigrations.py +0 -0
  159. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/commands/migrate.py +0 -0
  160. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/commands/partitions.py +0 -0
  161. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/commands/runserver.py +0 -0
  162. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/commands/shell.py +0 -0
  163. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/commands/showmigrations.py +0 -0
  164. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/commands/startdomain.py +0 -0
  165. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/__init__.py-tpl +0 -0
  166. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/api/__init__.py-tpl +0 -0
  167. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/api/dependencies.py-tpl +0 -0
  168. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/api/routes.py-tpl +0 -0
  169. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/api/schemas.py-tpl +0 -0
  170. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/application/__init__.py-tpl +0 -0
  171. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/application/commands.py-tpl +0 -0
  172. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/application/handlers.py-tpl +0 -0
  173. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/application/services.py-tpl +0 -0
  174. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/application/unit_of_work.py-tpl +0 -0
  175. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/apps.py-tpl +0 -0
  176. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/domain/__init__.py-tpl +0 -0
  177. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/domain/events.py-tpl +0 -0
  178. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/domain/exceptions.py-tpl +0 -0
  179. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/domain/model.py-tpl +0 -0
  180. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/domain/repositories.py-tpl +0 -0
  181. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/infrastructure/__init__.py-tpl +0 -0
  182. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/infrastructure/orm.py-tpl +0 -0
  183. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/infrastructure/repositories.py-tpl +0 -0
  184. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/infrastructure/unit_of_work.py-tpl +0 -0
  185. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/management/__init__.py-tpl +0 -0
  186. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/management/commands/__init__.py-tpl +0 -0
  187. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/management/commands/{{ aggregates }}.py-tpl +0 -0
  188. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/package/settings.py-tpl +0 -0
  189. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/tests/__init__.py-tpl +0 -0
  190. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/tests/fakes.py-tpl +0 -0
  191. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/tests/test_api.py-tpl +0 -0
  192. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/tests/test_model.py-tpl +0 -0
  193. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/tests/test_services.py-tpl +0 -0
  194. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/domain/tests/test_settings.py-tpl +0 -0
  195. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/project/dot-env-tpl +0 -0
  196. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/project/dot-gitignore-tpl +0 -0
  197. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/project/manage.py-tpl +0 -0
  198. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/project/pyproject.toml-tpl +0 -0
  199. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/project/src/config/__init__.py-tpl +0 -0
  200. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/project/src/config/asgi.py-tpl +0 -0
  201. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/project/src/config/settings/__init__.py-tpl +0 -0
  202. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/project/src/config/settings/base.py-tpl +0 -0
  203. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/project/src/config/settings/local.py-tpl +0 -0
  204. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/project/src/config/settings/production.py-tpl +0 -0
  205. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/project/src/config/settings/test.py-tpl +0 -0
  206. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/project/src/config/urls.py-tpl +0 -0
  207. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/project/src/domains/__init__.py-tpl +0 -0
  208. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/project/tests/__init__.py-tpl +0 -0
  209. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/management/templates/project/tests/test_health.py-tpl +0 -0
  210. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/py.typed +0 -0
  211. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/reporting.py +0 -0
  212. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/testing.py +0 -0
  213. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/tracing.py +0 -0
  214. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/utils/__init__.py +0 -0
  215. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/utils/closing.py +0 -0
  216. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/utils/formatting.py +0 -0
  217. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/utils/module_loading.py +0 -0
  218. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/utils/rates.py +0 -0
  219. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/utils/readiness.py +0 -0
  220. {domainkit-0.6.1 → domainkit-0.6.2}/src/domainkit/utils/text.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: domainkit
3
- Version: 0.6.1
3
+ Version: 0.6.2
4
4
  Summary: A Django-style framework for domain-driven FastAPI services.
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "domainkit"
3
- version = "0.6.1"
3
+ version = "0.6.2"
4
4
  description = "A Django-style framework for domain-driven FastAPI services."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -46,6 +46,7 @@ from .access import (
46
46
  Require,
47
47
  check_object_permissions,
48
48
  current_user,
49
+ mark_public,
49
50
  open_routes,
50
51
  )
51
52
  from .authentication import (
@@ -165,6 +166,7 @@ __all__ = [
165
166
  "has_perm",
166
167
  "has_perms",
167
168
  "make_password",
169
+ "mark_public",
168
170
  "open_routes",
169
171
  "password_help_texts",
170
172
  "validate_password",
@@ -25,7 +25,7 @@ A refused anonymous caller gets 401, so it knows to log in; a refused user
25
25
  gets 403. ``AuthenticationMiddleware`` must be installed.
26
26
  """
27
27
 
28
- from collections.abc import Sequence
28
+ from collections.abc import Iterator, Sequence
29
29
  from typing import Annotated, Any, ClassVar, Final, cast
30
30
 
31
31
  from fastapi import Depends, FastAPI, Request, status
@@ -33,6 +33,7 @@ from fastapi.dependencies.models import Dependant
33
33
  from fastapi.routing import APIRoute, APIWebSocketRoute, iter_route_contexts
34
34
  from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
35
35
  from starlette.requests import HTTPConnection
36
+ from starlette.routing import BaseRoute, Host, Mount, Route, WebSocketRoute
36
37
 
37
38
  from domainkit.ddd import PermissionDenied
38
39
  from domainkit.exceptions import ImproperlyConfigured
@@ -43,8 +44,10 @@ from .backends import has_perms
43
44
  from .models import AbstractBaseUser, AnyUser
44
45
 
45
46
  SAFE_METHODS: Final = frozenset({"GET", "HEAD", "OPTIONS"})
46
- FRAMEWORK_ROUTES: Final = "domainkit.http."
47
- """Modules whose routes (probes, metrics) guard themselves."""
47
+ FRAMEWORK_ROUTES: Final = ("domainkit.http.", "fastapi.")
48
+ """Modules whose routes guard themselves (probes, metrics, ``API_DOCS``)."""
49
+ PUBLIC_MARK: Final = "domainkit_public"
50
+ """The attribute ``mark_public`` sets."""
48
51
  MODEL_ACTIONS: Final = {
49
52
  "GET": "view",
50
53
  "HEAD": "view",
@@ -384,46 +387,103 @@ USER_GUARDS: Final[tuple[object, ...]] = (current_user, authenticated_user)
384
387
  """Dependencies that read the user, besides ``Require``."""
385
388
 
386
389
 
390
+ def mark_public[T](served: T) -> T:
391
+ """Mark a mounted application or a Starlette endpoint public on purpose.
392
+
393
+ They take no dependencies, so ``Public`` cannot sit on them, and
394
+ ``open_routes`` lists them otherwise::
395
+
396
+ app.mount("/static", mark_public(StaticFiles(directory="static")))
397
+ """
398
+ setattr(served, PUBLIC_MARK, True)
399
+ return served
400
+
401
+
387
402
  def open_routes(app: FastAPI) -> list[str]:
388
403
  """``METHOD /path`` of every route that declares no access rule.
389
404
 
390
405
  A route declares one with ``Require``, ``CurrentUser``,
391
406
  ``AuthenticatedUser``, or ``Public``, on itself or its router. The
392
- framework's own routes (``/health``, ``/ready``, ``/metrics``) guard
393
- themselves.
407
+ framework's own routes (``/health``, ``/ready``, ``/metrics``, and the
408
+ ``API_DOCS``) guard themselves. Mounted FastAPI and Starlette
409
+ applications are walked route by route. What takes no dependencies (a
410
+ mounted ``StaticFiles``, Starlette's own ``Route``) is listed unless
411
+ marked with ``mark_public``.
394
412
  """
413
+ return [name for name, _ in unguarded_routes(app)]
414
+
415
+
416
+ def unguarded_routes(app: FastAPI) -> list[tuple[str, bool]]:
417
+ """``open_routes``, each with whether it could take a rule (a dependency)."""
395
418
  guards = (public, *USER_GUARDS)
396
- return [name for name, dependant in _routes(app) if not _uses(dependant, guards)]
419
+ return [
420
+ (name, dependant is not None)
421
+ for name, dependant in _routes(app.routes)
422
+ if dependant is None or not _uses(dependant, guards)
423
+ ]
397
424
 
398
425
 
399
426
  def routes_need_users(app: FastAPI) -> bool:
400
427
  """Whether some route of ``app`` reads the user or checks rules."""
401
- return any(_uses(dependant, USER_GUARDS) for _, dependant in _routes(app))
428
+ return any(
429
+ dependant is not None and _uses(dependant, USER_GUARDS)
430
+ for _, dependant in _routes(app.routes)
431
+ )
402
432
 
403
433
 
404
- def _routes(app: FastAPI) -> list[tuple[str, Dependant]]:
434
+ def _routes(
435
+ routes: Sequence[BaseRoute], prefix: str = ""
436
+ ) -> Iterator[tuple[str, Dependant | None]]:
405
437
  """``METHOD /path`` and dependencies of each route, but the framework's own.
406
438
 
407
439
  Routers are walked as OpenAPI walks them, so the prefixes and
408
- dependencies of the routers a route is included through count.
440
+ dependencies of the routers a route is included through count, and so
441
+ are the applications mounted with routes of their own. What takes no
442
+ dependencies (an application without routes, Starlette's own routes)
443
+ has None, unless it was marked public.
409
444
  """
410
- found = []
411
- for context in iter_route_contexts(app.routes):
445
+ for context in iter_route_contexts(routes):
412
446
  route = context.original_route
447
+ # Routes of included routers keep their full path (and a WebSocket
448
+ # its dependencies) here; FastAPI's own context has them for others.
449
+ effective = getattr(context, "starlette_route", None)
450
+ if isinstance(effective, type(route)):
451
+ route = effective
452
+ found: tuple[str, Dependant | None]
413
453
  if isinstance(route, APIWebSocketRoute):
414
- # Included WebSockets keep their full path and dependencies here.
415
- effective = getattr(context, "starlette_route", None)
416
- if isinstance(effective, APIWebSocketRoute):
417
- route = effective
418
- name, dependant = f"WEBSOCKET {route.path}", route.dependant
454
+ found = (f"WEBSOCKET {prefix}{route.path}", route.dependant)
419
455
  elif isinstance(route, APIRoute):
420
- name = f"{' '.join(sorted(route.methods or ()))} {context.path}"
421
- dependant = context.dependant
422
- else:
456
+ methods = " ".join(sorted(route.methods or ()))
457
+ found = (f"{methods} {prefix}{context.path}", context.dependant)
458
+ elif _marked_public(route):
423
459
  continue
424
- if not route.endpoint.__module__.startswith(FRAMEWORK_ROUTES):
425
- found.append((name, dependant))
426
- return found
460
+ elif isinstance(route, Mount | Host) and route.routes:
461
+ inner = prefix + route.path if isinstance(route, Mount) else prefix
462
+ yield from _routes(route.routes, inner)
463
+ continue
464
+ else:
465
+ path = getattr(route, "path", "")
466
+ found = (f"{_kind(route)} {prefix}{path}".rstrip(), None)
467
+ endpoint = getattr(route, "endpoint", None)
468
+ if not getattr(endpoint, "__module__", "").startswith(FRAMEWORK_ROUTES):
469
+ yield found
470
+
471
+
472
+ def _marked_public(route: BaseRoute) -> bool:
473
+ """Whether what ``route`` serves was marked with ``mark_public``."""
474
+ served = (getattr(route, name, None) for name in ("endpoint", "app", "_base_app"))
475
+ return any(getattr(target, PUBLIC_MARK, False) is True for target in served)
476
+
477
+
478
+ def _kind(route: BaseRoute) -> str:
479
+ """How ``open_routes`` names a route that takes no dependencies."""
480
+ if isinstance(route, Route):
481
+ return " ".join(sorted(route.methods)) if route.methods else "ANY"
482
+ if isinstance(route, WebSocketRoute):
483
+ return "WEBSOCKET"
484
+ if isinstance(route, Host):
485
+ return f"HOST {route.host}"
486
+ return "MOUNT" if isinstance(route, Mount) else type(route).__name__
427
487
 
428
488
 
429
489
  def _uses(dependant: Dependant, guards: tuple[object, ...]) -> bool:
@@ -16,7 +16,7 @@ from domainkit.db.routing import router
16
16
  from domainkit.exceptions import ImproperlyConfigured
17
17
  from domainkit.utils.module_loading import import_string
18
18
 
19
- from .access import open_routes, routes_need_users
19
+ from .access import routes_need_users, unguarded_routes
20
20
  from .authentication import get_authenticators
21
21
  from .backends import get_backends
22
22
  from .hashers import insecure_hashers
@@ -97,7 +97,11 @@ class AuthConfig(DomainConfig):
97
97
  problems.extend(
98
98
  f"{route} declares no access rule: add Depends(Require(...)), or "
99
99
  "dependencies=[Public] if anyone may call it."
100
- for route in open_routes(app)
100
+ if takes_rules
101
+ else f"{route} takes no access rule: serve it from a FastAPI route "
102
+ "with Require(...), or wrap it in mark_public(...) if anyone may "
103
+ "call it."
104
+ for route, takes_rules in unguarded_routes(app)
101
105
  )
102
106
  return problems
103
107
 
@@ -22,7 +22,9 @@ callers may do (``domainkit.contrib.auth.access``).
22
22
  Refused credentials never reach ``RateLimitMiddleware``, so they are
23
23
  counted here, per client address, against ``RATE_LIMIT``. Past it, the
24
24
  address gets 429, and its requests with an ``Authorization`` header are
25
- refused before any lookup until ``Retry-After`` has passed.
25
+ refused before any lookup until ``Retry-After`` has passed. Credentials in
26
+ other headers, which only a project's own class reads, are refused too,
27
+ once read, even when they hold: a guess that works gets the same 429.
26
28
  """
27
29
 
28
30
  import logging
@@ -83,18 +85,20 @@ class AuthenticationMiddleware:
83
85
  await self.app(scope, receive, send)
84
86
  return
85
87
  connection = HTTPConnection(scope)
86
- if (
87
- self.rate is not None
88
- and "authorization" in connection.headers
89
- and (held := self._held(address_key(scope))) is not None
90
- ):
91
- await refuse(scope, receive, send, self.rate[0], held)
88
+ held = self._held(address_key(scope))
89
+ if held is not None and "authorization" in connection.headers:
90
+ await refuse(scope, receive, send, *held)
92
91
  return
93
92
  try:
94
93
  credentials = await self._authenticate(connection)
95
94
  except _Refused as refused:
96
95
  await self._refuse(scope, receive, send, refused)
97
96
  return
97
+ if held is not None and credentials is not None:
98
+ # Credentials in another header, read by a project's own class,
99
+ # are refused once read: a guess that works must not stand out.
100
+ await refuse(scope, receive, send, *held)
101
+ return
98
102
  scope["user"] = AnonymousUser() if credentials is None else credentials.user
99
103
  scope["auth"] = credentials
100
104
  context = current_request()
@@ -151,16 +155,19 @@ class AuthenticationMiddleware:
151
155
  if len(self.held) > MAX_HELD:
152
156
  self.held.popitem(last=False)
153
157
 
154
- def _held(self, address: str) -> Decision | None:
155
- """The refusal ``address`` is still held to, if any."""
158
+ def _held(self, address: str) -> tuple[int, Decision] | None:
159
+ """The limit and the refusal ``address`` is still held to, if any."""
156
160
  until = self.held.get(address)
157
- if until is None:
161
+ if until is None or self.rate is None:
158
162
  return None
159
163
  left = until - monotonic()
160
164
  if left <= 0:
161
165
  del self.held[address]
162
166
  return None
163
- return Decision(allowed=False, remaining=0, reset_after=left, retry_after=left)
167
+ refusal = Decision(
168
+ allowed=False, remaining=0, reset_after=left, retry_after=left
169
+ )
170
+ return self.rate[0], refusal
164
171
 
165
172
 
166
173
  class _Refused(Exception):
@@ -129,15 +129,20 @@ class UserRepository(SqlAlchemyRepository[AbstractBaseUser, UserId]):
129
129
  )
130
130
 
131
131
  async def active_superusers(self) -> int:
132
- """How many active superusers there are."""
132
+ """How many active superusers there are, locked until the commit.
133
+
134
+ Two transactions that each demote one of the last two superusers
135
+ would both count two; with the rows locked, the second count waits
136
+ for the first transaction and sees what it left.
137
+ """
133
138
  table = get_user_table()
134
139
  found = await self.session.execute(
135
- select(func.count())
136
- .select_from(table)
140
+ select(table.c.id)
137
141
  .where(table.c.is_superuser.is_(True), table.c.is_active.is_(True))
142
+ .order_by(table.c.id)
143
+ .with_for_update()
138
144
  )
139
- count: int = found.scalar_one()
140
- return count
145
+ return len(found.all())
141
146
 
142
147
  async def record_login(self, user: AbstractBaseUser) -> None:
143
148
  """Store the login time and record ``UserLoggedIn``.
@@ -213,7 +213,7 @@ class IdempotencyMiddleware:
213
213
  )
214
214
  else:
215
215
  headers = [
216
- (name.encode(), value.encode())
216
+ (name.encode("latin-1"), value.encode("latin-1"))
217
217
  for name, value in (existing["headers"] or {}).items()
218
218
  ]
219
219
  await send(
@@ -287,10 +287,10 @@ async def _finish(
287
287
  )
288
288
  if released.rowcount:
289
289
  return
290
- headers = {
291
- name.decode().lower(): value.decode()
290
+ headers = { # ASGI header values are Latin-1 bytes, not always UTF-8.
291
+ name.decode("latin-1").lower(): value.decode("latin-1")
292
292
  for name, value in started.get("headers", [])
293
- if name.decode().lower() in _STORED_HEADERS
293
+ if name.decode("latin-1").lower() in _STORED_HEADERS
294
294
  }
295
295
  await connection.execute(
296
296
  update(idempotency_keys)
File without changes
File without changes