Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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