diracx 0.0.1a49__tar.gz → 0.0.1a50__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 (451) hide show
  1. diracx-0.0.1a50/.github/workflows/generate_pixi_tasks_doc.py +61 -0
  2. {diracx-0.0.1a49 → diracx-0.0.1a50}/.github/workflows/main.yml +0 -1
  3. {diracx-0.0.1a49 → diracx-0.0.1a50}/.pre-commit-config.yaml +14 -2
  4. {diracx-0.0.1a49 → diracx-0.0.1a50}/.readthedocs.yaml +11 -3
  5. {diracx-0.0.1a49 → diracx-0.0.1a50}/PKG-INFO +1 -1
  6. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/pyproject.toml +1 -1
  7. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/src/diracx/core/config/schema.py +14 -5
  8. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/auth/schema.py +6 -2
  9. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/utils/__init__.py +15 -11
  10. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/utils/base.py +33 -1
  11. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/tests/auth/test_refresh_token.py +0 -2
  12. diracx-0.0.1a50/diracx-db/tests/utils/test_uuid.py +221 -0
  13. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/pyproject.toml +1 -1
  14. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/src/diracx/logic/auth/token.py +16 -12
  15. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/src/diracx/logic/jobs/utils.py +1 -1
  16. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/auth/authorize_code_flow.py +1 -30
  17. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/auth/device_flow.py +1 -50
  18. diracx-0.0.1a50/docs/SECURITY.md +4 -0
  19. diracx-0.0.1a50/docs/admin/explanations/auth-with-diracx.md +324 -0
  20. diracx-0.0.1a50/docs/admin/explanations/auth-with-external.md +32 -0
  21. diracx-0.0.1a50/docs/admin/how-to/install/embracing.md +128 -0
  22. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/admin/how-to/install/index.md +1 -0
  23. diracx-0.0.1a50/docs/admin/how-to/install/minimal-requirements.md +15 -0
  24. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/admin/how-to/install/register-a-vo.md +6 -4
  25. diracx-0.0.1a50/docs/admin/reference/env-variables.md +39 -0
  26. diracx-0.0.1a50/docs/admin/reference/settings-and-preferences.md +7 -0
  27. {diracx-0.0.1a49/docs/dev/explanations → diracx-0.0.1a50/docs/dev/explanations/components}/client.md +3 -3
  28. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/explanations/components/db.md +1 -1
  29. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/explanations/components/index.md +2 -2
  30. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/explanations/components/routes.md +7 -5
  31. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/explanations/extensions.md +19 -10
  32. diracx-0.0.1a50/docs/dev/how-to/client-generation.md +13 -0
  33. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/how-to/contribute.md +2 -2
  34. diracx-0.0.1a50/docs/dev/reference/entrypoints.md +42 -0
  35. diracx-0.0.1a50/docs/dev/reference/env-variables.md +5 -0
  36. diracx-0.0.1a50/docs/dev/reference/pixi-tasks.md +53 -0
  37. diracx-0.0.1a50/docs/user/index.md +10 -0
  38. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/user/reference/known-installations.md +12 -2
  39. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/containers/services/Dockerfile +2 -1
  40. {diracx-0.0.1a49 → diracx-0.0.1a50}/mkdocs.yml +6 -5
  41. {diracx-0.0.1a49 → diracx-0.0.1a50}/pixi.toml +22 -2
  42. diracx-0.0.1a50/release.notes.new +22 -0
  43. {diracx-0.0.1a49 → diracx-0.0.1a50}/tests/make_token_local.py +6 -4
  44. diracx-0.0.1a49/docs/SECURITY.md +0 -3
  45. diracx-0.0.1a49/docs/admin/explanations/authentication.md +0 -3
  46. diracx-0.0.1a49/docs/admin/how-to/install/embracing.md +0 -13
  47. diracx-0.0.1a49/docs/admin/reference/settings-and-preferences.md +0 -0
  48. diracx-0.0.1a49/docs/assets/images/pic02.jpg +0 -0
  49. diracx-0.0.1a49/docs/dev/how-to/client-generation.md +0 -2
  50. diracx-0.0.1a49/docs/dev/reference/entrypoints.md +0 -3
  51. diracx-0.0.1a49/docs/dev/reference/pixi-tasks.md +0 -3
  52. diracx-0.0.1a49/docs/user/index.md +0 -3
  53. diracx-0.0.1a49/release.notes.new +0 -43
  54. {diracx-0.0.1a49 → diracx-0.0.1a50}/.coveragerc +0 -0
  55. {diracx-0.0.1a49 → diracx-0.0.1a50}/.gitattributes +0 -0
  56. {diracx-0.0.1a49 → diracx-0.0.1a50}/.github/dependabot.yml +0 -0
  57. {diracx-0.0.1a49 → diracx-0.0.1a50}/.github/workflows/deployment.yml +0 -0
  58. {diracx-0.0.1a49 → diracx-0.0.1a50}/.github/workflows/integration.yml +0 -0
  59. {diracx-0.0.1a49 → diracx-0.0.1a50}/.github/workflows/make_release.py +0 -0
  60. {diracx-0.0.1a49 → diracx-0.0.1a50}/.github/workflows/update_chart_version.py +0 -0
  61. {diracx-0.0.1a49 → diracx-0.0.1a50}/.github/workflows/update_security_txt_expiry.yml +0 -0
  62. {diracx-0.0.1a49 → diracx-0.0.1a50}/.github/workflows/vulnerabilities.yml +0 -0
  63. {diracx-0.0.1a49 → diracx-0.0.1a50}/.gitignore +0 -0
  64. {diracx-0.0.1a49 → diracx-0.0.1a50}/LICENSE +0 -0
  65. {diracx-0.0.1a49 → diracx-0.0.1a50}/MANIFEST.in +0 -0
  66. {diracx-0.0.1a49 → diracx-0.0.1a50}/README.md +0 -0
  67. {diracx-0.0.1a49 → diracx-0.0.1a50}/codecov.yml +0 -0
  68. {diracx-0.0.1a49 → diracx-0.0.1a50}/containers/client/Dockerfile +0 -0
  69. {diracx-0.0.1a49 → diracx-0.0.1a50}/containers/services/Dockerfile +0 -0
  70. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-api/README.md +0 -0
  71. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-api/pyproject.toml +0 -0
  72. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-api/src/diracx/api/__init__.py +0 -0
  73. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-api/src/diracx/api/jobs.py +0 -0
  74. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-api/src/diracx/api/py.typed +0 -0
  75. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-api/src/diracx/api/utils.py +0 -0
  76. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-api/tests/test_jobs.py +0 -0
  77. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-api/tests/test_utils.py +0 -0
  78. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/README.md +0 -0
  79. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/pyproject.toml +0 -0
  80. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/src/diracx/cli/__init__.py +0 -0
  81. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/src/diracx/cli/__main__.py +0 -0
  82. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/src/diracx/cli/auth.py +0 -0
  83. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/src/diracx/cli/config.py +0 -0
  84. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/src/diracx/cli/internal/__init__.py +0 -0
  85. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/src/diracx/cli/internal/config.py +0 -0
  86. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/src/diracx/cli/internal/legacy.py +0 -0
  87. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/src/diracx/cli/jobs.py +0 -0
  88. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/src/diracx/cli/py.typed +0 -0
  89. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/src/diracx/cli/utils.py +0 -0
  90. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/tests/legacy/cs_sync/integration_test.cfg +0 -0
  91. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/tests/legacy/cs_sync/integration_test.yaml +0 -0
  92. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/tests/legacy/cs_sync/integration_test_buggy.cfg +0 -0
  93. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/tests/legacy/cs_sync/integration_test_secret.cfg +0 -0
  94. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/tests/legacy/cs_sync/test_cssync.py +0 -0
  95. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/tests/legacy/test_legacy.py +0 -0
  96. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/tests/test_internal.py +0 -0
  97. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/tests/test_jobs.py +0 -0
  98. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-cli/tests/test_login.py +0 -0
  99. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/README.md +0 -0
  100. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/pyproject.toml +0 -0
  101. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/_diracx_client_importer.pth +0 -0
  102. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/_client_importer.py +0 -0
  103. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/__init__.py +0 -0
  104. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/__init__.py +0 -0
  105. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/_client.py +0 -0
  106. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/_configuration.py +0 -0
  107. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/_patch.py +0 -0
  108. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/_serialization.py +0 -0
  109. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/_utils/__init__.py +0 -0
  110. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/_utils/serialization.py +0 -0
  111. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/_utils/utils.py +0 -0
  112. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/_vendor.py +0 -0
  113. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/aio/__init__.py +0 -0
  114. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/aio/_client.py +0 -0
  115. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/aio/_configuration.py +0 -0
  116. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/aio/_patch.py +0 -0
  117. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/aio/_vendor.py +0 -0
  118. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/aio/operations/__init__.py +0 -0
  119. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/aio/operations/_operations.py +0 -0
  120. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/aio/operations/_patch.py +0 -0
  121. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/models/__init__.py +0 -0
  122. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/models/_enums.py +0 -0
  123. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/models/_models.py +0 -0
  124. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/models/_patch.py +0 -0
  125. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/operations/__init__.py +0 -0
  126. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/operations/_operations.py +0 -0
  127. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/operations/_patch.py +0 -0
  128. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/_generated/py.typed +0 -0
  129. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/aio.py +0 -0
  130. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/models.py +0 -0
  131. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/patches/auth/aio.py +0 -0
  132. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/patches/auth/common.py +0 -0
  133. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/patches/auth/sync.py +0 -0
  134. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/patches/client/aio.py +0 -0
  135. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/patches/client/common.py +0 -0
  136. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/patches/client/sync.py +0 -0
  137. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/patches/jobs/aio.py +0 -0
  138. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/patches/jobs/common.py +0 -0
  139. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/patches/jobs/sync.py +0 -0
  140. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/patches/utils.py +0 -0
  141. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/py.typed +0 -0
  142. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/src/diracx/client/sync.py +0 -0
  143. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-client/tests/test_auth.py +0 -0
  144. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/README.md +0 -0
  145. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/src/diracx/core/__init__.py +0 -0
  146. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/src/diracx/core/config/__init__.py +0 -0
  147. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/src/diracx/core/config/sources.py +0 -0
  148. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/src/diracx/core/exceptions.py +0 -0
  149. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/src/diracx/core/extensions.py +0 -0
  150. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/src/diracx/core/models.py +0 -0
  151. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/src/diracx/core/preferences.py +0 -0
  152. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/src/diracx/core/properties.py +0 -0
  153. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/src/diracx/core/py.typed +0 -0
  154. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/src/diracx/core/resources.py +0 -0
  155. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/src/diracx/core/s3.py +0 -0
  156. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/src/diracx/core/settings.py +0 -0
  157. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/src/diracx/core/utils.py +0 -0
  158. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/tests/test_config_source.py +0 -0
  159. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/tests/test_entry_points.py +0 -0
  160. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/tests/test_extensions.py +0 -0
  161. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/tests/test_resources.py +0 -0
  162. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/tests/test_s3.py +0 -0
  163. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/tests/test_secrets.py +0 -0
  164. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-core/tests/test_utils.py +0 -0
  165. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/README.md +0 -0
  166. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/pyproject.toml +0 -0
  167. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/__init__.py +0 -0
  168. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/__main__.py +0 -0
  169. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/exceptions.py +0 -0
  170. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/os/__init__.py +0 -0
  171. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/os/job_parameters.py +0 -0
  172. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/os/utils.py +0 -0
  173. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/py.typed +0 -0
  174. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/__init__.py +0 -0
  175. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/auth/__init__.py +0 -0
  176. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/auth/db.py +0 -0
  177. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/dummy/__init__.py +0 -0
  178. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/dummy/db.py +0 -0
  179. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/dummy/schema.py +0 -0
  180. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/job/__init__.py +0 -0
  181. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/job/db.py +0 -0
  182. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/job/schema.py +0 -0
  183. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/job_logging/__init__.py +0 -0
  184. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/job_logging/db.py +0 -0
  185. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/job_logging/schema.py +0 -0
  186. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/pilot_agents/__init__.py +0 -0
  187. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/pilot_agents/db.py +0 -0
  188. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/pilot_agents/schema.py +0 -0
  189. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/sandbox_metadata/__init__.py +0 -0
  190. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/sandbox_metadata/db.py +0 -0
  191. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/sandbox_metadata/schema.py +0 -0
  192. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/task_queue/__init__.py +0 -0
  193. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/task_queue/db.py +0 -0
  194. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/task_queue/schema.py +0 -0
  195. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/utils/functions.py +0 -0
  196. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/src/diracx/db/sql/utils/types.py +0 -0
  197. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/tests/auth/test_authorization_flow.py +0 -0
  198. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/tests/auth/test_device_flow.py +0 -0
  199. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/tests/jobs/test_job_db.py +0 -0
  200. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/tests/jobs/test_job_logging_db.py +0 -0
  201. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/tests/jobs/test_sandbox_metadata.py +0 -0
  202. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/tests/opensearch/test_connection.py +0 -0
  203. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/tests/opensearch/test_index_template.py +0 -0
  204. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/tests/opensearch/test_search.py +0 -0
  205. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/tests/pilot_agents/__init__.py +0 -0
  206. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/tests/pilot_agents/test_pilot_agents_db.py +0 -0
  207. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/tests/test_dummy_db.py +0 -0
  208. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-db/tests/test_freeze_time.py +0 -0
  209. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/README.md +0 -0
  210. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/src/diracx/logic/__init__.py +0 -0
  211. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/src/diracx/logic/__main__.py +0 -0
  212. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/src/diracx/logic/auth/__init__.py +0 -0
  213. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/src/diracx/logic/auth/authorize_code_flow.py +0 -0
  214. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/src/diracx/logic/auth/device_flow.py +0 -0
  215. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/src/diracx/logic/auth/management.py +0 -0
  216. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/src/diracx/logic/auth/utils.py +0 -0
  217. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/src/diracx/logic/auth/well_known.py +0 -0
  218. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/src/diracx/logic/jobs/__init__.py +0 -0
  219. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/src/diracx/logic/jobs/query.py +0 -0
  220. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/src/diracx/logic/jobs/sandboxes.py +0 -0
  221. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/src/diracx/logic/jobs/status.py +0 -0
  222. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/src/diracx/logic/jobs/submission.py +0 -0
  223. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/src/diracx/logic/py.typed +0 -0
  224. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/src/diracx/logic/task_queues/__init__.py +0 -0
  225. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/src/diracx/logic/task_queues/priority.py +0 -0
  226. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/tests/jobs/test_sandboxes.py +0 -0
  227. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-logic/tests/jobs/test_status.py +0 -0
  228. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/README.md +0 -0
  229. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/pyproject.toml +0 -0
  230. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/__init__.py +0 -0
  231. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/access_policies.py +0 -0
  232. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/auth/__init__.py +0 -0
  233. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/auth/management.py +0 -0
  234. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/auth/token.py +0 -0
  235. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/auth/utils.py +0 -0
  236. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/auth/well_known.py +0 -0
  237. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/configuration.py +0 -0
  238. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/dependencies.py +0 -0
  239. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/factory.py +0 -0
  240. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/fastapi_classes.py +0 -0
  241. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/health/__init__.py +0 -0
  242. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/health/probes.py +0 -0
  243. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/jobs/__init__.py +0 -0
  244. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/jobs/access_policies.py +0 -0
  245. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/jobs/legacy.py +0 -0
  246. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/jobs/query.py +0 -0
  247. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/jobs/sandboxes.py +0 -0
  248. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/jobs/status.py +0 -0
  249. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/jobs/submission.py +0 -0
  250. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/otel.py +0 -0
  251. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/py.typed +0 -0
  252. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/utils/__init__.py +0 -0
  253. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/src/diracx/routers/utils/users.py +0 -0
  254. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/tests/auth/test_legacy_exchange.py +0 -0
  255. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/tests/auth/test_standard.py +0 -0
  256. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/tests/data/idp-server.invalid/.well-known/openid-configuration +0 -0
  257. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/tests/health/test_probes.py +0 -0
  258. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/tests/jobs/conftest.py +0 -0
  259. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/tests/jobs/test_query.py +0 -0
  260. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/tests/jobs/test_sandboxes.py +0 -0
  261. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/tests/jobs/test_status.py +0 -0
  262. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/tests/jobs/test_wms_access_policy.py +0 -0
  263. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/tests/test_config_manager.py +0 -0
  264. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/tests/test_generic.py +0 -0
  265. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-routers/tests/test_policy.py +0 -0
  266. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-testing/README.md +0 -0
  267. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-testing/pyproject.toml +0 -0
  268. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-testing/src/diracx/testing/__init__.py +0 -0
  269. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-testing/src/diracx/testing/__main__.py +0 -0
  270. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-testing/src/diracx/testing/client_generation.py +0 -0
  271. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-testing/src/diracx/testing/client_generation_pytest.py +0 -0
  272. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-testing/src/diracx/testing/dummy_osdb.py +0 -0
  273. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-testing/src/diracx/testing/entrypoints.py +0 -0
  274. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-testing/src/diracx/testing/mock_osdb.py +0 -0
  275. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-testing/src/diracx/testing/osdb.py +0 -0
  276. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-testing/src/diracx/testing/routers.py +0 -0
  277. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-testing/src/diracx/testing/scripts/collect_demo_coverage.sh +0 -0
  278. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-testing/src/diracx/testing/time.py +0 -0
  279. {diracx-0.0.1a49 → diracx-0.0.1a50}/diracx-testing/src/diracx/testing/utils.py +0 -0
  280. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/admin/explanations/configuration.md +0 -0
  281. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/admin/explanations/opentelemetry.md +0 -0
  282. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/admin/explanations/sandbox-store.md +0 -0
  283. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/admin/explanations/user-management.md +0 -0
  284. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/admin/how-to/install/convert-cs.md +0 -0
  285. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/admin/how-to/install/register-the-admin-vo.md +0 -0
  286. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/admin/index.md +0 -0
  287. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/admin/reference/security_model.md +0 -0
  288. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/admin/tutorials/authentication.md +0 -0
  289. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/assets/css/extra.css +0 -0
  290. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/assets/css/fontawesome-all.min.css +0 -0
  291. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/assets/css/images/arrow.svg +0 -0
  292. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/assets/css/images/overlay.png +0 -0
  293. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/assets/css/main.css +0 -0
  294. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/assets/home.html +0 -0
  295. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/assets/images/diracx-logo-square.svg +0 -0
  296. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/assets/images/logo.svg +0 -0
  297. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/assets/styles/extra.css +0 -0
  298. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/assets/webfonts/fa-brands-400.woff2 +0 -0
  299. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/assets/webfonts/fa-solid-900.woff +0 -0
  300. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/explanations/components/api.md +0 -0
  301. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/explanations/components/cli.md +0 -0
  302. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/explanations/designing-functionality.md +0 -0
  303. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/explanations/index.md +0 -0
  304. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/explanations/testing.md +0 -0
  305. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/how-to/add-a-cli-command.md +0 -0
  306. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/how-to/add-a-db.md +0 -0
  307. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/how-to/add-a-route.md +0 -0
  308. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/how-to/add-a-setting.md +0 -0
  309. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/how-to/add-a-task.md +0 -0
  310. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/how-to/add-a-test.md +0 -0
  311. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/how-to/add-functionality/index.md +0 -0
  312. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/how-to/client-customization.md +0 -0
  313. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/how-to/client-extension.md +0 -0
  314. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/how-to/develop-legacy-dirac.md +0 -0
  315. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/how-to/extend-diracx/index.md +0 -0
  316. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/how-to/use-the-demo/index.md +0 -0
  317. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/how-to/use-the-demo/swagger.md +0 -0
  318. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/how-to/use-the-demo/web.md +0 -0
  319. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/index.md +0 -0
  320. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/reference/application-state.md +0 -0
  321. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/reference/client-metapathfinder.md +0 -0
  322. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/reference/coding-conventions.md +0 -0
  323. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/reference/configuration.md +0 -0
  324. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/reference/db-transaction-model.md +0 -0
  325. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/reference/dependency-injection.md +0 -0
  326. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/reference/security-policies.md +0 -0
  327. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/reference/security-properties.md +0 -0
  328. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/reference/test-recipes.md +0 -0
  329. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/reference/writing-tests.md +0 -0
  330. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/tutorials/advanced-tutorial.md +0 -0
  331. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/tutorials/develop-web.md +0 -0
  332. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/tutorials/getting-started.md +0 -0
  333. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/tutorials/larger-developments.md +0 -0
  334. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/tutorials/making-changes.md +0 -0
  335. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/tutorials/play-with-auth.md +0 -0
  336. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/tutorials/run-locally.md +0 -0
  337. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/dev/tutorials/write-docs.md +0 -0
  338. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/index.md +0 -0
  339. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/overrides/main.html +0 -0
  340. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/roadmap.md +0 -0
  341. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/user/reference/client-configuration.md +0 -0
  342. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/user/reference/programmatic-usage/command-line-interface.md +0 -0
  343. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/user/reference/programmatic-usage/https-interface.md +0 -0
  344. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/user/reference/programmatic-usage/index.md +0 -0
  345. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/user/reference/programmatic-usage/python-interface.md +0 -0
  346. {diracx-0.0.1a49 → diracx-0.0.1a50}/docs/user/tutorials/getting-started.md +0 -0
  347. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/containers/client/Dockerfile +0 -0
  348. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/.github/workflows/main.yml +0 -0
  349. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/README.md +0 -0
  350. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-api/README.md +0 -0
  351. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-api/pyproject.toml +0 -0
  352. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-api/src/gubbins/api/__init__.py +0 -0
  353. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-api/src/gubbins/api/py.typed +0 -0
  354. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-api/tests/test_gubbins_api.py +0 -0
  355. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-cli/README.md +0 -0
  356. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-cli/pyproject.toml +0 -0
  357. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-cli/src/gubbins/cli/__init__.py +0 -0
  358. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-cli/src/gubbins/cli/config.py +0 -0
  359. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-cli/src/gubbins/cli/lollygag.py +0 -0
  360. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-cli/src/gubbins/cli/py.typed +0 -0
  361. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-cli/tests/test_gubbins_cli.py +0 -0
  362. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/README.md +0 -0
  363. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/pyproject.toml +0 -0
  364. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/__init__.py +0 -0
  365. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/__init__.py +0 -0
  366. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/_client.py +0 -0
  367. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/_configuration.py +0 -0
  368. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/_patch.py +0 -0
  369. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/_serialization.py +0 -0
  370. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/_utils/__init__.py +0 -0
  371. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/_utils/serialization.py +0 -0
  372. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/_utils/utils.py +0 -0
  373. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/_vendor.py +0 -0
  374. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/aio/__init__.py +0 -0
  375. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/aio/_client.py +0 -0
  376. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/aio/_configuration.py +0 -0
  377. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/aio/_patch.py +0 -0
  378. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/aio/_vendor.py +0 -0
  379. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/aio/operations/__init__.py +0 -0
  380. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/aio/operations/_operations.py +0 -0
  381. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/aio/operations/_patch.py +0 -0
  382. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/models/__init__.py +0 -0
  383. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/models/_enums.py +0 -0
  384. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/models/_models.py +0 -0
  385. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/operations/__init__.py +0 -0
  386. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/operations/_operations.py +0 -0
  387. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/operations/_patch.py +0 -0
  388. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/py.typed +0 -0
  389. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/aio.py +0 -0
  390. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/models.py +0 -0
  391. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/py.typed +0 -0
  392. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/src/gubbins/client/sync.py +0 -0
  393. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-client/tests/test_gubbins_client.py +0 -0
  394. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-core/README.md +0 -0
  395. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-core/pyproject.toml +0 -0
  396. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-core/src/gubbins/core/__init__.py +0 -0
  397. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-core/src/gubbins/core/config/__init__.py +0 -0
  398. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-core/src/gubbins/core/config/schema.py +0 -0
  399. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-core/src/gubbins/core/models.py +0 -0
  400. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-core/src/gubbins/core/properties.py +0 -0
  401. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-core/src/gubbins/core/py.typed +0 -0
  402. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-core/tests/test_config.py +0 -0
  403. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-core/tests/test_properties.py +0 -0
  404. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-db/README.md +0 -0
  405. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-db/pyproject.toml +0 -0
  406. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-db/src/gubbins/db/__init__.py +0 -0
  407. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-db/src/gubbins/db/py.typed +0 -0
  408. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-db/src/gubbins/db/sql/__init__.py +0 -0
  409. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-db/src/gubbins/db/sql/jobs/__init__.py +0 -0
  410. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-db/src/gubbins/db/sql/jobs/db.py +0 -0
  411. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-db/src/gubbins/db/sql/jobs/schema.py +0 -0
  412. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-db/src/gubbins/db/sql/lollygag/__init__.py +0 -0
  413. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-db/src/gubbins/db/sql/lollygag/db.py +0 -0
  414. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-db/src/gubbins/db/sql/lollygag/schema.py +0 -0
  415. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-db/tests/test_gubbins_job_db.py +0 -0
  416. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-db/tests/test_lollygag_db.py +0 -0
  417. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-logic/README.md +0 -0
  418. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-logic/pyproject.toml +0 -0
  419. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-logic/src/gubbins/logic/__init__.py +0 -0
  420. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-logic/src/gubbins/logic/auth/__init__.py +0 -0
  421. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-logic/src/gubbins/logic/auth/well_known.py +0 -0
  422. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-logic/src/gubbins/logic/lollygag/__init__.py +0 -0
  423. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-logic/src/gubbins/logic/lollygag/lollygag.py +0 -0
  424. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-logic/src/gubbins/logic/py.typed +0 -0
  425. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-logic/tests/test_gubbins_logic.py +0 -0
  426. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-routers/README.md +0 -0
  427. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-routers/pyproject.toml +0 -0
  428. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-routers/src/gubbins/routers/__init__.py +0 -0
  429. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-routers/src/gubbins/routers/dependencies.py +0 -0
  430. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-routers/src/gubbins/routers/lollygag/__init__.py +0 -0
  431. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-routers/src/gubbins/routers/lollygag/access_policy.py +0 -0
  432. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-routers/src/gubbins/routers/lollygag/lollygag.py +0 -0
  433. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-routers/src/gubbins/routers/py.typed +0 -0
  434. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-routers/src/gubbins/routers/well_known.py +0 -0
  435. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-routers/tests/data/idp-server.invalid/.well-known/openid-configuration +0 -0
  436. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-routers/tests/test_gubbins_job_manager.py +0 -0
  437. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-routers/tests/test_lollybag.py +0 -0
  438. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-routers/tests/test_wellknown.py +0 -0
  439. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-testing/README.md +0 -0
  440. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-testing/pyproject.toml +0 -0
  441. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/gubbins-testing/src/gubbins/testing/__init__.py +0 -0
  442. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/pixi.toml +0 -0
  443. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/pyproject.toml +0 -0
  444. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins/release.notes +0 -0
  445. {diracx-0.0.1a49 → diracx-0.0.1a50}/extensions/gubbins_values.yaml +0 -0
  446. {diracx-0.0.1a49 → diracx-0.0.1a50}/pyproject.toml +0 -0
  447. {diracx-0.0.1a49 → diracx-0.0.1a50}/release.notes +0 -0
  448. {diracx-0.0.1a49 → diracx-0.0.1a50}/release.notes.old +0 -0
  449. {diracx-0.0.1a49 → diracx-0.0.1a50}/run_local.sh +0 -0
  450. {diracx-0.0.1a49 → diracx-0.0.1a50}/tests/__init__.py +0 -0
  451. {diracx-0.0.1a49 → diracx-0.0.1a50}/tests/test_generic.py +0 -0
