clue-api 1.5.0.dev262__tar.gz → 1.5.0.dev271__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 (99) hide show
  1. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/PKG-INFO +1 -1
  2. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/models/network.py +5 -3
  3. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/plugin/__init__.py +141 -163
  4. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/pyproject.toml +1 -1
  5. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/LICENSE +0 -0
  6. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/README.md +0 -0
  7. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/.gitignore +0 -0
  8. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/__init__.py +0 -0
  9. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/api/__init__.py +0 -0
  10. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/api/base.py +0 -0
  11. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/api/v1/__init__.py +0 -0
  12. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/api/v1/actions.py +0 -0
  13. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/api/v1/auth.py +0 -0
  14. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/api/v1/configs.py +0 -0
  15. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/api/v1/fetchers.py +0 -0
  16. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/api/v1/lookup.py +0 -0
  17. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/api/v1/registration.py +0 -0
  18. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/api/v1/static.py +0 -0
  19. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/api/v1/sync.py +0 -0
  20. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/app.py +0 -0
  21. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/cache/__init__.py +0 -0
  22. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/common/__init__.py +0 -0
  23. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/common/bytes_utils.py +0 -0
  24. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/common/classification.py +0 -0
  25. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/common/classification.yml +0 -0
  26. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/common/dict_utils.py +0 -0
  27. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/common/exceptions.py +0 -0
  28. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/common/forge.py +0 -0
  29. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/common/json_utils.py +0 -0
  30. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/common/list_utils.py +0 -0
  31. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/common/logging/__init__.py +0 -0
  32. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/common/logging/audit.py +0 -0
  33. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/common/logging/format.py +0 -0
  34. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/common/regex.py +0 -0
  35. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/common/str_utils.py +0 -0
  36. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/common/swagger.py +0 -0
  37. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/common/uid.py +0 -0
  38. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/config.py +0 -0
  39. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/constants/__init__.py +0 -0
  40. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/constants/env.py +0 -0
  41. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/constants/supported_types.py +0 -0
  42. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/cronjobs/__init__.py +0 -0
  43. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/cronjobs/plugins.py +0 -0
  44. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/error.py +0 -0
  45. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/extensions/__init__.py +0 -0
  46. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/extensions/config.py +0 -0
  47. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/gunicorn_config.py +0 -0
  48. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/healthz.py +0 -0
  49. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/helper/discover.py +0 -0
  50. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/helper/headers.py +0 -0
  51. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/helper/oauth.py +0 -0
  52. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/models/__init__.py +0 -0
  53. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/models/actions.py +0 -0
  54. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/models/config.py +0 -0
  55. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/models/fetchers.py +0 -0
  56. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/models/graph.py +0 -0
  57. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/models/model_list.py +0 -0
  58. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/models/results/__init__.py +0 -0
  59. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/models/results/base.py +0 -0
  60. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/models/results/file.py +0 -0
  61. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/models/results/graph.py +0 -0
  62. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/models/results/image.py +0 -0
  63. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/models/results/status.py +0 -0
  64. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/models/results/validation.py +0 -0
  65. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/models/schema.py +0 -0
  66. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/models/selector.py +0 -0
  67. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/models/sync.py +0 -0
  68. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/models/validators.py +0 -0
  69. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/patched.py +0 -0
  70. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/plugin/celery_app.py +0 -0
  71. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/plugin/helpers/__init__.py +0 -0
  72. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/plugin/helpers/central_server.py +0 -0
  73. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/plugin/helpers/email_render.py +0 -0
  74. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/plugin/helpers/token.py +0 -0
  75. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/plugin/helpers/trino.py +0 -0
  76. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/plugin/models.py +0 -0
  77. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/plugin/utils.py +0 -0
  78. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/py.typed +0 -0
  79. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/remote/__init__.py +0 -0
  80. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/remote/datatypes/__init__.py +0 -0
  81. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/remote/datatypes/cache.py +0 -0
  82. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/remote/datatypes/events.py +0 -0
  83. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/remote/datatypes/hash.py +0 -0
  84. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/remote/datatypes/queues/__init__.py +0 -0
  85. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/remote/datatypes/queues/comms.py +0 -0
  86. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/remote/datatypes/set.py +0 -0
  87. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/remote/datatypes/user_quota_tracker.py +0 -0
  88. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/security/__init__.py +0 -0
  89. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/security/obo.py +0 -0
  90. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/security/utils.py +0 -0
  91. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/services/action_service.py +0 -0
  92. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/services/auth_service.py +0 -0
  93. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/services/config_service.py +0 -0
  94. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/services/fetcher_service.py +0 -0
  95. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/services/jwt_service.py +0 -0
  96. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/services/lookup_service.py +0 -0
  97. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/services/mongo_service.py +0 -0
  98. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/services/type_service.py +0 -0
  99. {clue_api-1.5.0.dev262 → clue_api-1.5.0.dev271}/clue/services/user_service.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clue-api
