mmisp-lib 0.8.0__tar.gz → 0.8.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/PKG-INFO +1 -1
  2. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/pyproject.toml +1 -1
  3. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/organisations.py +5 -5
  4. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/sharing_groups.py +51 -1
  5. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/commandline_tool/main.py +2 -2
  6. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/commandline_tool/organisation.py +3 -3
  7. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/config.py +19 -14
  8. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/database.py +3 -1
  9. mmisp_lib-0.8.2/src/mmisp/db/list_json_type.py +31 -0
  10. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/galaxy_cluster.py +2 -19
  11. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/organisation.py +2 -1
  12. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/server.py +2 -1
  13. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/sharing_group.py +44 -1
  14. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/compatibility_helpers.py +13 -0
  15. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/organisation_generator.py +1 -1
  16. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp_lib.egg-info/PKG-INFO +1 -1
  17. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp_lib.egg-info/SOURCES.txt +1 -0
  18. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/tests/test_commandline_tool.py +3 -3
  19. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/LICENSE +0 -0
  20. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/README.md +0 -0
  21. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/setup.cfg +0 -0
  22. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/__init__.py +0 -0
  23. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/attributes.py +0 -0
  24. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/auth_keys.py +0 -0
  25. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/authentication.py +0 -0
  26. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/common.py +0 -0
  27. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/events.py +0 -0
  28. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/feeds.py +0 -0
  29. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/galaxies.py +0 -0
  30. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/galaxy_clusters.py +0 -0
  31. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/galaxy_common.py +0 -0
  32. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/jobs.py +0 -0
  33. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/logs.py +0 -0
  34. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/noticelists.py +0 -0
  35. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/objects.py +0 -0
  36. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/py.typed +0 -0
  37. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/responses/__init__.py +0 -0
  38. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/responses/check_graph_response.py +0 -0
  39. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/responses/standard_status_response.py +0 -0
  40. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/roles.py +0 -0
  41. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/server.py +0 -0
  42. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/servers.py +0 -0
  43. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/shadow_attribute.py +0 -0
  44. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/sightings.py +0 -0
  45. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/statistics.py +0 -0
  46. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/tags.py +0 -0
  47. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/taxonomies.py +0 -0
  48. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/user_settings.py +0 -0
  49. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/users.py +0 -0
  50. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/warninglists.py +0 -0
  51. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/api_schemas/workflows.py +0 -0
  52. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/commandline_tool/__init__.py +0 -0
  53. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/commandline_tool/py.typed +0 -0
  54. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/commandline_tool/setup.py +0 -0
  55. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/commandline_tool/user.py +0 -0
  56. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/__init__.py +0 -0
  57. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/additional_properties.py +0 -0
  58. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/all_models.py +0 -0
  59. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/lib.py +0 -0
  60. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/mixins.py +0 -0
  61. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/__init__.py +0 -0
  62. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/admin_setting.py +0 -0
  63. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/attribute.py +0 -0
  64. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/auth_key.py +0 -0
  65. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/blocklist.py +0 -0
  66. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/correlation.py +0 -0
  67. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/event.py +0 -0
  68. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/feed.py +0 -0
  69. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/galaxy.py +0 -0
  70. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/identity_provider.py +0 -0
  71. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/log.py +0 -0
  72. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/noticelist.py +0 -0
  73. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/object.py +0 -0
  74. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/post.py +0 -0
  75. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/role.py +0 -0
  76. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/shadow_attribute.py +0 -0
  77. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/sighting.py +0 -0
  78. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/tag.py +0 -0
  79. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/taxonomy.py +0 -0
  80. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/threat_level.py +0 -0
  81. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/user.py +0 -0
  82. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/user_setting.py +0 -0
  83. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/warninglist.py +0 -0
  84. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/workflow.py +0 -0
  85. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/models/workflow_blueprint.py +0 -0
  86. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/mypy.py +0 -0
  87. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/print_changes.py +0 -0
  88. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/py.typed +0 -0
  89. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/db/uuid_type.py +0 -0
  90. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/lib/__init__.py +0 -0
  91. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/lib/actions.py +0 -0
  92. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/lib/attribute_search_filter.py +0 -0
  93. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/lib/attributes.py +0 -0
  94. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/lib/distribution.py +0 -0
  95. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/lib/fallbacks.py +0 -0
  96. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/lib/galaxies.py +0 -0
  97. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/lib/galaxy_clusters.py +0 -0
  98. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/lib/logger.py +0 -0
  99. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/lib/permissions.py +0 -0
  100. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/lib/py.typed +0 -0
  101. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/lib/serialisation_helper.py +0 -0
  102. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/lib/uuid.py +0 -0
  103. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/plugins/__init__.py +0 -0
  104. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/plugins/enrichment/__init__.py +0 -0
  105. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/plugins/enrichment/data.py +0 -0
  106. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/plugins/enrichment/enrichment_plugin.py +0 -0
  107. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/plugins/exceptions.py +0 -0
  108. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/plugins/models/__init__.py +0 -0
  109. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/plugins/models/attribute.py +0 -0
  110. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/plugins/plugin_info.py +0 -0
  111. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/plugins/plugin_type.py +0 -0
  112. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/plugins/py.typed +0 -0
  113. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/__init__.py +0 -0
  114. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/fixtures.py +0 -0
  115. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/attribute_generator.py +0 -0
  116. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/feed_generator.py +0 -0
  117. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/attribute_generator.py +0 -0
  118. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/auth_key_generator.py +0 -0
  119. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/correlation_exclusions_generator.py +0 -0
  120. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/correlation_value_generator.py +0 -0
  121. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/default_correlation_generator.py +0 -0
  122. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/event_generator.py +0 -0
  123. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/galaxy_generator.py +0 -0
  124. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/identity_provider_generator.py +0 -0
  125. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/noticelist_generator.py +0 -0
  126. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/object_generator.py +0 -0
  127. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/over_correlating_value_generator.py +0 -0
  128. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/post_generator.py +0 -0
  129. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/role_generator.py +0 -0
  130. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/server_generator.py +0 -0
  131. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/shadow_attribute_generator.py +0 -0
  132. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/sharing_group_generator.py +0 -0
  133. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/sighting_generator.py +0 -0
  134. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/tag_generator.py +0 -0
  135. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/taxonomy_generator.py +0 -0
  136. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/user_generator.py +0 -0
  137. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/user_setting_generator.py +0 -0
  138. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/warninglist_generator.py +0 -0
  139. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/model_generators/workflow_generator.py +0 -0
  140. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/object_generator.py +0 -0
  141. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/sighting_generator.py +0 -0
  142. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/tag_generator.py +0 -0
  143. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/tests/generators/taxonomies_generator.py +0 -0
  144. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/util/__init__.py +0 -0
  145. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/util/crypto.py +0 -0
  146. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/util/models.py +0 -0
  147. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/util/partial.py +0 -0
  148. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/util/py.typed +0 -0
  149. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/util/uuid.py +0 -0
  150. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/workflows/__init__.py +0 -0
  151. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/workflows/execution.py +0 -0
  152. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/workflows/fastapi.py +0 -0
  153. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/workflows/graph.py +0 -0
  154. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/workflows/input.py +0 -0
  155. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/workflows/legacy.py +0 -0
  156. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/workflows/misp_core_format.py +0 -0
  157. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/workflows/modules.py +0 -0
  158. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp/workflows/py.typed +0 -0
  159. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp_lib.egg-info/dependency_links.txt +0 -0
  160. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp_lib.egg-info/entry_points.txt +0 -0
  161. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp_lib.egg-info/requires.txt +0 -0
  162. {mmisp_lib-0.8.0 → mmisp_lib-0.8.2}/src/mmisp_lib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mmisp-lib