@@ -0,0 +1,61 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import subprocess
5
+ from collections import defaultdict
6
+
7
+
8
+ def get_task_group(task_name):
9
+ if task_name.startswith("pytest-diracx"):
10
+ return "DiracX"
11
+ if task_name.startswith("pytest-gubbins"):
12
+ return "Gubbins"
13
+ if task_name.startswith("mkdocs"):
14
+ return "Documentation"
15
+ if task_name == "pre-commit":
16
+ return "Pre-commit"
17
+ if task_name == "generate-client":
18
+ return "Client Generation"
19
+ if task_name == "shellcheck":
20
+ return "Shellcheck"
21
+ return "Default"
22
+
23
+
24
+ def main():
25
+ """Generate the pixi-tasks.md file."""
26
+ result = subprocess.run(
27
+ ["pixi", "task", "list", "--json"], # noqa: S607
28
+ capture_output=True,
29
+ text=True,
30
+ )
31
+ environments = json.loads(result.stdout)
32
+
33
+ tasks_by_group = defaultdict(list)
34
+ for env in environments:
35
+ for feature in env["features"]:
36
+ for task in feature["tasks"]:
37
+ group = get_task_group(task["name"])
38
+ tasks_by_group[group].append(task)
39
+
40
+ with open("docs/dev/reference/pixi-tasks.md", "w") as f:
41
+ f.write("# Pixi Tasks\n\n")
42
+ f.write("*This page is auto-generated. Do not edit directly.*\n\n")
43
+ f.write("DiracX uses [Pixi](https://pixi.dev/) as package manager\n")
44
+ f.write("This page documents the available pixi tasks.\n\n")
45
+
46
+ for group in sorted(tasks_by_group.keys()):
47
+ f.write(f"## {group} Tasks\n\n")
48
+ # Remove duplicates
49
+ tasks = list({v["name"]: v for v in tasks_by_group[group]}.values())
50
+ for task in sorted(tasks, key=lambda x: x["name"]):
51
+ description = (
52
+ task.get("description") or task.get("cmd") or "No description"
53
+ )
54
+ f.write(f"- `{task['name']}`: {description}\n")
55
+ f.write("\n")
56
+
57
+ f.write("Generated by .github/workflows/generate_pixi_tasks_doc.py\n")
58
+
59
+
60
+ if __name__ == "__main__":
61
+ main()
@@ -13,7 +13,6 @@ concurrency:
13
13
  cancel-in-progress: true