3
- Version: 1.5.0.dev262
3
+ Version: 1.5.0.dev271
4
4
  Summary: Clue distributed enrichment service
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -362,6 +362,7 @@ class QueryEntry(BaseModel):
362
362
  examples=sample(sorted(CLASSIFICATION.list_all_classification_combinations()), k=5),
363
363
  ),
364
364
  ] = "TLP:CLEAR"
365
+
365
366
  count: Annotated[
366
367
  int,
367
368
  Field(
@@ -369,14 +370,17 @@ class QueryEntry(BaseModel):
369
370
  examples=sorted([floor(i / 10) for i in randbytes(5)]), # noqa: S311
370
371
  ),
371
372
  ] = 1
373
+
372
374
  link: Annotated[
373
375
  Optional[Url],
374
376
  Field(description="Link to more information", examples=[Url("https://example.com/moreinfo"), None]),
375
377
  ] = None
378
+
376
379
  annotations: Annotated[
377
380
  list[Annotation],
378
381
  Field(description="A list of annotations returned from the service for this entry"),
379
382
  ] = []
383
+
380
384
  raw_data: Annotated[
381
385
  Any,
382
386
  Field(
@@ -385,9 +389,7 @@ class QueryEntry(BaseModel):
385
389
  ),
386
390
  ] = None
387
391
 
388
- expiry: Annotated[
389
- datetime | None, Field(description="When should this record expire?", default_factory=generate_expiry)
390
- ]
392
+ expiry: datetime | None = Field(description="When should this record expire?", default_factory=generate_expiry)
391
393
 
392
394
  model_config = ConfigDict(validate_assignment=True)
393
395
 
@@ -18,7 +18,6 @@ from pydantic_core import PydanticSerializationError
18
18
 
19
19
  from clue.cache import Cache
