howler-api 4.0.0.dev850__tar.gz → 4.0.0.dev871__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. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/PKG-INFO +1 -1
  2. howler_api-4.0.0.dev871/howler/api/v1/utils/etag.py +106 -0
  3. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v2/ingest.py +4 -2
  4. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v2/search.py +8 -11
  5. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/datastore/collection.py +1 -1
  6. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/search_service.py +4 -0
  7. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/pyproject.toml +1 -1
  8. howler_api-4.0.0.dev850/howler/api/v1/utils/etag.py +0 -84
  9. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/README.md +0 -0
  10. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/__init__.py +0 -0
  11. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/actions/__init__.py +0 -0
  12. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/actions/add_label.py +0 -0
  13. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/actions/add_to_bundle.py +0 -0
  14. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/actions/add_to_case.py +0 -0
  15. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/actions/change_field.py +0 -0
  16. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/actions/demote.py +0 -0
  17. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/actions/example_plugin.py +0 -0
  18. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/actions/prioritization.py +0 -0
  19. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/actions/promote.py +0 -0
  20. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/actions/remove_from_bundle.py +0 -0
  21. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/actions/remove_label.py +0 -0
  22. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/actions/transition.py +0 -0
  23. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/__init__.py +0 -0
  24. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/base.py +0 -0
  25. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/socket.py +0 -0
  26. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v1/__init__.py +0 -0
  27. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v1/action.py +0 -0
  28. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v1/analytic.py +0 -0
  29. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v1/auth.py +0 -0
  30. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v1/clue.py +0 -0
  31. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v1/configs.py +0 -0
  32. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v1/dossier.py +0 -0
  33. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v1/help.py +0 -0
  34. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v1/hit.py +0 -0
  35. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v1/notebook.py +0 -0
  36. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v1/overview.py +0 -0
  37. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v1/search.py +0 -0
  38. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v1/template.py +0 -0
  39. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v1/tool.py +0 -0
  40. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v1/user.py +0 -0
  41. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v1/utils/__init__.py +0 -0
  42. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v1/view.py +0 -0
  43. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v2/__init__.py +0 -0
  44. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/api/v2/case.py +0 -0
  45. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/app.py +0 -0
  46. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/common/README.md +0 -0
  47. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/common/__init__.py +0 -0
  48. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/common/classification.py +0 -0
  49. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/common/classification.yml +0 -0
  50. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/common/exceptions.py +0 -0
  51. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/common/loader.py +0 -0
  52. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/common/logging/__init__.py +0 -0
  53. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/common/logging/audit.py +0 -0
  54. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/common/logging/format.py +0 -0
  55. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/common/net.py +0 -0
  56. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/common/net_static.py +0 -0
  57. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/common/random_user.py +0 -0
  58. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/common/swagger.py +0 -0
  59. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/config.py +0 -0
  60. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/cronjobs/__init__.py +0 -0
  61. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/cronjobs/correlation.py +0 -0
  62. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/cronjobs/retention.py +0 -0
  63. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/cronjobs/view_cleanup.py +0 -0
  64. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/datastore/README.md +0 -0
  65. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/datastore/__init__.py +0 -0
  66. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/datastore/bulk.py +0 -0
  67. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/datastore/constants.py +0 -0
  68. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/datastore/exceptions.py +0 -0
  69. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/datastore/howler_store.py +0 -0
  70. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/datastore/migrations/fix_process.py +0 -0
  71. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/datastore/operations.py +0 -0
  72. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/datastore/schemas.py +0 -0
  73. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/datastore/store.py +0 -0
  74. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/datastore/support/__init__.py +0 -0
  75. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/datastore/support/build.py +0 -0
  76. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/datastore/support/schemas.py +0 -0
  77. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/datastore/types.py +0 -0
  78. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/error.py +0 -0
  79. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/external/README.md +0 -0
  80. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/external/__init__.py +0 -0
  81. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/external/generate_mitre.py +0 -0
  82. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/external/generate_sigma_rules.py +0 -0
  83. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/external/generate_tlds.py +0 -0
  84. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/external/reindex_data.py +0 -0
  85. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/external/wipe_databases.py +0 -0
  86. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/gunicorn_config.py +0 -0
  87. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/healthz.py +0 -0
  88. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/helper/__init__.py +0 -0
  89. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/helper/azure.py +0 -0
  90. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/helper/discover.py +0 -0
  91. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/helper/hit.py +0 -0
  92. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/helper/oauth.py +0 -0
  93. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/helper/search.py +0 -0
  94. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/helper/workflow.py +0 -0
  95. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/helper/ws.py +0 -0
  96. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/README.md +0 -0
  97. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/__init__.py +0 -0
  98. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/base.py +0 -0
  99. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/charter.txt +0 -0
  100. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/constants.py +0 -0
  101. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/helper.py +0 -0
  102. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/howler_enum.py +0 -0
  103. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/mixins.py +0 -0
  104. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/__init__.py +0 -0
  105. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/action.py +0 -0
  106. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/analytic.py +0 -0
  107. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/assemblyline.py +0 -0
  108. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/aws.py +0 -0
  109. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/azure.py +0 -0
  110. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/case.py +0 -0
  111. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/cbs.py +0 -0
  112. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/clue.py +0 -0
  113. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/config.py +0 -0
  114. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/dossier.py +0 -0
  115. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/__init__.py +0 -0
  116. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/agent.py +0 -0
  117. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/autonomous_system.py +0 -0
  118. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/client.py +0 -0
  119. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/cloud.py +0 -0
  120. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/code_signature.py +0 -0
  121. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/container.py +0 -0
  122. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/dns.py +0 -0
  123. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/egress.py +0 -0
  124. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/elf.py +0 -0
  125. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/email.py +0 -0
  126. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/error.py +0 -0
  127. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/event.py +0 -0
  128. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/faas.py +0 -0
  129. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/file.py +0 -0
  130. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/geo.py +0 -0
  131. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/group.py +0 -0
  132. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/hash.py +0 -0
  133. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/host.py +0 -0
  134. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/http.py +0 -0
  135. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/ingress.py +0 -0
  136. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/interface.py +0 -0
  137. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/network.py +0 -0
  138. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/observer.py +0 -0
  139. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/organization.py +0 -0
  140. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/os.py +0 -0
  141. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/pe.py +0 -0
  142. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/process.py +0 -0
  143. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/registry.py +0 -0
  144. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/related.py +0 -0
  145. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/rule.py +0 -0
  146. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/server.py +0 -0
  147. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/threat.py +0 -0
  148. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/tls.py +0 -0
  149. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/url.py +0 -0
  150. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/user.py +0 -0
  151. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/user_agent.py +0 -0
  152. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/ecs/vulnerability.py +0 -0
  153. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/gcp.py +0 -0
  154. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/hit.py +0 -0
  155. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/howler_data.py +0 -0
  156. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/lead.py +0 -0
  157. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/localized_label.py +0 -0
  158. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/observable.py +0 -0
  159. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/overview.py +0 -0
  160. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/pivot.py +0 -0
  161. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/record.py +0 -0
  162. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/template.py +0 -0
  163. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/user.py +0 -0
  164. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/models/view.py +0 -0
  165. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/random_data.py +0 -0
  166. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/odm/randomizer.py +0 -0
  167. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/patched.py +0 -0
  168. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/plugins/__init__.py +0 -0
  169. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/plugins/config.py +0 -0
  170. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/remote/__init__.py +0 -0
  171. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/remote/datatypes/README.md +0 -0
  172. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/remote/datatypes/__init__.py +0 -0
  173. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/remote/datatypes/counters.py +0 -0
  174. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/remote/datatypes/events.py +0 -0
  175. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/remote/datatypes/hash.py +0 -0
  176. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/remote/datatypes/lock.py +0 -0
  177. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/remote/datatypes/queues/__init__.py +0 -0
  178. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/remote/datatypes/queues/comms.py +0 -0
  179. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/remote/datatypes/queues/multi.py +0 -0
  180. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/remote/datatypes/queues/named.py +0 -0
  181. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/remote/datatypes/queues/priority.py +0 -0
  182. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/remote/datatypes/set.py +0 -0
  183. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/remote/datatypes/user_quota_tracker.py +0 -0
  184. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/security/__init__.py +0 -0
  185. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/security/socket.py +0 -0
  186. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/security/utils.py +0 -0
  187. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/__init__.py +0 -0
  188. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/action_service.py +0 -0
  189. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/analytic_service.py +0 -0
  190. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/auth_service.py +0 -0
  191. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/bundle_compat_service.py +0 -0
  192. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/case_service.py +0 -0
  193. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/config_service.py +0 -0
  194. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/correlation_service.py +0 -0
  195. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/docs_service.py +0 -0
  196. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/dossier_service.py +0 -0
  197. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/event_service.py +0 -0
  198. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/hit_service.py +0 -0
  199. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/jwt_service.py +0 -0
  200. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/lucene_service.py +0 -0
  201. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/notebook_service.py +0 -0
  202. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/observable_service.py +0 -0
  203. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/overview_service.py +0 -0
  204. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/template_service.py +0 -0
  205. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/user_service.py +0 -0
  206. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/services/viewer_service.py +0 -0
  207. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/telemetry.py +0 -0
  208. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/utils/__init__.py +0 -0
  209. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/utils/annotations.py +0 -0
  210. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/utils/chunk.py +0 -0
  211. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/utils/compat.py +0 -0
  212. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/utils/constants.py +0 -0
  213. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/utils/dict_utils.py +0 -0
  214. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/utils/isotime.py +0 -0
  215. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/utils/list_utils.py +0 -0
  216. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/utils/lucene.py +0 -0
  217. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/utils/path.py +0 -0
  218. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/utils/socket_utils.py +0 -0
  219. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/utils/str_utils.py +0 -0
  220. {howler_api-4.0.0.dev850 → howler_api-4.0.0.dev871}/howler/utils/uid.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: howler-api