3
- Version: 0.8.0
3
+ Version: 0.8.2
4
4
  Requires-Python: >=3.11.0
5
5
  Description-Content-Type: text/markdown
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mmisp-lib"
3
- version = "0.8.0"
3
+ version = "0.8.2"
4
4
  description = ""
5
5
  authors = []
6
6
  readme = "README.md"
@@ -23,7 +23,7 @@ class Organisation(BaseOrganisation):
23
23
  contacts: str | None = None
24
24
  local: bool
25
25
  """organisation gains access to the local instance, otherwise treated as external"""
26
- restricted_to_domain: str | list | None = None
26
+ restricted_to_domain: list | str | None = None
27
27
  landingpage: str | None = None
28
28
 
29
29
  class Config:
@@ -45,7 +45,7 @@ class GetOrganisationResponse(BaseModel):
45
45
  created_by: str
46
46
  contacts: str | None = None
47
47
  local: bool
48
- restricted_to_domain: str | list | None = None
48
+ restricted_to_domain: list | str | None = None
49
49
  landingpage: str | None = None
50
50
 
51
51
  def dict(self: Self, *args, **kwargs) -> dict:
@@ -90,7 +90,7 @@ class OrganisationUsersResponse(BaseModel):
90
90
  uuid: str | None = None
91
91
  contacts: str | None = None