20
20
  from clue.common.exceptions import (
21
- AuthenticationException,
22
21
  ClueException,
23
22
  ClueValueError,
24
23
  InvalidDataException,
@@ -515,34 +514,6 @@ class CluePlugin:
515
514
 
516
515
  self.logger.debug("Initialization complete!")
517
516
 
518
- def __check_actions(self) -> list[Action] | None:
519
- """Validate token and retrieve dynamic actions if setup_actions is configured.
520
-
521
- This method handles token validation when required and calls the setup_actions
522
- function to get a potentially user-specific or dynamically generated list of actions.
523
-
524
- Returns:
525
- list[Action] | None: List of actions if setup_actions is configured, None otherwise
526
-
527
- Raises:
528
- AuthenticationException: If token validation fails
529
- """
530
- if self.setup_actions:
531
- # Validate token if token validation is configured
532
- if self.validate_token:
533
- token, error = self.validate_token()
534
-
535
- if error:
536
- self.logger.error("Error on token validation: %s", error)
537
- raise AuthenticationException(error)
538
- else:
539
- token = None
540
-
541
- # Call user-defined setup_actions with base actions and validated token
542
- return self.setup_actions(self.actions or [], token)
543
-
544
- return None
545
-
546
517
  def __init_apm(self):
547
518
  """Initialize Application Performance Monitoring (APM) using Elastic APM.
548
519
 
@@ -790,6 +761,96 @@ class CluePlugin:
790
761
  status_code,
791
762
  )
792
763
 
764
+ def _resolve_token(self: Self, context: str | None = None) -> tuple[str | None, Response | None]:
765
+ """Call the plugin's validate_token function and return the token, or an error response.
766
+
767
+ Args:
768
+ context: Optional name of the calling endpoint (e.g. "action", "fetcher") used
769
+ in the warning message when no validator is configured.
770
+
771
+ Returns:
772
+ tuple[str | None, Response | None]: ``(token, None)`` on success,
773
+ or ``(None, error_response)`` when validation fails or raises.
774
+ """
775
+ if not self.validate_token:
776
+ if context:
777
+ self.logger.warning(
778
+ "No token validator provided. The access token will not be provided to the %s.", context
779
+ )
780
+ else:
781
+ self.logger.warning("No token validator provided")
782
+ return None, None
783
+
784
+ self.logger.debug("Executing plugin-provided token validator")
785
+ try:
786
+ token, error = self.validate_token()
787
+ except Exception:
788
+ self.logger.exception("Catastrophic error in validate_token")
789
+ return None, self.make_api_response(None, "An internal error has occurred", 500)
790
+
791
+ if error:
792
+ return None, self.make_api_response(None, f"Error on token validation: {error}", status_code=401)
793
+
794
+ self.logger.debug("Token is valid")
795
+ return token, None
796
+
797
+ def _call_plugin_func(self: Self, func: Callable, *args: Any, **kwargs: Any) -> tuple[Any, Response | None]:
798
+ """Call a plugin-provided function with standard enrichment exception handling.
799
+
800
+ Maps the well-known Clue exceptions to their corresponding HTTP responses and
801
+ catches any other exception as a 500. Intended for ``enrich`` and
802
+ ``alternate_bulk_lookup`` calls.
803
+
804
+ Args:
805
+ func: The plugin function to call.
806
+ *args: Positional arguments forwarded to the function.
807
+ **kwargs: Keyword arguments forwarded to the function.
808
+
809
+ Returns:
810
+ tuple[Any, Response | None]: ``(result, None)`` on success,
811
+ or ``(None, error_response)`` when the function raises.
812
+ """
813
+ try:
814
+ return func(*args, **kwargs), None
815
+ except InvalidDataException as e:
816
+ return None, self.make_api_response(None, e.message, 400)
817
+ except NotFoundException:
818
+ return None, self.make_api_response([], "", 404)
819
+ except TimeoutException as e:
820
+ return None, self.make_api_response(None, e.message or "Request timed out", 408)
821
+ except UnprocessableException as e:
822
+ return None, self.make_api_response(None, e.message, 422)
823
+ except Exception:
824
+ self.logger.exception("Unknown internal exception")
825
+ return None, self.make_api_response(None, "An internal error has occurred", 500)
826
+
827
+ def _get_actions(self: Self) -> tuple[list[Action], Response | None]:
828
+ """Retrieve the action list, running setup_actions if configured.
829
+
830
+ If setup_actions is configured, validates the token first and then calls it
831
+ to get a potentially user-specific or dynamic list of actions.
832
+
833
+ Returns:
834
+ tuple[list[Action], Response | None]: ``(actions, None)`` on success,
835
+ or ``([], error_response)`` if token validation or setup_actions fails.
836
+ """
837
+ try:
838
+ if self.setup_actions:
839
+ token, error_response = self._resolve_token(context="action setup")
840
+ if error_response:
841
+ return [], error_response
842
+
843
+ # Call user-defined setup_actions with base actions and validated token
844
+ actions = self.setup_actions(self.actions or [], token)
845
+ if actions is None:
846
+ return self.actions or [], None
847
+ return actions, None
848
+
849
+ return self.actions or [], None
850
+ except Exception:
851
+ self.logger.exception("Exception on setup actions:")
852
+ return [], self.make_api_response({}, err="Error on action setup.", status_code=500)
853
+
793
854
  def get_type_names(self: Self) -> Response:
794
855
  """Return the list of supported selector types with their classifications.
795
856
 
@@ -859,16 +920,10 @@ class CluePlugin:
859
920
  422,
860
921
  )