14
14
 
15
15
  jobs:
16
-
17
16
  shellcheck:
18
17
  runs-on: ubuntu-latest
19
18
  steps:
@@ -7,6 +7,9 @@ default_install_hook_types:
7
7
  default_language_version:
8
8
  python: python3
9
9
 
10
+ ci:
11
+ skip: [generate-pixi-docs]
12
+
10
13
  default_stages: [pre-commit]
11
14
 
12
15
  repos:
@@ -27,14 +30,14 @@ repos:
27
30
  - id: check-added-large-files
28
31
 
29
32
  - repo: https://github.com/astral-sh/ruff-pre-commit
30
- rev: 'v0.12.11'
33
+ rev: 'v0.13.0'
31
34
  hooks:
32
35
  - id: ruff
33
36
  args: ["--fix", "--show-fixes"]
34
37
  - id: ruff-format
35
38
 
36
39
  - repo: https://github.com/pre-commit/mirrors-mypy
37
- rev: v1.17.1
40
+ rev: v1.18.1
38
41
  hooks:
39
42
  - id: mypy
40
43
  additional_dependencies:
@@ -71,3 +74,12 @@ repos:
71
74
  entry: '(?i)lollygag'
72
75
  language: pygrep
73
76
  files: ^diracx
77
+
78
+ - repo: local
79
+ hooks:
80
+ - id: generate-pixi-docs
81
+ name: Generate pixi tasks documentation
82
+ entry: pixi run -e default python .github/workflows/generate_pixi_tasks_doc.py
83
+ language: system
84
+ pass_filenames: false
85
+ files: ^pixi\.toml$|^pixi\.lock$ # only run if pixi files change
@@ -8,9 +8,17 @@
8
8
  # This is just a quick example to get started.