3
- Version: 4.0.0.dev850
3
+ Version: 4.0.0.dev871
4
4
  Summary: Howler - API server
5
5
  License: MIT
6
6
  Keywords: howler,alerting,gc,canada,cse-cst,cse,cst,cyber,cccs
@@ -0,0 +1,106 @@
1
+ """ETag utility module for handling HTTP ETags in Flask responses.
2
+
3
+ ETags (Entity Tags) are HTTP headers used for web cache validation and conditional requests.
4
+ They help optimize performance by allowing clients to cache responses and only fetch
5
+ new data when the resource has actually changed.
6
+ """
7
+
8
+ import functools
9
+ import re
10
+
11
+ from flask import Response, request
12
+
13
+ from howler.api import not_modified
14
+
15
+
16
+ def add_etag(getter=None, check_if_match=True):
17
+ """Decorator to add ETag handling to a Flask response.
18
+
19
+ This decorator implements HTTP ETag functionality for API endpoints, enabling:
20
+ - Conditional requests using If-Match headers
21
+ - Cache validation to prevent unnecessary data transfers
22
+ - Version tracking for resources
23
+
24
+ When ``getter`` is provided, the decorator pre-fetches the object and its
25
+ version, injects ``server_version`` into kwargs, caches the object, and
26
+ supports ``If-Match`` conditional requests.
27
+
28
+ When ``getter`` is ``None``, the decorator only handles converting
29
+ ``(Response, version)`` return tuples into a single Response with the
30
+ ``ETag`` header set. This is useful for endpoints that manage their own
31
+ version retrieval (e.g. v2 endpoints).
32
+
33
+ Args:
34
+ getter: Optional function that retrieves the object and its version.
35
+ When None, the decorator only handles ETag header setting.
36
+ check_if_match (bool): Whether to check If-Match headers for conditional requests.
37
+ Only used when getter is provided.
38
+
39
+ Returns:
40
+ Decorated function with ETag support
41
+ """
42
+
43
+ def wrapper(f):
44
+ """Inner wrapper function that applies ETag functionality to the decorated function."""
45
+
46
+ @functools.wraps(f)
47
+ def generate_etag(*args, **kwargs):
48
+ """Generate and handle ETags for the HTTP response."""
49
+ if getter is not None:
50
+ # Retrieve the object and its version using the provided getter function
51
+ # The getter should return (object, version) tuple
52
+ obj, version = getter(
53
+ kwargs.get("id", kwargs.get("username", None)),
54
+ as_odm=True,
55
+ version=True,
56
+ )
57
+
58
+ # Handle conditional requests with If-Match header
59
+ # If the client's version matches the current version and it's a GET request
60
+ # without metadata parameter, return 304 Not Modified to save bandwidth
61
+ if (
62
+ check_if_match
63
+ and "If-Match" in request.headers
64
+ and request.headers["If-Match"] == version
65
+ and request.method == "GET"
66
+ and "metadata" not in request.args
67
+ ):
68
+ return not_modified()
69
+
70
+ # Extract the resource type from the API path and create a cache key
71
+ # e.g., "/api/v1/users/123" becomes "cached_users"
72
+ key = re.sub(r"^\/api\/v\d+\/(\w+)\/.+$", r"cached_\1", request.path)
73
+ kwargs[key] = obj
74
+
75
+ # Call the original function with the cached object and version
76
+ values = f(*args, server_version=version, **kwargs)
77
+
78
+ # Handle different return value formats from the decorated function
79
+ # If there is only one return, it's just the response
80
+ if isinstance(values, Response):
81
+ # Only add ETag header for successful responses (not 409 Conflict or 400 Bad Request)
82
+ if values.status_code != 409 and values.status_code != 400:
83
+ values.headers["ETag"] = version
84
+ return values
85
+
86
+ # If there are two returns, it's the response and the new version
87
+ # This happens when the function modifies the resource and returns an updated version
88
+ else:
89
+ if values[0].status_code != 409 and values[0].status_code != 400:
90
+ # Add the new ETag version to successful responses
91
+ values[0].headers["ETag"] = values[1]
92
+ return values[0]
93
+
94
+ # No getter: just call the function and handle (Response, version) tuples
95
+ values = f(*args, **kwargs)
96
+
97
+ if isinstance(values, Response):
98
+ return values
99
+
100
+ if values[0].status_code != 409 and values[0].status_code != 400:
101
+ values[0].headers["ETag"] = values[1]
102
+ return values[0]
103
+
104
+ return generate_etag
105
+
106
+ return wrapper
@@ -5,6 +5,7 @@ from flask import request
5
5
  from mergedeep import Strategy, merge