861
922
 
862
- token: str | None = None
863
- if self.validate_token:
864
- try:
865
- token, error = self.validate_token()
866
- except Exception as e:
867
- self.logger.exception("Catastrophic error in validate_token")
868
- return self.make_api_response(None, f"Something went wrong: {e}", 500)
923
+ token, error_response = self._resolve_token()
924
+ if error_response:
925
+ return error_response
869
926
 
870
- if error:
871
- return self.make_api_response(None, f"Error on token validation: {error}", status_code=401)
872
927
  try:
873
928
  if self.cache and params.use_cache:
874
929
  if result := self.cache.get(type_name, value, params):
@@ -883,22 +938,12 @@ class CluePlugin:
883
938
  except Exception:
884
939
  self.logger.exception("Unknown internal exception on cache check, continuing to standard enrichment")
885
940
 
886
- try:
887
- results = self.enrich(type_name, value, params, token)
941
+ results, error_response = self._call_plugin_func(self.enrich, type_name, value, params, token)
942
+ if error_response:
943
+ return error_response
888
944
 
889
- if not isinstance(results, list):
890
- results = [results]
891
- except InvalidDataException as e:
892
- return self.make_api_response(None, e.message, 400)
893
- except NotFoundException:
894
- return self.make_api_response([], "", 404)
895
- except TimeoutException as e:
896
- return self.make_api_response(None, e.message or "Request timed out", 408)
897
- except UnprocessableException as e:
898
- return self.make_api_response(None, e.message, 422)
899
- except Exception as e:
900
- self.logger.exception("Unknown internal exception")
901
- return self.make_api_response(None, f"Something went wrong when enriching: {e}", 500)
945
+ if not isinstance(results, list):
946
+ results = [results]
902
947
 
903
948
  try:
904
949
  serialized_reult = TypeAdapter(list[QueryEntry]).dump_python(results, mode="json", exclude_none=True)
@@ -998,22 +1043,9 @@ class CluePlugin:
998
1043
 
999
1044
  remaining_items.append(entry)
1000
1045
 
1001
- token: str | None = None
1002
- if self.validate_token:
1003
- self.logger.debug("Executing plugin-provided token validator")
1004
-
1005
- try:
1006
- token, error = self.validate_token()
1007
- except Exception as e:
1008
- self.logger.exception("Catastrophic error in validate_token")
1009
- return self.make_api_response(None, f"Something went wrong: {e}", 500)
1010
-
1011
- if error:
1012
- return self.make_api_response(None, f"Error on token validation: {error}", status_code=401)
1013
-
1014
- self.logger.debug("Token is valid")
1015
- else:
1016
- self.logger.warning("No token validator provided")
1046
+ token, error_response = self._resolve_token()
1047
+ if error_response:
1048
+ return error_response
1017
1049
 
1018
1050
  # All results were cached
1019
1051
  if len(remaining_items) == 0:
@@ -1022,23 +1054,22 @@ class CluePlugin:
1022
1054
  elif self.alternate_bulk_lookup:
1023
1055
  self.logger.debug("Executing plugin-provided alternate bulk lookup script")
1024
1056
 
1025
- try:
1026
- alternate_results = self.alternate_bulk_lookup(remaining_items, params, token)
1057
+ alternate_results, error_response = self._call_plugin_func(
1058
+ self.alternate_bulk_lookup, remaining_items, params, token
1059
+ )
1060
+ if error_response:
1061
+ return error_response
1027
1062
 