9
9
  # https://docs.readthedocs.io/page/guides/reproducible-builds.html
10
10
  jobs:
11
- pre_install:
12
- - pip install mkdocs-material
13
- - pip install mkdocs-diracx-plugin@git+https://github.com/DIRACGrid/mkdocs-diracx-plugin
11
+ create_environment:
12
+ - asdf plugin add pixi
13
+ - asdf install pixi latest
14
+ - asdf global pixi latest
15
+
16
+ install:
17
+ - pixi install -e mkdocs
18
+
19
+ build:
20
+ html:
21
+ - pixi run -e mkdocs mkdocs-build --site-dir $READTHEDOCS_OUTPUT/html
14
22
 
15
23
  mkdocs:
16
24
  configuration: mkdocs.yml
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: diracx
3
- Version: 0.0.1a49
3
+ Version: 0.0.1a50
4
4
  Summary: Client installation for users of DiracX installations
5
5
  License: GPL-3.0-only
6
6
  License-File: LICENSE
@@ -23,7 +23,7 @@ dependencies = [
23
23
  "pydantic-settings",
24
24
  "pyyaml",
25
25
  "sh",
26
- "diraccommon >=9.0.0a67",
26
+ "diraccommon >=9.0.0",
27
27
  ]
28
28
  dynamic = ["version"]
