From a196323fa17c7ef2928455469330e950d49b2a62 Mon Sep 17 00:00:00 2001 From: hblankenship Date: Fri, 15 Nov 2024 09:25:26 -0600 Subject: [PATCH 1/4] put and patch batch metada --- dojo/api_v2/serializers.py | 45 +++++++++++++++++++++++++++++++ dojo/api_v2/views.py | 54 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/dojo/api_v2/serializers.py b/dojo/api_v2/serializers.py index c9f73c97ce3..de0e6a49dee 100644 --- a/dojo/api_v2/serializers.py +++ b/dojo/api_v2/serializers.py @@ -417,6 +417,51 @@ class Meta: fields = "__all__" +class MetadataSerializer(serializers.Serializer): + name = serializers.CharField(max_length=120) + value = serializers.CharField(max_length=300) + + +class MetaMainSerializer(serializers.Serializer): + id = serializers.IntegerField(read_only=True) + + product = serializers.PrimaryKeyRelatedField( + queryset=Product.objects.all(), + required=False, + default=None, + allow_null=True, + ) + endpoint = serializers.PrimaryKeyRelatedField( + queryset=Endpoint.objects.all(), + required=False, + default=None, + allow_null=True, + ) + finding = serializers.PrimaryKeyRelatedField( + queryset=Finding.objects.all(), + required=False, + default=None, + allow_null=True, + ) + metadata = MetadataSerializer(many=True) + + def validate(self, data): + product_id = data.get("product", None) + endpoint_id = data.get("endpoint", None) + finding_id = data.get("finding", None) + metadata = data.get("metadata") + + for item in metadata: + # this will only verify that one and only one of product, endpoint, or finding is passed... + DojoMeta(product=product_id, + endpoint=endpoint_id, + finding=finding_id, + name=item.get("name"), + value=item.get("value")).clean() + + return data + + class ProductMetaSerializer(serializers.ModelSerializer): class Meta: model = DojoMeta diff --git a/dojo/api_v2/views.py b/dojo/api_v2/views.py index e6dd30dab4d..2ba5ea9d966 100644 --- a/dojo/api_v2/views.py +++ b/dojo/api_v2/views.py @@ -1650,6 +1650,60 @@ class DojoMetaViewSet( def get_queryset(self): return get_authorized_dojo_meta(Permissions.Product_View) + @extend_schema( + methods=["post", "patch"], + request=serializers.MetaMainSerializer, + responses={status.HTTP_200_OK: serializers.MetaMainSerializer}, + filters=False, + ) + @action( + detail=False, methods=["post", "patch"], pagination_class=None, + ) + def batch(self, request, pk=None): + serialized_data = serializers.MetaMainSerializer(data=request.data) + if serialized_data.is_valid(raise_exception=True): + if request.method == "POST": + self.process_post(request.data) + if request.method == "PATCH": + self.process_patch(request.data) + + return Response(status=status.HTTP_201_CREATED, data=serialized_data.data) + + def process_post(self: object, data: dict): + product = Product.objects.filter(id=data.get("product")).first() + finding = Finding.objects.filter(id=data.get("finding")).first() + endpoint = Finding.objects.filter(id=data.get("endpoint")).first() + metalist = data.get("metadata") + for metadata in metalist: + try: + DojoMeta.objects.create( + product=product, + finding=finding, + endpoint=endpoint, + name=metadata.get("name"), + value=metadata.get("value"), + ) + except (IntegrityError) as ex: # this should not happen as the data was validated in the batch call + raise ValidationError(str(ex)) + + def process_patch(self: object, data: dict): + product = Product.objects.filter(id=data.get("product")).first() + finding = Finding.objects.filter(id=data.get("finding")).first() + endpoint = Finding.objects.filter(id=data.get("endpoint")).first() + metalist = data.get("metadata") + for metadata in metalist: + dojometa = DojoMeta.objects.filter(product=product, finding=finding, endpoint=endpoint, name=metadata.get("name")) + if dojometa: + try: + dojometa.update( + name=metadata.get("name"), + value=metadata.get("value"), + ) + except (IntegrityError) as ex: + raise ValidationError(str(ex)) + else: + msg = f"Metadata {metadata.get('name')} not found for object." + raise ValidationError(msg) @extend_schema_view(**schema_with_prefetch()) class ProductViewSet( From c4930085e365cf1d2bb21bfd19af38b7d626d0c8 Mon Sep 17 00:00:00 2001 From: hblankenship Date: Fri, 15 Nov 2024 09:31:26 -0600 Subject: [PATCH 2/4] blank lines are ruff --- dojo/api_v2/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dojo/api_v2/views.py b/dojo/api_v2/views.py index 2ba5ea9d966..1a28a3d2d61 100644 --- a/dojo/api_v2/views.py +++ b/dojo/api_v2/views.py @@ -1705,6 +1705,7 @@ def process_patch(self: object, data: dict): msg = f"Metadata {metadata.get('name')} not found for object." raise ValidationError(msg) + @extend_schema_view(**schema_with_prefetch()) class ProductViewSet( prefetch.PrefetchListMixin, From db4db4b4acc18bf477e5675485e855c08f3783e5 Mon Sep 17 00:00:00 2001 From: Harold Blankenship <36673698+hblankenship@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:35:26 -0600 Subject: [PATCH 3/4] Update dojo/api_v2/views.py Oof. Good eye Co-authored-by: Charles Neill <1749665+cneill@users.noreply.github.com> --- dojo/api_v2/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dojo/api_v2/views.py b/dojo/api_v2/views.py index 1a28a3d2d61..00a6da1b556 100644 --- a/dojo/api_v2/views.py +++ b/dojo/api_v2/views.py @@ -1672,7 +1672,7 @@ def batch(self, request, pk=None): def process_post(self: object, data: dict): product = Product.objects.filter(id=data.get("product")).first() finding = Finding.objects.filter(id=data.get("finding")).first() - endpoint = Finding.objects.filter(id=data.get("endpoint")).first() + endpoint = Endpoint.objects.filter(id=data.get("endpoint")).first() metalist = data.get("metadata") for metadata in metalist: try: From f2e86b091419a482877370a8af495f61907cb396 Mon Sep 17 00:00:00 2001 From: Harold Blankenship <36673698+hblankenship@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:35:43 -0600 Subject: [PATCH 4/4] Update dojo/api_v2/views.py Same Co-authored-by: Charles Neill <1749665+cneill@users.noreply.github.com> --- dojo/api_v2/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dojo/api_v2/views.py b/dojo/api_v2/views.py index 00a6da1b556..6caea557471 100644 --- a/dojo/api_v2/views.py +++ b/dojo/api_v2/views.py @@ -1689,7 +1689,7 @@ def process_post(self: object, data: dict): def process_patch(self: object, data: dict): product = Product.objects.filter(id=data.get("product")).first() finding = Finding.objects.filter(id=data.get("finding")).first() - endpoint = Finding.objects.filter(id=data.get("endpoint")).first() + endpoint = Endpoint.objects.filter(id=data.get("endpoint")).first() metalist = data.get("metadata") for metadata in metalist: dojometa = DojoMeta.objects.filter(product=product, finding=finding, endpoint=endpoint, name=metadata.get("name"))