1028
- for _type, _values in alternate_results.items():
1029
- for _value, _result in _values.items():
1030
- bulk_result[_type][_value] = _result
1031
- except InvalidDataException as e:
1032
- return self.make_api_response(None, e.message, 400)
1033
- except NotFoundException:
1034
- return self.make_api_response([], "", 404)
1035
- except TimeoutException as e:
1036
- return self.make_api_response(None, e.message or "Request timed out", 408)
1037
- except UnprocessableException as e:
1038
- return self.make_api_response(None, e.message, 422)
1039
- except Exception as e:
1040
- self.logger.exception("Unknown internal exception")
1041
- return self.make_api_response(None, f"Something went wrong when enriching: {e}", 500)
1063
+ if not isinstance(alternate_results, dict):
1064
+ self.logger.error(
1065
+ "alternate_bulk_lookup returned unexpected type: %s",
1066
+ type(alternate_results).__name__,
1067
+ )
1068
+ return self.make_api_response(None, "Something went wrong when enriching: unexpected result type", 500)
1069
+
1070
+ for _type, _values in alternate_results.items():
1071
+ for _value, _result in _values.items():
1072
+ bulk_result[_type][_value] = _result
1042
1073
 
1043
1074
  if self.cache and len(remaining_items) > 0:
1044
1075
  self.logger.info("Caching results for %s selectors", len(remaining_items))
@@ -1089,20 +1120,15 @@ class CluePlugin:
1089
1120
  ...
1090
1121
  }