29
29
 
@@ -147,20 +147,29 @@ class JobDescriptionConfig(BaseModel):
147
147
  AllowedJobTypes: list[str] = ["User", "Test", "Hospital"]
148
148
 
149
149
 
150
- class InputDataPolicyConfig(BaseModel):
151
- InputDataModule: str = "LocalInputData"
150
+ class InputDataPolicyProtocolsConfig(BaseModel):
151
+ Remote: list[str] = []
152
+ Local: list[str] = []
152
153
 
153
154
 
154
- class ExternalsPolicyConfig(BaseModel):
155
- SoftwareDistModule: str = "LocalSoftwareDist"
155
+ class InputDataPolicyConfig(BaseModel):
156
+ # TODO: Remove this once the model is extended to support everything
157
+ model_config = ConfigDict(extra="ignore", frozen=True)
158
+
159
+ Default: str = "Default = DIRAC.WorkloadManagementSystem.Client.InputDataByProtocol"
160
+ Download: str = "DIRAC.WorkloadManagementSystem.Client.DownloadInputData"
161
+ Protocol: str = "DIRAC.WorkloadManagementSystem.Client.InputDataByProtocol"
162
+ AllReplicas: bool = True
163
+ Protocols: InputDataPolicyProtocolsConfig = InputDataPolicyProtocolsConfig()
164
+ InputDataModule: str = "DIRAC.Core.Utilities.InputDataResolution"
156
165
 
157
166
 
158
167
  class OperationsConfig(BaseModel):
159
168
  EnableSecurityLogging: bool = False
160
- ExternalsPolicy: ExternalsPolicyConfig = ExternalsPolicyConfig()
161
169
  InputDataPolicy: InputDataPolicyConfig = InputDataPolicyConfig()
162
170
  JobDescription: JobDescriptionConfig = JobDescriptionConfig()
163
171
  Services: ServicesConfig = ServicesConfig()
172
+ SoftwareDistModule: str = "LocalSoftwareDist"
164
173
 
165
174
  Cloud: MutableMapping[str, Any] | None = None
166
175
  DataConsistency: MutableMapping[str, Any] | None = None
@@ -10,7 +10,12 @@ from sqlalchemy import (
10
10
  )
11
11
  from sqlalchemy.orm import declarative_base
12
12
 
13
- from diracx.db.sql.utils import Column, DateNowColumn, EnumColumn, NullColumn
13
+ from diracx.db.sql.utils import (
14
+ Column,
15
+ DateNowColumn,
16
+ EnumColumn,
17
+ NullColumn,
18
+ )
14
19
 
15
20
  USER_CODE_LENGTH = 8
16
21
 
@@ -92,7 +97,6 @@ class RefreshTokens(Base):
92
97
  status = EnumColumn(
93
98
  "Status", RefreshTokenStatus, server_default=RefreshTokenStatus.CREATED.name
94
99
  )
95
- creation_time = DateNowColumn("CreationTime", index=True)
96
100
  scope = Column("Scope", String(1024))
97
101
 
98
102
  # User attributes bound to the refresh token