6
6
 
7
7
  from howler.api import bad_request, created, forbidden, internal_error, make_subapi_blueprint, no_content, not_found, ok
8
+ from howler.api.v1.utils.etag import add_etag
8
9
  from howler.common.exceptions import HowlerException, HowlerValueError
9
10
  from howler.common.loader import datastore
10
11
  from howler.common.logging import get_logger
@@ -254,7 +255,8 @@ def validate(index: str, **kwargs):
254
255
  @generate_swagger_docs()
255
256
  @ingest_api.route("/<index>/<id>/overwrite", methods=["PATCH"])
256
257
  @api_login(audit=False, required_priv=["W"])
257
- def overwrite(index: str, id: str, server_version: str, **kwargs):
258
+ @add_etag()
259
+ def overwrite(index: str, id: str, **kwargs):
258
260
  """Overwrite a record.
259
261
 
260
262
  Variables:
@@ -301,7 +303,7 @@ def overwrite(index: str, id: str, server_version: str, **kwargs):
301
303
  ),
302
304
  )
303
305
 
304
- ds[index].save(id, odm(new_record) if odm else new_record, version=server_version)
306
+ ds[index].save(id, odm(new_record) if odm else new_record, version=kwargs.get("server_version"))
305
307
 
306
308
  new_record, new_version = ds[index].get(id, as_obj=False, version=True)
307
309
 
@@ -5,7 +5,6 @@ from typing import Any
5
5
  from elasticsearch import BadRequestError
6
6
  from elasticsearch._sync.client.indices import IndicesClient
7
7
  from flask import Request, request
8
- from werkzeug.exceptions import BadRequest
9
8
 
10
9
  from howler.api import bad_request, make_subapi_blueprint, ok
11
10
  from howler.common.loader import datastore
@@ -30,10 +29,7 @@ def generate_params(request: Request, fields: list[str], multi_fields: list[str]
30
29
  params = {}
31
30
 
32
31
  if request.method == "POST":
33
- try:
34
- req_data = request.json
35
- except BadRequest:
36
- req_data = {"query": "*:*"}
32
+ req_data = request.get_json(silent=True) or {"query": "*:*"}
37
33
 
38
34
  params = {
39
35
  **params,
@@ -131,7 +127,8 @@ def search(indexes: str, **kwargs):
131
127
  return bad_request(err="There was no search query.")
132
128
 
133
129
  metadata = params.pop("metadata", [])
134
- result = search_service.search(indexes, query, **params)
130
+ access_control = params.pop("access_control", None)
131
+ result = search_service.search(indexes, query, access_control=access_control, **params)
135
132
 
136
133
  if metadata and any(idx in index_list for idx in ["hit"]):
137
134
  hit_service.augment_metadata(result["items"], metadata, user)
@@ -236,7 +233,7 @@ def count(index, **kwargs):
236
233
 
237
234
  Result Example:
238
235
  {
239
- "total": 201, # Total results found
236
+ "count": 201, # Total results found
240
237
  }
241
238
  """