1091
1122
  """
1092
- try:
1093
- actions = self.__check_actions()
1094
- except Exception:
1095
- self.logger.exception("Exception on setup actions:")
1096
-
1097
- return self.make_api_response({}, err="Error on action setup.", status_code=500)
1098
-
1099
- if actions is None:
1100
- actions = self.actions or []
1123
+ actions, error_response = self._get_actions()
1124
+ if error_response:
1125
+ return error_response
1101
1126
 
1102
- if not self.validate_token or not (token := self.validate_token()[0]):
1103
- self.logger.debug("Returning %s actions for unknown user", len(actions))
1104
- else:
1127
+ token, _ = self._resolve_token()
1128
+ if token:
1105
1129
  self.logger.debug("Returning %s actions for user %s", len(actions), get_username(token))
1130
+ else:
1131
+ self.logger.debug("Returning %s actions for unknown user", len(actions))
1106
1132
 
1107
1133
  results: dict[str, dict[str, Any]] = {}
1108
1134
  for action in actions:
@@ -1130,32 +1156,17 @@ class CluePlugin:
1130
1156
  if not self.run_action:
1131
1157
  return self.make_api_response({}, err=f"{self.app_name} does not support any actions.", status_code=400)
1132
1158
 
1133
- try:
1134
- actions = self.__check_actions()
1135
- except Exception:
1136
- self.logger.exception("Exception on setup actions:")
1137
-
1138
- return self.make_api_response({}, err="Error on action setup.", status_code=500)
1139
-
1140
- if actions is None:
1141
- actions = self.actions or []
1159
+ actions, error_response = self._get_actions()
1160
+ if error_response:
1161
+ return error_response
1142
1162
 
1143
1163
  action_to_run = next((action for action in actions if action.id == action_id), None)
1144
1164
  if not action_to_run:
1145
1165
  return self.make_api_response({}, err="Action does not exist", status_code=404)
1146
1166
 
1147
- token: str | None = None
1148
- if self.validate_token:
1149
- self.logger.debug("Executing plugin-provided token validator")
1150
-
1151
- token, error = self.validate_token()
1152
-
1153
- if error:
1154
- return self.make_api_response(None, f"Error on token validation: {error}", status_code=401)
1155
-
1156
- self.logger.debug("Token is valid")
1157
- else:
1158
- self.logger.warning("No token validation provided. The access token will not be provided to the action.")
1167
+ token, error_response = self._resolve_token(context="action")
1168
+ if error_response:
1169
+ return error_response
1159
1170
 
1160
1171
  # Extract the parameter type from the action definition for validation
1161
1172
  param_type: Any = action_to_run.model_fields["params"].annotation or Any
@@ -1225,32 +1236,17 @@ class CluePlugin:
1225
1236
  {}, err=f"{self.app_name} does not support the get action status functions.", status_code=400
1226
1237
  )
1227
1238
 
1228
- try:
1229
- actions = self.__check_actions()
1230
- except Exception:
1231
- self.logger.exception("Exception on setup actions:")
1232
-
1233
- return self.make_api_response({}, err="Error on action setup.", status_code=500)
1234
-
1235
- if actions is None:
1236
- actions = self.actions or []
1239
+ actions, error_response = self._get_actions()
1240
+ if error_response:
1241
+ return error_response
1237
1242
 
1238
1243
  action_to_check = next((action for action in actions if action.id == action_id), None)
1239
1244
  if not action_to_check:
1240
1245
  return self.make_api_response({}, err="Action does not exist", status_code=404)
1241
1246
 
1242
- token: str | None = None
1243
- if self.validate_token:
1244
- self.logger.debug("Executing plugin-provided token validator")
1245
-
1246
- token, error = self.validate_token()
1247
-
1248
- if error:
1249
- return self.make_api_response(None, f"Error on token validation: {error}", status_code=401)
1250
-
1251
- self.logger.debug("Token is valid")
1252
- else:
1253
- self.logger.warning("No token validation provided. The access token will not be provided to the action.")
1247
+ token, error_response = self._resolve_token(context="action")
1248
+ if error_response:
1249
+ return error_response
1254
1250
 
1255
1251
  try:
1256
1252
  # Validate request body against the action's parameter schema
@@ -1338,18 +1334,9 @@ class CluePlugin:
1338
1334
  if not fetcher_to_run:
1339
1335
  return self.make_api_response({}, err=f"Fetcher {fetcher_id} does not exist", status_code=404)
1340
1336
 
1341
- token: str | None = None
1342
- if self.validate_token:
1343
- self.logger.debug("Executing plugin-provided token validator")
1344
-
1345
- token, error = self.validate_token()
1346
-
1347
- if error:
1348
- return self.make_api_response(None, f"Error on token validation: {error}", status_code=401)
1349
-
1350
- self.logger.debug("Token is valid")
1351
- else:
1352
- self.logger.warning("No token validation provided. The access token will not be provided to the fetcher.")
1337
+ token, error_response = self._resolve_token(context="fetcher")
1338
+ if error_response:
1339
+ return error_response
1353
1340
 
1354
1341
  status_code = 200
1355
1342
  try:
@@ -1431,18 +1418,9 @@ class CluePlugin:
1431
1418
  if not fetcher:
1432
1419
  return self.make_api_response({}, err=f"Fetcher {fetcher_id} does not exist", status_code=404)
1433
1420
 
1434
- token: str | None = None
1435
- if self.validate_token:
1436
- self.logger.debug("Executing plugin-provided token validator")
1437
-
1438
- token, error = self.validate_token()
1439
-
1440
- if error:
1441
- return self.make_api_response(None, f"Error on token validation: {error}", status_code=401)
1442
-
1443
- self.logger.debug("Token is valid")
1444
- else:
1445
- self.logger.warning("No token validation provided. The access token will not be provided to the fetcher.")
1421
+ token, error_response = self._resolve_token(context="fetcher")
1422
+ if error_response:
1423
+ return error_response
1446
1424
 
1447
1425
  status_code = 200
1448
1426
  try:
@@ -141,7 +141,7 @@ log_cli_level = "WARN"
141
141
  [tool.poetry]
142
142
  package-mode = true
143
143
  name = "clue-api"
144
- version = "1.5.0.dev262"
144
+ version = "1.5.0.dev271"
145
145
  description = "Clue distributed enrichment service"
146
146
  authors = ["Canadian Centre for Cyber Security <contact@cyber.gc.ca>"]
147
147
  license = "MIT"
File without changes