@@ -1,16 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from .base import (
4
- BaseSQLDB,
5
- SQLDBUnavailableError,
6
- _get_columns,
7
- apply_search_filters,
8
- apply_sort_constraints,
9
- )
10
- from .functions import hash, substract_date, utcnow
11
- from .types import Column, DateNowColumn, EnumBackedBool, EnumColumn, NullColumn
12
-
13
- __all__ = (
3
+ __all__ = [
14
4
  "_get_columns",
15
5
  "utcnow",
16
6
  "Column",
@@ -24,4 +14,18 @@ __all__ = (
24
14
  "substract_date",
25
15
  "hash",
26
16
  "SQLDBUnavailableError",
17
+ "uuid7_from_datetime",
18
+ "uuid7_to_datetime",
19
+ ]
20
+
21
+ from .base import (
22
+ BaseSQLDB,
23
+ SQLDBUnavailableError,
24
+ _get_columns,
25
+ apply_search_filters,
26
+ apply_sort_constraints,
27
+ uuid7_from_datetime,
28
+ uuid7_to_datetime,
27
29
  )
30
+ from .functions import hash, substract_date, utcnow
31
+ from .types import Column, DateNowColumn, EnumBackedBool, EnumColumn, NullColumn
@@ -7,13 +7,15 @@ import re
7
7
  from abc import ABCMeta
8
8
  from collections.abc import AsyncIterator
9
9
  from contextvars import ContextVar
10
- from datetime import datetime
10
+ from datetime import datetime, timezone
11
11
  from typing import Any, Self, cast
12
+ from uuid import UUID as StdUUID # noqa: N811
12
13
 
13
14
  from pydantic import TypeAdapter
14
15
  from sqlalchemy import DateTime, MetaData, func, select
15
16
  from sqlalchemy.exc import OperationalError
16
17
  from sqlalchemy.ext.asyncio import AsyncConnection, AsyncEngine, create_async_engine
18
+ from uuid_utils import UUID, uuid7
17
19
 
18
20
  from diracx.core.exceptions import InvalidQueryError
19
21
  from diracx.core.extensions import select_from_extension
@@ -416,3 +418,33 @@ def apply_sort_constraints(column_mapping, stmt, sorts):
416
418
  if sort_columns:
417
419
  stmt = stmt.order_by(*sort_columns)
418
420
  return stmt
421
+
422
+
423
+ def uuid7_to_datetime(uuid: UUID | StdUUID | str) -> datetime:
424
+ """Convert a UUIDv7 to a datetime."""
425
+ if isinstance(uuid, StdUUID):
426
+ # Convert stdlib UUID to uuid_utils.UUID
427
+ uuid = UUID(str(uuid))
428
+ elif not isinstance(uuid, UUID):
429
+ # Convert string or other types to uuid_utils.UUID
430
+ uuid = UUID(uuid)
431
+ if uuid.version != 7:
432
+ raise ValueError(f"UUID {uuid} is not a UUIDv7")
433
+ return datetime.fromtimestamp(uuid.timestamp / 1000.0, tz=timezone.utc)
434
+
435
+
436
+ def uuid7_from_datetime(dt: datetime, *, randomize: bool = True) -> UUID:
437
+ """Generate a UUIDv7 corresponding to the given datetime.
438
+
439
+ If randomize is True, the standard uuid7 function is used resulting in the
440
+ lowest 62-bits being random. If randomize is False, the UUIDv7 will be the
441
+ lowest possible UUIDv7 for the given datetime.
442
+ """
443
+ timestamp = dt.timestamp()
444
+ if randomize:
445
+ uuid = uuid7(int(timestamp), int((timestamp % 1) * 1e9))
446
+ else:
447
+ time_high = int(timestamp * 1000) >> 16
448
+ time_low = int(timestamp * 1000) & 0xFFFF
449
+ uuid = UUID.from_fields((time_high, time_low, 0x7000, 0x80, 0, 0))
450
+ return uuid
@@ -56,7 +56,6 @@ async def test_get(auth_db: AuthDB):
56
56
  refresh_token_details["sub"],
57
57
  refresh_token_details["scope"],
58
58
  )
59
- creation_time = (await auth_db.get_refresh_token(jti))["CreationTime"]
60
59
 
61
60
  # Enrich the dict with the generated refresh token attributes
62
61
  expected_refresh_token = {
@@ -64,7 +63,6 @@ async def test_get(auth_db: AuthDB):
64
63
  "Scope": refresh_token_details["scope"],
65
64
  "JTI": jti,
66
65
  "Status": RefreshTokenStatus.CREATED,
67
- "CreationTime": creation_time,
68
66
  }
69
67
 
70
68
  # Get refresh token details
@@ -0,0 +1,221 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime, timedelta, timezone
4
+ from uuid import UUID as StdUUID # noqa: N811
5
+
6
+ import freezegun
7
+ import pytest
8
+ from uuid_utils import UUID, uuid7
9
+
10
+ from diracx.db.sql.utils import uuid7_from_datetime, uuid7_to_datetime
11
+
12
+
13
+ def frozen_uuid7() -> UUID:
14
+ """Create a UUID7 in a way which respects the freezegun context."""
15
+ timestamp = datetime.now(tz=timezone.utc).timestamp()
16
+ return uuid7(int(timestamp), int((timestamp % 1) * 1e9))
17
+
18
+
19
+ class TestDatetimeToUuid7:
20
+ """Test cases for the datetime_to_uuid7 function."""
21
+
22
+ def test_datetime_to_uuid7_random(self):
23
+ """Test that the datetime_to_uuid7 function returns a UUID7 with the current timestamp."""
24
+ dt = datetime.fromisoformat("2024-01-15T12:30:45.123456+00:00")
25
+ result = uuid7_from_datetime(dt, randomize=True)
26
+ assert str(result)[:15] == "018d0d1a-5183-7"
27
+ assert len(set(str(result)[15:])) > 4
28
+
29
+ def test_datetime_to_uuid7_deterministic(self):
30
+ """Test that the datetime_to_uuid7 function returns a UUID7 with the current timestamp."""
31
+ dt = datetime.fromisoformat("2024-01-15T12:30:45.123456+00:00")
32
+ result = uuid7_from_datetime(dt, randomize=False)
33
+ assert str(result) == "018d0d1a-5183-7000-8000-000000000000"
34
+
35
+
36
+ class TestUuid7ToDatetime:
37
+ """Test cases for the uuid7_to_datetime function."""
38
+
39
+ def test_uuid7_now(self):
40
+ """Test that the uuid7 function returns a UUID7 with the current timestamp."""
41
+ test_uuid = frozen_uuid7()
42
+ result = uuid7_to_datetime(test_uuid)
43
+ assert result.tzinfo == timezone.utc
44
+ assert result - datetime.now(tz=timezone.utc) < timedelta(milliseconds=2)
45
+
46
+ @freezegun.freeze_time("2024-01-15 12:30:45.123456")
47
+ def test_uuid7_to_datetime_with_uuid_utils_uuid(self):
48
+ """Test conversion with uuid_utils.UUID object."""
49
+ test_uuid = frozen_uuid7()
50
+ result = uuid7_to_datetime(test_uuid)
51
+
52
+ assert isinstance(result, datetime)
53
+ assert result.tzinfo == timezone.utc
54
+ assert result.isoformat() == "2024-01-15T12:30:45.123000+00:00"
55
+
56
+ @freezegun.freeze_time("2024-01-15 12:30:45.123456")
57
+ def test_uuid7_to_datetime_with_stdlib_uuid(self):
58
+ """Test conversion with standard library UUID object."""
59
+ test_uuid = frozen_uuid7()
60
+ stdlib_uuid = StdUUID(str(test_uuid))
61
+
62
+ result = uuid7_to_datetime(stdlib_uuid)
63
+ assert isinstance(result, datetime)
64
+ assert result.tzinfo == timezone.utc
65
+
66
+ @freezegun.freeze_time("2024-01-15 12:30:45.123456")
67
+ def test_uuid7_to_datetime_with_string_uuid(self):
68
+ """Test conversion with string UUID that gets converted to uuid_utils.UUID."""
69
+ test_uuid = frozen_uuid7()
70
+ uuid_string = str(test_uuid)
71
+
72
+ result = uuid7_to_datetime(uuid_string)
73
+ assert isinstance(result, datetime)
74
+ assert result.tzinfo == timezone.utc
75
+
76
+ @freezegun.freeze_time("2024-01-15 12:30:45.123456")
77
+ def test_uuid7_to_datetime_precision(self):
78
+ """Test that the conversion maintains reasonable precision."""
79
+ test_uuid = frozen_uuid7()
80
+ result = uuid7_to_datetime(test_uuid)
81
+
82
+ assert isinstance(result, datetime)
83
+ assert result.tzinfo == timezone.utc
84
+ assert result.isoformat() == "2024-01-15T12:30:45.123000+00:00"
85
+
86
+ @freezegun.freeze_time("1970-01-01 00:00:00.000000")
87
+ def test_uuid7_to_datetime_epoch_boundary(self):
88
+ """Test conversion with UUID7 at epoch boundary."""
89
+ test_uuid = frozen_uuid7()
90
+ result = uuid7_to_datetime(test_uuid)
91
+
92
+ assert isinstance(result, datetime)
93
+ assert result.tzinfo == timezone.utc
94
+ assert result.isoformat() == "1970-01-01T00:00:00+00:00"
95
+
96
+ @freezegun.freeze_time("2100-01-01 00:00:00.000000")
97
+ def test_uuid7_to_datetime_future_timestamp(self):
98
+ """Test conversion with future timestamp."""
99
+ test_uuid = frozen_uuid7()
100
+ result = uuid7_to_datetime(test_uuid)
101
+
102
+ assert isinstance(result, datetime)
103
+ assert result.tzinfo == timezone.utc
104
+ assert result.isoformat() == "2100-01-01T00:00:00+00:00"
105
+
106
+ @freezegun.freeze_time("2024-01-15 12:30:45.123456")
107
+ def test_uuid7_to_datetime_consistency(self):
108
+ """Test that multiple calls with the same UUID return the same result."""
109
+ test_uuid = frozen_uuid7()
110
+
111
+ result1 = uuid7_to_datetime(test_uuid)
112
+ result2 = uuid7_to_datetime(test_uuid)
113
+
114
+ assert result1.isoformat() == result2.isoformat()
115
+ assert result1 == result2
116
+
117
+ @freezegun.freeze_time("2024-01-15 12:30:45.123456")
118
+ def test_uuid7_to_datetime_different_input_types_same_result(self):
119
+ """Test that different input types representing the same UUID return the same result."""
120
+ test_uuid = frozen_uuid7()
121
+ uuid_string = str(test_uuid)
122
+ stdlib_uuid = StdUUID(uuid_string)
123
+
124
+ result_from_uuid_utils = uuid7_to_datetime(test_uuid)
125
+ result_from_string = uuid7_to_datetime(uuid_string)
126
+ result_from_stdlib = uuid7_to_datetime(stdlib_uuid)
127
+
128
+ # All results should be identical
129
+ assert result_from_uuid_utils == result_from_string == result_from_stdlib
130
+
131
+ def test_uuid7_to_datetime_invalid_input_type(self):
132
+ """Test that invalid input types raise appropriate errors."""
133
+ with pytest.raises(TypeError):
134
+ uuid7_to_datetime(123) # type: ignore
135
+
136
+ with pytest.raises(TypeError):
137
+ uuid7_to_datetime(123.45) # type: ignore
138
+
139
+ with pytest.raises(TypeError):
140
+ uuid7_to_datetime([]) # type: ignore
141
+
142
+ with pytest.raises(TypeError):
143
+ uuid7_to_datetime({}) # type: ignore
144
+
145
+ def test_uuid7_to_datetime_invalid_uuid_string(self):
146
+ """Test that invalid UUID strings raise appropriate errors."""
147
+ with pytest.raises(ValueError):
148
+ uuid7_to_datetime("not-a-uuid")
149
+
150
+ with pytest.raises(ValueError):
151
+ uuid7_to_datetime("12345")
152
+
153
+ with pytest.raises(ValueError):
154
+ uuid7_to_datetime("")
155
+
156
+ def test_uuid7_to_datetime_non_uuid7_uuid(self):
157
+ """Test behavior with non-UUID7 UUIDs."""
158
+ uuid4 = StdUUID("550e8400-e29b-41d4-a716-446655440000")
159
+
160
+ with pytest.raises(ValueError, match="is not a UUIDv7"):
161
+ uuid7_to_datetime(uuid4)
162
+
163
+ @freezegun.freeze_time("2024-01-15 12:30:45.123456")
164
+ def test_uuid7_to_datetime_timezone_awareness(self):
165
+ """Test that the returned datetime is always timezone-aware and in UTC."""
166
+ test_uuid = frozen_uuid7()
167
+ result = uuid7_to_datetime(test_uuid)
168
+
169
+ assert result.tzinfo is not None
170
+
171
+ assert result.tzinfo == timezone.utc
172
+
173
+ assert result.tzinfo.utcoffset(result) is not None
174
+
175
+ @freezegun.freeze_time("2024-01-15 12:30:45.123456")
176
+ def test_uuid7_to_datetime_millisecond_precision(self):
177
+ """Test that the conversion handles millisecond precision correctly."""
178
+ uuids = [frozen_uuid7() for _ in range(5)]
179
+ results = [uuid7_to_datetime(uuid) for uuid in uuids]
180
+
181
+ for result in results:
182
+ assert isinstance(result, datetime)
183
+ assert result.tzinfo == timezone.utc
184
+
185
+ timestamps = [r.timestamp() for r in results]
186
+ assert timestamps == sorted(timestamps)
187
+
188
+ @freezegun.freeze_time("2024-01-15 12:30:45.123456")
189
+ def test_uuid7_to_datetime_timestamp_property(self):
190
+ """Test that the function correctly uses the timestamp property."""
191
+ test_uuid = frozen_uuid7()
192
+ result = uuid7_to_datetime(test_uuid)
193
+
194
+ expected_timestamp = test_uuid.timestamp / 1000.0
195
+ actual_timestamp = result.timestamp()
196
+
197
+ assert abs(actual_timestamp - expected_timestamp) < 0.001
198
+
199
+ @freezegun.freeze_time("2024-01-15 12:30:45.123456")
200
+ def test_uuid7_to_datetime_deterministic_with_freeze(self):
201
+ """Test that the function works deterministically with freezegun."""
202
+ with freezegun.freeze_time("2024-01-15 12:30:45.123000"):
203
+ uuid1 = frozen_uuid7()
204
+ result1 = uuid7_to_datetime(uuid1)
205
+
206
+ with freezegun.freeze_time("2024-01-15 12:30:45.123000"):
207
+ uuid2 = frozen_uuid7()
208
+ result2 = uuid7_to_datetime(uuid2)
209
+
210
+ time_diff = abs((result1 - result2).total_seconds())
211
+ assert time_diff < 1.0
212
+
213
+ @freezegun.freeze_time("1970-01-01 00:00:00")
214
+ def test_uuid7_to_datetime_edge_case_zero_timestamp(self):
215
+ """Test edge case with very small timestamp values."""
216
+ test_uuid = frozen_uuid7()
217
+ result = uuid7_to_datetime(test_uuid)
218
+
219
+ assert isinstance(result, datetime)
220
+ assert result.tzinfo == timezone.utc
221
+ assert result.isoformat() == "1970-01-01T00:00:00+00:00"
@@ -14,7 +14,7 @@ classifiers = [
14
14
  ]
15
15
  dependencies = [
16
16
  "cachetools",
17
- "diraccommon >=9.0.0a67",
17
+ "diraccommon >=9.0.0",
18
18
  "diracx-core",
19
19
  "diracx-db",
20
20
  "joserfc",
@@ -28,6 +28,7 @@ from diracx.core.properties import SecurityProperty
28
28
  from diracx.core.settings import AuthSettings
29
29
  from diracx.db.sql import AuthDB
30
30
  from diracx.db.sql.auth.schema import FlowStatus, RefreshTokenStatus
31
+ from diracx.db.sql.utils import uuid7_to_datetime
31
32
  from diracx.db.sql.utils.functions import substract_date
32
33
 
33
34
  from .utils import (
@@ -315,12 +316,11 @@ async def exchange_token(
315
316
  # Merge the VO with the subject to get a unique DIRAC sub
316
317
  sub = f"{vo}:{sub}"
317
318
 
318
- creation_time = datetime.now(timezone.utc)
319
319
  refresh_payload: RefreshTokenPayload | None = None
320
320
  if include_refresh_token:
321
321
  # Insert the refresh token with user details into the RefreshTokens table
322
322
  # User details are needed to regenerate access tokens later
323
- jti, creation_time = await insert_refresh_token(
323
+ refresh_jti = await insert_refresh_token(
324
324
  auth_db=auth_db,
325
325
  subject=sub,
326
326
  scope=scope,
@@ -329,9 +329,12 @@ async def exchange_token(
329
329
  # Generate refresh token payload
330
330
  if refresh_token_expire_minutes is None:
331
331
  refresh_token_expire_minutes = settings.refresh_token_expire_minutes
332
+ refresh_exp = uuid7_to_datetime(refresh_jti) + timedelta(
333
+ minutes=refresh_token_expire_minutes
334
+ )
332
335
  refresh_payload = {
333
- "jti": str(jti),
334
- "exp": creation_time + timedelta(minutes=refresh_token_expire_minutes),
336
+ "jti": str(refresh_jti),
337
+ "exp": refresh_exp,
335
338
  # legacy_exchange is used to indicate that the original refresh token
336
339
  # was obtained from the legacy_exchange endpoint
337
340
  "legacy_exchange": legacy_exchange,
@@ -341,15 +344,19 @@ async def exchange_token(
341
344
  # Generate access token payload
342
345
  # For now, the access token is only used to access DIRAC services,
343
346
  # therefore, the audience is not set and checked
347
+ access_jti = uuid7()
348
+ access_exp = uuid7_to_datetime(access_jti) + timedelta(
349
+ minutes=settings.access_token_expire_minutes
350
+ )
344
351
  access_payload: AccessTokenPayload = {
345
352
  "sub": sub,
346
353
  "vo": vo,
347
354
  "iss": settings.token_issuer,
348
355
  "dirac_properties": list(properties),
349
- "jti": str(uuid7()),
356
+ "jti": str(access_jti),
350
357
  "preferred_username": preferred_username,
351
358
  "dirac_group": dirac_group,
352
- "exp": creation_time + timedelta(minutes=settings.access_token_expire_minutes),
359
+ "exp": access_exp,
353
360
  "dirac_policies": {},
354
361
  }
355
362
 
@@ -382,8 +389,8 @@ async def insert_refresh_token(
382
389
  auth_db: AuthDB,
383
390
  subject: str,
384
391
  scope: str,
385
- ) -> tuple[UUID, datetime]:
386
- """Insert a refresh token into the database and return the JWT ID and creation time."""
392
+ ) -> UUID:
393
+ """Insert a refresh token into the database and return the JWT ID."""
387
394
  # Generate a JWT ID
388
395
  jti = uuid7()
389
396
 
@@ -393,10 +400,7 @@ async def insert_refresh_token(
393
400
  subject=subject,
394
401
  scope=scope,
395
402
  )
396
-
397
- # Get the creation time of the refresh token
398
- refresh_token = await auth_db.get_refresh_token(jti)
399
- return jti, refresh_token["CreationTime"]
403
+ return jti
400
404
 
401
405
 
402
406
  async def get_device_flow(auth_db: AuthDB, device_code: str, max_validity: int):
@@ -39,7 +39,7 @@ def make_check_and_prepare_job_config(config: Config, vo: str):
39
39
  ops = config.Operations[vo]
40
40
  return {
41
41
  "inputDataPolicyForVO": ops.InputDataPolicy.InputDataModule,
42
- "softwareDistModuleForVO": ops.ExternalsPolicy.SoftwareDistModule,
42
+ "softwareDistModuleForVO": ops.SoftwareDistModule,
43
43
  "defaultCPUTimeForOwnerGroup": ops.JobDescription.DefaultCPUTime,
44
44
  "getDIRACPlatform": partial(find_compatible_platforms, config=config),
45
45
  }
@@ -1,35 +1,6 @@
1
1
  """Authorization code flow.
2
2
 
3
- Client Application DIRAC Auth Service IAM (Identity Access Management)
4
- ------------------ ------------------ --------------------------------
5
- | | |
6
- |---(A)--- Authorization Request ---->| |
7
- | |---(B)--- Authorization Request -------->|
8
- | | |
9
- | |<--(C)--- Authorization Grant -----------|
10
- | | |
11
- | |---(D)--- ID Token Request ------------->|
12
- | | |
13
- | |<--(E)--- ID Token ----------------------|
14
- |<--(F)--- Authorization Grant -------| |
15
- | | |
16
- |---(G)--- Access Token Request ----->| |
17
- | | |
18
- |<--(H)--- Access Token --------------| |
19
- | | |
20
-
21
-
22
- * (A) The flow is initiated by the client making a request to the DIRAC auth service (GET /authorize) with PKCE.
23
- * (B) The DIRAC auth service stores the request details and redirects the user to the IAM's authorization flow
24
- by performing an authorization request on behalf of the user.
25
- * (C) Once done, the IAM redirects the user back to the DIRAC auth service (GET /authorize/complete).
26
- * (D) The DIRAC auth service exchanges the code for an ID token by making a token request to the IAM.
27
- * (E) The IAM responds with an access, a refresh and an ID tokens.
28
- * (F) The DIRAC auth service only stores the ID token and redirects the user to the client's redirect URI.
29
- * (G) The client requests an access token from the DIRAC auth service by making a request to
30
- the token endpoint (POST /token).
31
- * (H) The DIRAC auth service responds with a DIRAC access token, based on the ID token.
32
- * The client can then use the access token to access the DIRAC services.
3
+ See docs/admin/explanations/authentication.md
33
4
  """
34
5
 
35
6
  from __future__ import annotations