242
239
  user = kwargs["user"]
@@ -245,7 +242,7 @@ def count(index, **kwargs):
245
242
  if collection is None:
246
243
  return bad_request(err=f"Not a valid index to search in: {index}")
247
244
 
248
- params, req_data = generate_params(request, [], [])
245
+ params, req_data = generate_params(request, ["timeout"], ["filters"])
249
246
 
250
247
  boolean_fields = ["use_archive"]
251
248
  params.update(
@@ -256,15 +253,15 @@ def count(index, **kwargs):
256
253
  }
257
254
  )
258
255
 
259
- if has_access_control(index):
260
- params.update({"access_control": user["access_control"]})
256
+ access_control = user["access_control"] if has_access_control(index) else None
261
257
 
262
258
  query = req_data.get("query", None)
263
259
  if not query:
264
260
  return bad_request(err="There was no search query.")
265
261
 
262
+ filters = params.pop("filters", [])
266
263
  try:
267
- return ok(collection().count(query, **params))
264
+ return ok(collection().count(query, filters, access_control=access_control))
268
265
  except (SearchException, BadRequestError) as e:
269
266
  return bad_request(err=f"SearchException: {e}")
270
267
 
@@ -1922,7 +1922,7 @@ class ESCollection(Generic[ModelType]):
1922
1922
  search result object that consists of the following:
