Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 121 additions & 20 deletions AGENTS.md

Large diffs are not rendered by default.

5 changes: 1 addition & 4 deletions dojo/api_v2/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -721,10 +721,7 @@ class Meta:
fields = ["path"]


class ProductTypeSerializer(serializers.ModelSerializer):
class Meta:
model = Product_Type
fields = "__all__"
from dojo.product_type.api.serializer import ProductTypeSerializer # noqa: E402


class EngagementSerializer(serializers.ModelSerializer):
Expand Down
83 changes: 0 additions & 83 deletions dojo/api_v2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@
Notes,
Product,
Product_API_Scan_Configuration,
Product_Type,
Regulation,
Risk_Acceptance,
SLA_Configuration,
Expand All @@ -128,9 +127,6 @@
get_authorized_product_api_scan_configurations,
get_authorized_products,
)
from dojo.product_type.queries import (
get_authorized_product_types,
)
from dojo.query_utils import build_count_subquery
from dojo.reports.views import (
prefetch_related_findings_for_report,
Expand Down Expand Up @@ -1783,85 +1779,6 @@ def generate_report(self, request, pk=None):
return Response(report.data)


# Authorization: object-based
@extend_schema_view(**schema_with_prefetch())
# Authorization: object-based
@extend_schema_view(**schema_with_prefetch())
class ProductTypeViewSet(
PrefetchDojoModelViewSet,
):
serializer_class = serializers.ProductTypeSerializer
queryset = Product_Type.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_fields = [
"id",
"name",
"critical_product",
"key_product",
"created",
"updated",
]
permission_classes = (
IsAuthenticated,
permissions.UserHasProductTypePermission,
)

def get_queryset(self):
return get_authorized_product_types(
"view",
).distinct()

def destroy(self, request, *args, **kwargs):
instance = self.get_object()
if get_setting("ASYNC_OBJECT_DELETE"):
async_del = async_delete()
async_del.delete(instance)
else:
instance.delete()
return Response(status=status.HTTP_204_NO_CONTENT)

@extend_schema(
request=serializers.ReportGenerateOptionSerializer,
responses={status.HTTP_200_OK: serializers.ReportGenerateSerializer},
)
@action(
detail=True, methods=["post"],
# IsAuthenticated only: report generation requires View permission,
# enforced by the permission-filtered get_queryset(). The viewset's
# permission_classes would check Edit (POST), which is too restrictive.
permission_classes=[IsAuthenticated],
)
def generate_report(self, request, pk=None):
product_type = self.get_object()

options = {}
# prepare post data
report_options = serializers.ReportGenerateOptionSerializer(
data=request.data,
)
if report_options.is_valid():
options["include_finding_notes"] = report_options.validated_data[
"include_finding_notes"
]
options["include_finding_images"] = report_options.validated_data[
"include_finding_images"
]
options[
"include_executive_summary"
] = report_options.validated_data["include_executive_summary"]
options[
"include_table_of_contents"
] = report_options.validated_data["include_table_of_contents"]
else:
return Response(
report_options.errors, status=status.HTTP_400_BAD_REQUEST,
)

data = report_generate(request, product_type, options)
report = serializers.ReportGenerateSerializer(data)
return Response(report.data)


# Authorization: authenticated, configuration
class DevelopmentEnvironmentViewSet(
DojoModelViewSet,
Expand Down
16 changes: 0 additions & 16 deletions dojo/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -3780,22 +3780,6 @@ class Meta:
# re-exported at the bottom of this module for backward compatibility.


class ProductTypeFilter(DojoFilter):
name = CharFilter(lookup_expr="icontains")

o = OrderingFilter(
# tuple-mapping retains order
fields=(
("name", "name"),
),
)

class Meta:
model = Product_Type
exclude = []
include = ("name",)


class TestTypeFilter(DojoFilter):
name = CharFilter(lookup_expr="icontains")

Expand Down
39 changes: 1 addition & 38 deletions dojo/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,44 +243,7 @@ def value_from_datadict(self, data, files, name):
return data.get(name, None)


class Product_TypeForm(forms.ModelForm):
description = forms.CharField(widget=forms.Textarea(attrs={}),
required=False)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["critical_product"].label = labels.ORG_CRITICAL_PRODUCT_LABEL
self.fields["key_product"].label = labels.ORG_KEY_PRODUCT_LABEL

class Meta:
model = Product_Type
fields = ["name", "description", "critical_product", "key_product"]


class Delete_Product_TypeForm(forms.ModelForm):
id = forms.IntegerField(required=True,
widget=forms.widgets.HiddenInput())

class Meta:
model = Product_Type
fields = ["id"]


class Add_Product_Type_AuthorizedUsersForm(forms.Form):
users = forms.ModelMultipleChoiceField(
queryset=Dojo_User.objects.none(), required=True, label="Users",
)

def __init__(self, *args, product_type=None, **kwargs):
super().__init__(*args, **kwargs)
self.product_type = product_type
current = product_type.authorized_users.values_list("pk", flat=True)
self.fields["users"].queryset = (
Dojo_User.objects.filter(is_active=True)
.exclude(is_superuser=True)
.exclude(pk__in=current)
.order_by("first_name", "last_name")
)
from dojo.product_type.ui.forms import Add_Product_Type_AuthorizedUsersForm, Delete_Product_TypeForm, Product_TypeForm # noqa: E402, F401, I001


class Test_TypeForm(forms.ModelForm):
Expand Down
71 changes: 1 addition & 70 deletions dojo/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -758,75 +758,7 @@ def clean(self):
raise ValidationError(msg)


class Product_Type(BaseModel):

"""
Product types represent the top level model, these can be business unit divisions, different offices or locations, development teams, or any other logical way of distinguishing "types" of products.
`
Examples:
* IAM Team
* Internal / 3rd Party
* Main company / Acquisition
* San Francisco / New York offices
"""

name = models.CharField(max_length=255, unique=True)
description = models.CharField(max_length=4000, null=True, blank=True)
critical_product = models.BooleanField(default=False)
key_product = models.BooleanField(default=False)
authorized_users = models.ManyToManyField(Dojo_User, related_name="authorized_product_types", blank=True)

class Meta:
ordering = ("name",)

def __str__(self):
return self.name

def get_absolute_url(self):
return reverse("product_type", args=[str(self.id)])

def get_breadcrumbs(self):
return [{"title": str(self),
"url": reverse("edit_product_type", args=(self.id,))}]

@cached_property
def critical_present(self):
c_findings = Finding.objects.filter(
test__engagement__product__prod_type=self, severity="Critical")
if c_findings.count() > 0:
return True
return None

@cached_property
def high_present(self):
c_findings = Finding.objects.filter(
test__engagement__product__prod_type=self, severity="High")
if c_findings.count() > 0:
return True
return None

@cached_property
def calc_health(self):
h_findings = Finding.objects.filter(
test__engagement__product__prod_type=self, severity="High")
c_findings = Finding.objects.filter(
test__engagement__product__prod_type=self, severity="Critical")
health = 100
if c_findings.count() > 0:
health = 40
health -= ((c_findings.count() - 1) * 5)
if h_findings.count() > 0:
if health == 100:
health = 60
health -= ((h_findings.count() - 1) * 2)
if health < 5:
return 5
return health

# only used by bulk risk acceptance api
@property
def unaccepted_open_findings(self):
return Finding.objects.filter(risk_accepted=False, active=True, duplicate=False, test__engagement__product__prod_type=self)
from dojo.product_type.models import Product_Type # noqa: E402 -- re-export; mid-file as Product FK uses it below


class Product_Line(models.Model):
Expand Down Expand Up @@ -4432,7 +4364,6 @@ def __str__(self):
admin.site.register(Endpoint_Status)
admin.site.register(Endpoint)
admin.site.register(Product)
admin.site.register(Product_Type)
admin.site.register(UserContactInfo)
admin.site.register(Notes)
admin.site.register(Note_Type)
Expand Down
2 changes: 1 addition & 1 deletion dojo/organization/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from django.urls import re_path

from dojo.product import views as product_views
from dojo.product_type import views
from dojo.product_type.ui import views
from dojo.utils import redirect_view

# TODO: remove the else: branch once v3 migration is complete
Expand Down
1 change: 1 addition & 0 deletions dojo/product_type/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import dojo.product_type.admin # noqa: F401
9 changes: 9 additions & 0 deletions dojo/product_type/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.contrib import admin

from dojo.product_type.models import Product_Type


@admin.register(Product_Type)
class Product_TypeAdmin(admin.ModelAdmin):

"""Admin support for the Product_Type model."""
1 change: 1 addition & 0 deletions dojo/product_type/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
path = "product_types" # noqa: RUF067
9 changes: 9 additions & 0 deletions dojo/product_type/api/serializer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from rest_framework import serializers

from dojo.product_type.models import Product_Type


class ProductTypeSerializer(serializers.ModelSerializer):
class Meta:
model = Product_Type
fields = "__all__"
6 changes: 6 additions & 0 deletions dojo/product_type/api/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from dojo.product_type.api.views import ProductTypeViewSet


def add_product_type_urls(router):
router.register("product_types", ProductTypeViewSet, basename="product_type")
return router
Loading
Loading