92
92
  local: bool | None = None
93
- restricted_to_domain: str | list | None = None
93
+ restricted_to_domain: list | str | None = None
94
94
  landingpage: str | None = None
95
95
 
96
96
 
@@ -105,7 +105,7 @@ class AddOrganisation(BaseModel):
105
105
  contacts: str | None = None
106
106
  local: bool
107
107
  """organisation gains access to the local instance, otherwise treated as external"""
108
- restricted_to_domain: str | None = None
108
+ restricted_to_domain: list[str] | None = None
109
109
  landingpage: str | None = None
110
110
 
111
111
  class Config:
@@ -121,7 +121,7 @@ class EditOrganisation(BaseModel):
121
121
  contacts: str | None = None
122
122
  local: bool
123
123
  """organisation gains access to the local instance, otherwise treated as external"""
124
- restricted_to_domain: str | None = None
124
+ restricted_to_domain: list[str] | None = None
125
125
  landingpage: str | None = None
126
126
 
127
127
  class Config:
@@ -1,3 +1,4 @@
1
+ import uuid
1
2
  from datetime import datetime
2
3
 
3
4
  from pydantic import BaseModel, Field
@@ -22,6 +23,28 @@ class SharingGroup(BaseModel):
22
23
  roaming: bool
23
24
 
24
25
 
26
+ # org_count: int = 0
27
+
28
+
29
+ class ShortSharingGroup(BaseModel):
30
+ id: int
31
+ name: str
32
+ releasability: str
33
+ description: str
34
+ uuid: str
35
+ active: bool
36
+ local: bool
37
+ roaming: bool
38
+
39
+ org_count: int = 0
40
+
41
+
42
+ class ShortOrganisation(BaseModel):
43
+ id: int
44
+ name: str
45
+ uuid: uuid.UUID
46
+
47
+
25
48
  class SharingGroupServer(BaseModel):
26
49
  id: int
27
50
  sharing_group_id: int
@@ -81,12 +104,39 @@ class ViewUpdateSharingGroupLegacyResponseSharingGroupOrgItem(BaseModel):
81
104
  Organisation: ViewUpdateSharingGroupLegacyResponseOrganisationInfo
82
105
 
83
106
 
84
- class ViewUpdateSharingGroupLegacyResponse(BaseModel):
107
+ class SharingGroupResponse(BaseModel):
108
+ SharingGroup: ShortSharingGroup
109
+ Organisation: ShortOrganisation
110
+ SharingGroupOrg: list[ViewUpdateSharingGroupLegacyResponseSharingGroupOrgItem]
111
+ SharingGroupServer: list[ViewUpdateSharingGroupLegacyResponseSharingGroupServerItem]
112
+
113
+ editable: bool | None = None
114
+ deletable: bool | None = None
115
+
116
+ class Config:
117
+ json_encoders = {datetime: lambda v: v.strftime("%Y-%m-%d %H:%M:%S")}
118
+
119
+
120
+ class SingleSharingGroupResponse(BaseModel):
85
121
  SharingGroup: SharingGroup
86
122
  Organisation: Organisation
87
123
  SharingGroupOrg: list[ViewUpdateSharingGroupLegacyResponseSharingGroupOrgItem]
88
124
  SharingGroupServer: list[ViewUpdateSharingGroupLegacyResponseSharingGroupServerItem]
89
125
 
126
+ class Config:
127
+ json_encoders = {datetime: lambda v: v.strftime("%Y-%m-%d %H:%M:%S")}
128
+
129
+
130
+ class ViewUpdateSharingGroupLegacyResponse(SharingGroupResponse):
131
+ pass
132
+
133
+
134
+ class GetSharingGroupsIndex(BaseModel):
135
+ response: list[SharingGroupResponse]
136
+
137
+ class Config:
138
+ json_encoders = {datetime: lambda v: v.strftime("%Y-%m-%d %H:%M:%S")}
139
+
90
140
 
91
141
  class UpdateSharingGroupLegacyBody(BaseModel):