1923
1923
 
1924
1924
  {
1925
- "total": 123456, # Total number of documents matching the query
1925
+ "count": 123456, # Total number of documents matching the query
1926
1926
  }
1927
1927
 
1928
1928
  :param query: lucene query to search for
@@ -99,6 +99,7 @@ def search( # noqa: C901
99
99
  timeout: int | None = None,
100
100
  track_total_hits: bool = False,
101
101
  metadata: list[str] | None = None,
102
+ access_control: str | None = None,
102
103
  ) -> SearchResult[dict[str, Any]]:
103
104
  """Search through specified index for a given query. Uses lucene search syntax for query.
104
105
 
@@ -136,6 +137,9 @@ def search( # noqa: C901
136
137
  else:
137
138
  parsed_filters = filters
138
139
 
140
+ if access_control:
141
+ parsed_filters.append(access_control)
142
+
139
143
  if query is None:
140
144
  query = "id:*"
141
145
 
@@ -152,7 +152,7 @@ suppress-none-returning = true
152
152
  [tool.poetry]
153
153
  package-mode = true
154
154
  name = "howler-api"
155
- version = "4.0.0.dev850"
155
+ version = "4.0.0.dev871"
156
156
  description = "Howler - API server"
157
157
  authors = [
158
158
  "Canadian Centre for Cyber Security <howler@cyber.gc.ca>",
@@ -1,84 +0,0 @@
1
- """ETag utility module for handling HTTP ETags in Flask responses.
2
-
3
- ETags (Entity Tags) are HTTP headers used for web cache validation and conditional requests.
4
- They help optimize performance by allowing clients to cache responses and only fetch
5
- new data when the resource has actually changed.
6
- """
7
-
8
- import functools
9
- import re
10
-
11
- from flask import Response, request
12
-
13
- from howler.api import not_modified
14
-
15
-
16
- def add_etag(getter, check_if_match=True):
17
- """Decorator to add ETag handling to a Flask response.
18
-
19
- This decorator implements HTTP ETag functionality for API endpoints, enabling:
20
- - Conditional requests using If-Match headers
21
- - Cache validation to prevent unnecessary data transfers
22
- - Version tracking for resources
23
-
24
- Args:
25
- getter: Function that retrieves the object and its version
26
- check_if_match (bool): Whether to check If-Match headers for conditional requests
27
-
28
- Returns:
29
- Decorated function with ETag support
30
- """
31
-
32
- def wrapper(f):
33
- """Inner wrapper function that applies ETag functionality to the decorated function."""
34
-
35
- @functools.wraps(f)
36
- def generate_etag(*args, **kwargs):
37
- """Generate and handle ETags for the HTTP response."""
38
- # Retrieve the object and its version using the provided getter function
39
- # The getter should return (object, version) tuple
40
- obj, version = getter(
41
- kwargs.get("id", kwargs.get("username", None)),
42
- as_odm=True,
43
- version=True,
44
- )
45
-
46
- # Handle conditional requests with If-Match header
47
- # If the client's version matches the current version and it's a GET request
48
- # without metadata parameter, return 304 Not Modified to save bandwidth
49
- if (
50
- check_if_match
51
- and "If-Match" in request.headers
52
- and request.headers["If-Match"] == version
53
- and request.method == "GET"
54
- and "metadata" not in request.args
55
- ):
56
- return not_modified()
57
-
58
- # Extract the resource type from the API path and create a cache key
59
- # e.g., "/api/v1/users/123" becomes "cached_users"
60
- key = re.sub(r"^\/api\/v\d+\/(\w+)\/.+$", r"cached_\1", request.path)
61
- kwargs[key] = obj
62
-
63
- # Call the original function with the cached object and version
64
- values = f(*args, server_version=version, **kwargs)
65
-
66
- # Handle different return value formats from the decorated function
67
- # If there is only one return, it's just the response
68
- if isinstance(values, Response):
69
- # Only add ETag header for successful responses (not 409 Conflict or 400 Bad Request)
70
- if values.status_code != 409 and values.status_code != 400:
71
- values.headers["ETag"] = version
72
- return values
73
-
74
- # If there are two returns, it's the response and the new version
75
- # This happens when the function modifies the resource and returns an updated version
76
- else:
77
- if values[0].status_code != 409 and values[0].status_code != 400:
78
- # Add the new ETag version to successful responses
79
- values[0].headers["ETag"] = values[1]
80
- return values[0]
81
-
82
- return generate_etag
83
-
84
- return wrapper