Skip to content
Merged
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
3 changes: 2 additions & 1 deletion .dryrunsecurity.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ sensitiveCodepaths:
- 'dojo/group/*.py'
- 'dojo/importers/*.py'
- 'dojo/importers/**/*.py'
- 'dojo/jira_link/*.py'
- 'dojo/jira/*.py'
- 'dojo/jira/**/*.py'
- 'dojo/metrics/*.py'
- 'dojo/note_type/*.py'
- 'dojo/notes/*.py'
Expand Down
101 changes: 15 additions & 86 deletions dojo/api_v2/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
from rest_framework.fields import DictField, MultipleChoiceField

import dojo.finding.helper as finding_helper
import dojo.jira_link.helper as jira_helper
import dojo.risk_acceptance.helper as ra_helper
from dojo.authorization.authorization import user_has_permission
from dojo.authorization.roles_permissions import Permissions
Expand All @@ -40,6 +39,7 @@
from dojo.importers.base_importer import BaseImporter
from dojo.importers.default_importer import DefaultImporter
from dojo.importers.default_reimporter import DefaultReImporter
from dojo.jira import services as jira_services
from dojo.location.models import Location, LocationFindingReference
from dojo.models import (
DEFAULT_NOTIFICATION,
Expand Down Expand Up @@ -75,9 +75,6 @@
Finding_Template,
General_Survey,
Global_Role,
JIRA_Instance,
JIRA_Issue,
JIRA_Project,
Language_Type,
Languages,
Network_Locations,
Expand Down Expand Up @@ -1376,79 +1373,11 @@ class Meta:
fields = "__all__"


class JIRAIssueSerializer(serializers.ModelSerializer):
url = serializers.SerializerMethodField(read_only=True)

class Meta:
model = JIRA_Issue
fields = "__all__"

def get_url(self, obj) -> str:
return jira_helper.get_jira_issue_url(obj)

def validate(self, data):
if self.context["request"].method == "PATCH":
engagement = data.get("engagement", self.instance.engagement)
finding = data.get("finding", self.instance.finding)
finding_group = data.get(
"finding_group", self.instance.finding_group,
)
else:
engagement = data.get("engagement", None)
finding = data.get("finding", None)
finding_group = data.get("finding_group", None)

if (
(engagement and not finding and not finding_group)
or (finding and not engagement and not finding_group)
or (finding_group and not engagement and not finding)
):
pass
else:
msg = "Either engagement or finding or finding_group has to be set."
raise serializers.ValidationError(msg)

if finding:
if (linked_finding := jira_helper.jira_already_linked(finding, data.get("jira_key"), data.get("jira_id"))) is not None:
msg = "JIRA issue " + data.get("jira_key") + " already linked to " + reverse("view_finding", args=(linked_finding.id,))
raise serializers.ValidationError(msg)

return data


class JIRAInstanceSerializer(serializers.ModelSerializer):
class Meta:
model = JIRA_Instance
fields = "__all__"
extra_kwargs = {
"password": {"write_only": True},
}


class JIRAProjectSerializer(serializers.ModelSerializer):
class Meta:
model = JIRA_Project
fields = "__all__"

def validate(self, data):
if self.context["request"].method == "PATCH":
engagement = data.get("engagement", self.instance.engagement)
product = data.get("product", self.instance.product)
else:
engagement = data.get("engagement", None)
product = data.get("product", None)

if (engagement and product) or (not engagement and not product):
msg = "Either engagement or product has to be set."
raise serializers.ValidationError(msg)

if "custom_fields" in data and isinstance(data["custom_fields"], str):
try:
data["custom_fields"] = json.loads(data["custom_fields"])
except json.JSONDecodeError as e:
raise serializers.ValidationError({"custom_fields": f"Invalid JSON: {e}"}) from e

return data
from dojo.jira.api.serializers import ( # noqa: E402, F401 backward compat
JIRAInstanceSerializer,
JIRAIssueSerializer,
JIRAProjectSerializer,
)


class SonarqubeIssueSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -1770,7 +1699,7 @@ def get_test(self, obj):

@extend_schema_field(JIRAIssueSerializer)
def get_jira(self, obj):
issue = jira_helper.get_jira_issue(obj)
issue = jira_services.get_issue(obj)
if issue is None:
return None
return JIRAIssueSerializer(read_only=True).to_representation(issue)
Expand Down Expand Up @@ -1844,11 +1773,11 @@ def get_accepted_risks(self, obj):

@extend_schema_field(serializers.DateTimeField())
def get_jira_creation(self, obj):
return jira_helper.get_jira_creation(obj)
return jira_services.get_creation(obj)

@extend_schema_field(serializers.DateTimeField())
def get_jira_change(self, obj):
return jira_helper.get_jira_change(obj)
return jira_services.get_change(obj)

@extend_schema_field(FindingRelatedFieldsSerializer)
def get_related_fields(self, obj):
Expand Down Expand Up @@ -1924,9 +1853,9 @@ def update(self, instance, validated_data):
for location_ref in locations:
location_ref.location.associate_with_finding(instance)

if push_to_jira or finding_helper.is_keep_in_sync_with_jira(instance):
if push_to_jira or jira_services.is_keep_in_sync(instance):
# Push synchronously so that we can see jira errors in real time
success, message = jira_helper.push_to_jira(instance, sync=True)
success, message = jira_services.push(instance, sync=True)
if not success:
raise serializers.ValidationError(message)

Expand Down Expand Up @@ -2083,7 +2012,7 @@ def create(self, validated_data):
save_vulnerability_ids(new_finding, parsed_vulnerability_ids)

if push_to_jira:
jira_helper.push_to_jira(new_finding)
jira_services.push(new_finding)

# Create a notification
create_notification(
Expand Down Expand Up @@ -3081,9 +3010,9 @@ class ReportGenerateSerializer(serializers.Serializer):
)


class EngagementUpdateJiraEpicSerializer(serializers.Serializer):
epic_name = serializers.CharField(required=False, max_length=200)
epic_priority = serializers.CharField(required=False, allow_null=True)
from dojo.jira.api.serializers import ( # noqa: E402, F401 backward compat
EngagementUpdateJiraEpicSerializer,
)


class TagSerializer(serializers.Serializer):
Expand Down
101 changes: 17 additions & 84 deletions dojo/api_v2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
from rest_framework.response import Response

import dojo.finding.helper as finding_helper
import dojo.jira_link.helper as jira_helper
from dojo.api_v2 import (
mixins as dojo_mixins,
)
Expand Down Expand Up @@ -88,10 +87,7 @@
get_authorized_groups,
)
from dojo.importers.auto_create_context import AutoCreateContextManager
from dojo.jira_link.queries import (
get_authorized_jira_issues,
get_authorized_jira_projects,
)
from dojo.jira import services as jira_services
from dojo.labels import get_labels
from dojo.models import (
Announcement,
Expand All @@ -117,9 +113,6 @@
Finding_Template,
General_Survey,
Global_Role,
JIRA_Instance,
JIRA_Issue,
JIRA_Project,
Language_Type,
Languages,
Network_Locations,
Expand Down Expand Up @@ -720,15 +713,18 @@ def download_file(self, request, file_id, pk=None):
def update_jira_epic(self, request, pk=None):
engagement = self.get_object()
try:

if engagement.has_jira_issue:
dojo_dispatch_task(jira_helper.update_epic, engagement.id, **request.data)
task = jira_services.get_epic_task("update_epic")
if task:
dojo_dispatch_task(task, engagement.id, **request.data)
response = Response(
{"info": "Jira Epic update query sent"},
status=status.HTTP_200_OK,
)
else:
dojo_dispatch_task(jira_helper.add_epic, engagement.id, **request.data)
task = jira_services.get_epic_task("add_epic")
if task:
dojo_dispatch_task(task, engagement.id, **request.data)
response = Response(
{"info": "Jira Epic create query sent"},
status=status.HTTP_200_OK,
Expand Down Expand Up @@ -1088,7 +1084,7 @@ class FindingViewSet(
def perform_update(self, serializer):
# IF JIRA is enabled and this product has a JIRA configuration
push_to_jira = serializer.validated_data.get("push_to_jira")
jira_project = jira_helper.get_jira_project(serializer.instance)
jira_project = jira_services.get_project(serializer.instance)
if get_system_setting("enable_jira") and jira_project:
push_to_jira = push_to_jira or jira_project.push_all_issues

Expand Down Expand Up @@ -1361,9 +1357,9 @@ def notes(self, request, pk=None):
)

if finding.has_jira_issue:
jira_helper.add_comment(finding, note)
jira_services.add_comment(finding, note)
elif finding.has_jira_group_issue:
jira_helper.add_comment(finding.finding_group, note)
jira_services.add_comment(finding.finding_group, note)

serialized_note = serializers.NoteSerializer(
{"author": author, "entry": entry, "private": private},
Expand Down Expand Up @@ -1769,74 +1765,11 @@ def metadata(self, request, pk=None):


# Authorization: configuration
class JiraInstanceViewSet(
DojoModelViewSet,
):
serializer_class = serializers.JIRAInstanceSerializer
queryset = JIRA_Instance.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_fields = ["id", "url"]
permission_classes = (permissions.UserHasConfigurationPermissionSuperuser,)

def get_queryset(self):
return JIRA_Instance.objects.all().order_by("id")


# Authorization: object-based
# @extend_schema_view(**schema_with_prefetch())
# Nested models with prefetch make the response schema too long for Swagger UI
class JiraIssuesViewSet(
PrefetchDojoModelViewSet,
):
serializer_class = serializers.JIRAIssueSerializer
queryset = JIRA_Issue.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_fields = [
"id",
"jira_id",
"jira_key",
"finding",
"engagement",
"finding_group",
]

permission_classes = (
IsAuthenticated,
permissions.UserHasJiraIssuePermission,
)

def get_queryset(self):
return get_authorized_jira_issues(Permissions.Product_View)


# Authorization: object-based
@extend_schema_view(**schema_with_prefetch())
class JiraProjectViewSet(
PrefetchDojoModelViewSet,
):
serializer_class = serializers.JIRAProjectSerializer
queryset = JIRA_Project.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_fields = [
"id",
"jira_instance",
"product",
"engagement",
"enabled",
"component",
"project_key",
"push_all_issues",
"enable_engagement_epic_mapping",
"push_notes",
]

permission_classes = (
IsAuthenticated,
permissions.UserHasJiraProductPermission,
)

def get_queryset(self):
return get_authorized_jira_projects(Permissions.Product_View)
from dojo.jira.api.views import ( # noqa: E402, F401 backward compat
JiraInstanceViewSet,
JiraIssuesViewSet,
JiraProjectViewSet,
)


# Authorization: superuser
Expand Down Expand Up @@ -2871,7 +2804,7 @@ def perform_create(self, serializer):
push_to_jira = serializer.validated_data.get("push_to_jira")
if get_system_setting("enable_jira"):
jira_driver = engagement or (product or None)
if jira_project := (jira_helper.get_jira_project(jira_driver) if jira_driver else None):
if jira_project := (jira_services.get_project(jira_driver) if jira_driver else None):
push_to_jira = push_to_jira or jira_project.push_all_issues

# Add pghistory context for audit trail (adds to existing middleware context).
Expand Down Expand Up @@ -3029,7 +2962,7 @@ def perform_create(self, serializer):
push_to_jira = serializer.validated_data.get("push_to_jira")
if get_system_setting("enable_jira"):
jira_driver = test or (engagement or (product or None))
if jira_project := (jira_helper.get_jira_project(jira_driver) if jira_driver else None):
if jira_project := (jira_services.get_project(jira_driver) if jira_driver else None):
push_to_jira = push_to_jira or jira_project.push_all_issues
logger.debug("push_to_jira: %s", push_to_jira)
# Add pghistory context for audit trail (adds to existing middleware context)
Expand Down
8 changes: 5 additions & 3 deletions dojo/engagement/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from django.db.models.signals import pre_save
from django.dispatch import receiver

import dojo.jira_link.helper as jira_helper
from dojo.celery_dispatch import dojo_dispatch_task
from dojo.jira import services as jira_services
from dojo.models import Engagement

logger = logging.getLogger(__name__)
Expand All @@ -16,8 +16,10 @@ def close_engagement(eng):
eng.status = "Completed"
eng.save()

if jira_helper.get_jira_project(eng):
dojo_dispatch_task(jira_helper.close_epic, eng.id, push_to_jira=True)
if jira_services.get_project(eng):
task = jira_services.get_epic_task("close_epic")
if task:
dojo_dispatch_task(task, eng.id, push_to_jira=True)


def reopen_engagement(eng):
Expand Down
Loading
Loading