92
142
  id: int | None = None
@@ -43,7 +43,7 @@ async def create_organisation(
43
43
  sector: str | None = None,
44
44
  contacts_email: str | None = None,
45
45
  local: bool | None = None,
46
- restricted_domain: str | None = None,
46
+ restricted_domain: list[str] | None = None,
47
47
  landingpage: str | None = None,
48
48
  ) -> str:
49
49
  """create-organisation <name> [-admin_email <admin_email>] [- description <description>] [-type <type>]
@@ -115,7 +115,7 @@ async def edit_organisation(
115
115
  sector: str | None = None,
116
116
  contacts_email: str | None = None,
117
117
  local: bool | None = None,
118
- restricted_domain: str | None = None,
118
+ restricted_domain: list[str] | None = None,
119
119
  landingpage: str | None = None,
120
120
  ) -> str:
121
121
  """edit-organisation <organisation> [-new_name <new_name>] [-admin_email <admin_email>] [-description <description>]
@@ -18,7 +18,7 @@ async def create(
18
18
  sector: str | None,
19
19
  contacts_email: str | None,
20
20
  local: bool | None,
21
- restricted_domain: str | None,
21
+ restricted_domain: list[str] | None,
22
22
  landingpage: str | None,
23
23
  ) -> None:
24
24
  organisation = Organisation()
@@ -67,7 +67,7 @@ async def edit_organisation(
67
67
  sector: str | None,
68
68
  contacts_email: str | None,
69
69
  local: bool | None,
70
- restricted_domain: str | None,
70
+ restricted_domain: list[str] | None,
71
71
  landingpage: str | None,
72
72
  ) -> None:
73
73
  if isinstance(organisation, str):
@@ -109,7 +109,7 @@ async def set_attributes(
109
109
  sector: str | None,
110
110
  contacts_email: str | None,
111
111
  local: bool | None,
112
- restricted_domain: str | None,
112
+ restricted_domain: list[str] | None,
113
113
  landingpage: str | None,
114
114
  ) -> None:
115
115
  if name is not None:
@@ -10,33 +10,38 @@ The following environment variables are supported:
10
10
  """
11
11
 
12
12
  import logging
13
- from dataclasses import dataclass
14
13
  from os import getenv
15
14
 
16
15
  from dotenv import load_dotenv
16
+ from pydantic import BaseSettings, Field, root_validator
17
17
 
18
18
 
19
- @dataclass
20
- class DatabaseConfig:
21
- DATABASE_URL: str
22
- DEBUG: bool
23
- RETRY_SLEEP: int
24
- MAX_RETRIES: int
19
+ class DatabaseConfig(BaseSettings):
20
+ DATABASE_URL: str | None = None
21
+ DEBUG: bool = False
22
+ RETRY_SLEEP: int = Field(5, env="DB_RETRY")
23
+ MAX_RETRIES: int = Field(100, env="DB_MAX_RETRIES")
24
+ CONNECTION_INIT: bool = True
25
25
  DB_LOGLEVEL: str | None = None
26
26
 
27
+ @root_validator(skip_on_failure=True)
28
+ def require_db_url_or_no_connection(cls: "DatabaseConfig", values: dict) -> dict:
29
+ db_url = values.get("DATABASE_URL")
30
+ connection_init = values.get("CONNECTION_INIT")
31
+ if db_url is None and connection_init:
32
+ raise ValueError("Environment variable DATABASE_URL is required when CONNECTION_INIT is not False")
33
+
34
+ return values
35
+
27
36
 
28
37
  load_dotenv(getenv("ENV_FILE", ".env"))
29
38
 
30
39
 
31
- config: DatabaseConfig = DatabaseConfig(
32
- DATABASE_URL=getenv("DATABASE_URL", ""),
33
- DEBUG=bool(getenv("DEBUG", False)),
34
- RETRY_SLEEP=int(getenv("DB_RETRY", 5)),
35
- MAX_RETRIES=int(getenv("DB_MAX_RETRIES", 100)),
36
- DB_LOGLEVEL=getenv("DB_LOGLEVEL", None),
37
- )
40
+ config: DatabaseConfig = DatabaseConfig()
41
+
38
42
  sqlalchemy_logger = logging.getLogger("sqlalchemy.engine")
39
43
  sqlalchemy_logger.setLevel(logging.WARNING)
44
+
40
45
  if config.DEBUG:
41
46
  sqlalchemy_logger.setLevel(logging.INFO)
42
47
 
@@ -114,4 +114,6 @@ async def create_all_models() -> None:
114
114
  await sessionmanager.create_all()
115
115
 
116
116
 
117
- sessionmanager = DatabaseSessionManager()
117
+ sessionmanager = None
118
+ if config.CONNECTION_INIT:
119
+ sessionmanager = DatabaseSessionManager()
@@ -0,0 +1,31 @@
1
+ import json
2
+ from typing import Any, Self
3
+
4
+ from sqlalchemy.engine import Dialect
5
+ from sqlalchemy.types import Text, TypeDecorator
6
+
7
+
8
+ class DBListJson(TypeDecorator):
9
+ impl = Text
10
+
11
+ def load_dialect_impl(self: Self, dialect: Dialect) -> Any:
12
+ return dialect.type_descriptor(Text)
13
+
14
+ def process_bind_param(self: Self, value: Any, dialect: Dialect) -> str | None:
15
+ """Handle value before getting into the DB"""
16
+ if value is None:
17
+ return None
18
+ if not isinstance(value, list):
19
+ raise ValueError("this type should only be used for lists")
20
+ return json.dumps(value)
21
+
22
+ def process_result_value(self: Self, value: Any, dialect: Dialect) -> list | None:
23
+ """Handle values from the database"""
24
+ if value is None:
25
+ return None
26
+
27
+ res = json.loads(value)
28
+ if not isinstance(res, list):
29
+ raise ValueError("this type should only be used for lists")
30
+
31
+ return res
@@ -1,10 +1,7 @@
1
- import json
2
- from typing import Self
3
-
4
1
  from sqlalchemy import Boolean, ForeignKey, Integer, String, Text
5
- from sqlalchemy.ext.hybrid import hybrid_property
6
2
  from sqlalchemy.orm import relationship
7
3
 
4
+ from mmisp.db.list_json_type import DBListJson
8
5
  from mmisp.db.mixins import DictMixin, UpdateMixin
9
6
  from mmisp.db.mypy import Mapped, mapped_column
10
7
  from mmisp.db.uuid_type import DBUUID
@@ -28,7 +25,7 @@ class GalaxyCluster(Base, UpdateMixin, DictMixin):
28
25
  Integer, ForeignKey(Galaxy.id, ondelete="CASCADE"), nullable=False, index=True
29
26
  )
30
27
  source: Mapped[str] = mapped_column(String(255), nullable=False, default="")
31
- _authors: Mapped[str] = mapped_column("authors", Text, nullable=False)
28
+ authors: Mapped[list[str]] = mapped_column(DBListJson, nullable=False)
32
29
  version: Mapped[int] = mapped_column(Integer, default=0, index=True)
33
30
  distribution: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
34
31
  sharing_group_id: Mapped[int] = mapped_column(Integer, index=True, nullable=True, default=None)
@@ -75,20 +72,6 @@ class GalaxyCluster(Base, UpdateMixin, DictMixin):
75
72
  uselist=False,
76
73
  ) # type:ignore[assignment,var-annotated]
77
74
 
78
- def __init__(self: Self, *arg, **kwargs) -> None:
79
- if "authors" in kwargs:
80
- self._authors = json.dumps(kwargs["authors"])
81
- del kwargs["authors"]
82
- super().__init__(*arg, **kwargs)
83
-
84
- @hybrid_property
85
- def authors(self: Self) -> list[str]:
86
- return json.loads(self._authors)
87
-
88
- @authors.setter # type: ignore[no-redef]
89
- def authors(self: Self, value: list[str]) -> None:
90
- self._authors = json.dumps(value)
91
-
92
75
 
93
76
  class GalaxyElement(Base, DictMixin, UpdateMixin):
94
77
  __tablename__ = "galaxy_elements"
@@ -3,6 +3,7 @@ from datetime import datetime
3
3
  from sqlalchemy import Boolean, DateTime, Integer, String, Text
4
4
  from sqlalchemy.orm import relationship
5
5
 
6
+ from mmisp.db.list_json_type import DBListJson
6
7
  from mmisp.db.mixins import DictMixin
7
8
  from mmisp.db.mypy import Mapped, mapped_column
8
9
 
@@ -26,7 +27,7 @@ class Organisation(Base, DictMixin):
26
27
  uuid: Mapped[str] = mapped_column(String(255), unique=True)
27
28
  contacts: Mapped[str] = mapped_column(Text)
28
29
  local: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
29
- restricted_to_domain: Mapped[str] = mapped_column(Text)
30
+ restricted_to_domain: Mapped[list[str]] = mapped_column(DBListJson)
30
31
  landingpage: Mapped[str] = mapped_column(Text)
31
32
 
32
33
  # Relationship to users
@@ -1,12 +1,13 @@
1
1
  from sqlalchemy import Boolean, Integer, String, Text
2
2
  from sqlalchemy.orm import relationship
3
3
 
4
+ from mmisp.db.mixins import DictMixin
4
5
  from mmisp.db.mypy import Mapped, mapped_column
5
6
 
6
7
  from ..database import Base
7
8
 
8
9
 
9
- class Server(Base):
10
+ class Server(Base, DictMixin):
10
11
  __tablename__ = "servers"
11
12
 
12
13
  id: Mapped[int] = mapped_column(Integer, primary_key=True, nullable=False)
@@ -1,6 +1,7 @@
1
1
  from datetime import datetime
2
2
 
3
3
  from sqlalchemy import Boolean, DateTime, Integer, String, Text
4
+ from sqlalchemy.orm import relationship
4
5
 
5
6
  from mmisp.db.mixins import DictMixin
6
7
  from mmisp.db.mypy import Mapped, mapped_column
@@ -18,7 +19,7 @@ class SharingGroup(Base, DictMixin):
18
19
  description = mapped_column(Text, nullable=False, default="")
19
20
  uuid = mapped_column(String(40), unique=True, default=uuid, nullable=False)
20
21
  organisation_uuid = mapped_column(String(40), nullable=False)
21
- org_id = mapped_column(Integer, nullable=False, index=True)
22
+ org_id = mapped_column(Integer, nullable=False, index=True) # the organisation that created the sharing group
22
23
  sync_user_id = mapped_column(Integer, nullable=False, default=0, index=True)
23
24
  active = mapped_column(Boolean, nullable=False, default=False)
24
25
  created = mapped_column(DateTime, default=datetime.utcnow, nullable=False)
@@ -26,6 +27,34 @@ class SharingGroup(Base, DictMixin):
26
27
  local = mapped_column(Boolean, nullable=False, default=True)
27
28
  roaming = mapped_column(Boolean, default=False, nullable=False)
28
29
 
30
+ creator_org = relationship(
31
+ "Organisation",
32
+ primaryjoin="SharingGroup.org_id == Organisation.id",
33
+ lazy="raise_on_sql",
34
+ foreign_keys="SharingGroup.org_id",
35
+ ) # type:ignore[assignment,var-annotated]
36
+ organisations = relationship(
37
+ "Organisation",
38
+ primaryjoin="SharingGroup.id == SharingGroupOrg.sharing_group_id",
39
+ secondary="sharing_group_orgs",
40
+ secondaryjoin="SharingGroupOrg.org_id == Organisation.id",
41
+ lazy="raise_on_sql",
42
+ viewonly=True,
43
+ )
44
+
45
+ sharing_group_orgs = relationship(
46
+ "SharingGroupOrg",
47
+ primaryjoin="SharingGroup.id == SharingGroupOrg.sharing_group_id",
48
+ lazy="raise_on_sql",
49
+ foreign_keys="SharingGroupOrg.sharing_group_id",
50
+ ) # type:ignore[assignment,var-annotated]
51
+ sharing_group_servers = relationship(
52
+ "SharingGroupServer",
53
+ primaryjoin="SharingGroup.id == SharingGroupServer.sharing_group_id",
54
+ lazy="raise_on_sql",
55
+ foreign_keys="SharingGroupServer.sharing_group_id",
56
+ ) # type:ignore[assignment,var-annotated]
57
+
29
58
 
30
59
  class SharingGroupOrg(Base, DictMixin):
31
60
  __tablename__ = "sharing_group_orgs"
@@ -35,6 +64,13 @@ class SharingGroupOrg(Base, DictMixin):
35
64
  org_id = mapped_column(Integer, index=True, nullable=False)
36
65
  extend = mapped_column(Boolean, default=False, nullable=False)
37
66
 
67
+ organisation = relationship(
68
+ "Organisation",
69
+ primaryjoin="SharingGroupOrg.org_id == Organisation.id",
70
+ lazy="raise_on_sql",
71
+ foreign_keys="SharingGroupOrg.org_id",
72
+ ) # type:ignore[assignment,var-annotated]
73
+
38
74
 
39
75
  class SharingGroupServer(Base, DictMixin):
40
76
  __tablename__ = "sharing_group_servers"
@@ -43,3 +79,10 @@ class SharingGroupServer(Base, DictMixin):
43
79
  sharing_group_id = mapped_column(Integer, index=True, nullable=False)
44
80
  server_id = mapped_column(Integer, index=True, nullable=False)
45
81
  all_orgs = mapped_column(Boolean, index=True, nullable=False, default=False)
82
+
83
+ server = relationship(
84
+ "Server",
85
+ primaryjoin="SharingGroupServer.server_id == Server.id",
86
+ lazy="raise_on_sql",
87
+ foreign_keys="SharingGroupServer.server_id",
88
+ ) # type:ignore[assignment,var-annotated]
@@ -1,3 +1,5 @@
1
+ import json
2
+
1
3
  import httpx
2
4
  from deepdiff import DeepDiff
3
5
  from icecream import ic
@@ -62,4 +64,15 @@ def get_legacy_modern_diff(http_method, path, body, auth_key, client, preprocess
62
64
  if diff["dictionary_item_removed"] == {}:
63
65
  del diff["dictionary_item_removed"]
64
66
 
67
+ # somehow misp manages to maintain the json encoded restricted_to_domain str in GalaxyCluster only.
68
+ # we will ignore this, this is too broken
69
+ if diff.get("type_changes", False):
70
+ diff["type_changes"] = {
71
+ k: v
72
+ for k, v in diff["type_changes"].items()
73
+ if not ("restricted_to_domain" in k and v["new_value"] == json.dumps(v["old_value"]))
74
+ }
75
+ if diff["type_changes"] == {}:
76
+ del diff["type_changes"]
77
+
65
78
  return diff
@@ -15,6 +15,6 @@ def generate_organisation() -> Organisation:
15
15
  created_by=0,
16
16
  contacts="Test Org\r\nBuilding 42\r\nAdenauerring 7\r\n76131 Karlsruhe\r\nGermany",
17
17
  local=True,
18
- restricted_to_domain="",
18
+ restricted_to_domain=[],
19
19
  landingpage="",
20
20
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mmisp-lib
3
- Version: 0.8.0
3
+ Version: 0.8.2
4
4
  Requires-Python: >=3.11.0
5
5
  Description-Content-Type: text/markdown
6
6
  License-File: LICENSE
@@ -45,6 +45,7 @@ src/mmisp/db/all_models.py
45
45
  src/mmisp/db/config.py
46
46
  src/mmisp/db/database.py
47
47
  src/mmisp/db/lib.py
48
+ src/mmisp/db/list_json_type.py
48
49
  src/mmisp/db/mixins.py
49
50
  src/mmisp/db/mypy.py
50
51
  src/mmisp/db/print_changes.py
@@ -55,7 +55,7 @@ async def test_create_organisation(db, site_admin_user) -> None:
55
55
  sector = time_now
56
56
  contacts_email = time_now
57
57
  local = False
58
- restricted_domain = False
58
+ restricted_domain: list[str] = []
59
59
  landingpage = time_now
60
60
  await main.create_organisation(
61
61
  name, admin_email, description, type, nationality, sector, contacts_email, local, restricted_domain, landingpage
@@ -157,7 +157,7 @@ async def test_edit_organisation(db, organisation, site_admin_user) -> None:
157
157
  new_sector = time_now
158
158
  new_contacts_email = time_now
159
159
  new_local = False
160
- new_restricted_domain = "{}"
160
+ new_restricted_domain: list[str] = []
161
161
  new_landingpage = time_now
162
162
  await main.edit_organisation(
163
163
  organisation.name,
@@ -181,7 +181,7 @@ async def test_edit_organisation(db, organisation, site_admin_user) -> None:
181
181
  assert organisation.sector == new_sector
182
182
  assert organisation.contacts == new_contacts_email
183
183
  assert bool(organisation.local) is new_local
184
- assert str(organisation.restricted_to_domain) == new_restricted_domain
184
+ assert organisation.restricted_to_domain == new_restricted_domain
185
185
  assert organisation.landingpage == new_landingpage
186
186
 
187
187
  try:
File without changes
File without changes
File without changes
File without changes