diff --git a/NOTICE b/NOTICE
index e939bd7fc99..7733257f54b 100644
--- a/NOTICE
+++ b/NOTICE
@@ -3910,49 +3910,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-drf-yasg
-1.20.0
-BSD License
-.. |br| raw:: html
-
-
-
-#######
-License
-#######
-
-********************
-BSD 3-Clause License
-********************
-
-Copyright (c) 2017 - 2019, Cristian V. |br|\ All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-* Neither the name of the copyright holder nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
ecdsa
0.17.0
MIT
diff --git a/docs/content/en/getting_started/upgrading/2.32.md b/docs/content/en/getting_started/upgrading/2.32.md
index 0d04c771e36..59081b30857 100644
--- a/docs/content/en/getting_started/upgrading/2.32.md
+++ b/docs/content/en/getting_started/upgrading/2.32.md
@@ -2,6 +2,13 @@
title: 'Upgrading to DefectDojo Version 2.32.x'
toc_hide: true
weight: -20240205
-description: No special instructions.
+description: Breaking change: Removal of OpenAPI 2.0 Swagger
---
There are no special instructions for upgrading to 2.32.x. Check the [Release Notes](https://github.com/DefectDojo/django-DefectDojo/releases/tag/2.32.0) for the contents of the release.
+
+**Removal**
+
+The OpenAPI 2.0 Swagger API documentation was removed in favor of the existing
+OpenAPI 3.0 API documentation page.
+
+*Note*: The API has not changed in any way and behaves the same between OAPI2 and OAPI3
\ No newline at end of file
diff --git a/docs/content/en/integrations/api-v2-docs.md b/docs/content/en/integrations/api-v2-docs.md
index c64dfcc8919..7b8d1f7956c 100644
--- a/docs/content/en/integrations/api-v2-docs.md
+++ b/docs/content/en/integrations/api-v2-docs.md
@@ -16,11 +16,8 @@ Docs link on the user drop down menu in the header.
![image](../../images/api_v2_1.png)
-The documentation is generated using [Django Rest Framework
-Yet Another Swagger Generator](https://github.com/axnsan12/drf-yasg/), and is
-interactive. On the top of API v2 docs is a link that generates an OpenAPI v2 spec.
-
-As a preparation to move to OpenAPIv3, we have added an compatible spec and documentation at [`/api/v2/oa3/swagger-ui/`](https://demo.defectdojo.org/api/v2/oa3/swagger-ui/)
+The documentation is generated using [drf-spectacular](https://drf-spectacular.readthedocs.io/) at [`/api/v2/oa3/swagger-ui/`](https://demo.defectdojo.org/api/v2/oa3/swagger-ui/), and is
+interactive. On the top of API v2 docs is a link that generates an OpenAPI v3 spec.
To interact with the documentation, a valid Authorization header value
is needed. Visit the `/api/key-v2` view to generate your
diff --git a/dojo/api_v2/mixins.py b/dojo/api_v2/mixins.py
index e0770971f3f..54d55a76d09 100644
--- a/dojo/api_v2/mixins.py
+++ b/dojo/api_v2/mixins.py
@@ -1,7 +1,6 @@
from django.db import DEFAULT_DB_ALIAS
from django.contrib.admin.utils import NestedObjects
from drf_spectacular.utils import extend_schema
-from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import action
from rest_framework import status
from rest_framework.authtoken.models import Token
@@ -17,10 +16,6 @@ class DeletePreviewModelMixin:
status.HTTP_200_OK: serializers.DeletePreviewSerializer(many=True)
},
)
- @swagger_auto_schema(
- method="get",
- responses={"default": serializers.DeletePreviewSerializer(many=True)},
- )
@action(detail=True, methods=["get"], filter_backends=[], suffix="List")
def delete_preview(self, request, pk=None):
object = self.get_object()
diff --git a/dojo/api_v2/prefetch/__init__.py b/dojo/api_v2/prefetch/__init__.py
index f0449c7b303..3d02655ec22 100644
--- a/dojo/api_v2/prefetch/__init__.py
+++ b/dojo/api_v2/prefetch/__init__.py
@@ -1,4 +1,3 @@
from .mixins import PrefetchListMixin, PrefetchRetrieveMixin
-from .schema import get_prefetch_schema
-__all__ = ["PrefetchListMixin", "PrefetchRetrieveMixin", "get_prefetch_schema"]
+__all__ = ["PrefetchListMixin", "PrefetchRetrieveMixin"]
diff --git a/dojo/api_v2/prefetch/schema.py b/dojo/api_v2/prefetch/schema.py
index 6d04e751800..6fc08681477 100644
--- a/dojo/api_v2/prefetch/schema.py
+++ b/dojo/api_v2/prefetch/schema.py
@@ -1,84 +1,5 @@
-from drf_yasg import openapi, utils
from .prefetcher import _Prefetcher
from .utils import _get_prefetchable_fields
-from ..schema import extra_schema
-from ..schema.utils import LazySchemaRef
-
-
-def get_prefetch_schema(methods, serializer):
- """Swagger / OpenAPI v2 (drf-yasg) Return a composable swagger schema that contains in the query the fields that can be prefetch from the model
- supported by the serializer and in the reponse the structure of these fields in a new top-level attribute
- named prefetch.
-
- Returns:
- ComposableSchema: A swagger schema
- """
- prefetcher = _Prefetcher()
- fields = _get_prefetchable_fields(serializer())
-
- field_to_serializer = dict(
- [
- (name, prefetcher._find_serializer(field_type))
- for name, field_type in fields
- if prefetcher._find_serializer(field_type)
- ]
- )
- fields_to_refname = dict(
- [
- (name, utils.get_serializer_ref_name(serializer()))
- for name, serializer in field_to_serializer.items()
- ]
- )
- fields_name = [
- name
- for name, field_type in fields
- if prefetcher._find_serializer(field_type)
- ]
-
- # New openapi parameter corresponding to the prefetchable fields
- prefetch_params = [
- openapi.Parameter(
- "prefetch",
- in_=openapi.IN_QUERY,
- required=False,
- type=openapi.TYPE_ARRAY,
- items=openapi.Items(type=openapi.TYPE_STRING, enum=fields_name),
- )
- ]
-
- additional_props = dict(
- [
- (
- name,
- openapi.Schema(
- type=openapi.TYPE_OBJECT,
- read_only=True,
- additional_properties=LazySchemaRef(
- fields_to_refname[name], True
- ),
- ),
- )
- for name in fields_name
- ]
- )
- prefetch_response = {
- "200": {
- "prefetch": openapi.Schema(
- type=openapi.TYPE_OBJECT, properties=additional_props
- )
- }
- }
-
- schema = extra_schema.IdentitySchema()
- for method in methods:
- schema = schema.composeWith(
- extra_schema.ExtraParameters(method, prefetch_params)
- )
- schema = schema.composeWith(
- extra_schema.ExtraResponseField(method, prefetch_response)
- )
-
- return schema
def _get_path_to_GET_serializer_map(generator):
@@ -97,6 +18,25 @@ def _get_path_to_GET_serializer_map(generator):
return path_to_GET_serializer
+def get_serializer_ref_name(serializer):
+ """Get serializer's ref_name
+ inspired by https://github.com/axnsan12/drf-yasg/blob/78031f0c189585c30fccb5005a6899f2d34289a9/src/drf_yasg/utils.py#L416
+
+ :param serializer: Serializer instance
+ :return: Serializer's ``ref_name`` or ``None`` for inline serializer
+ :rtype: str or None
+ """
+ serializer_meta = getattr(serializer, 'Meta', None)
+ serializer_name = type(serializer).__name__
+ if hasattr(serializer_meta, 'ref_name'):
+ ref_name = serializer_meta.ref_name
+ else:
+ ref_name = serializer_name
+ if ref_name.endswith('Serializer'):
+ ref_name = ref_name[:-len('Serializer')]
+ return ref_name
+
+
def prefetch_postprocessing_hook(result, generator, request, public):
"""OpenAPI v3 (drf-spectacular) Some endpoints are using the PrefetchListMixin and PrefetchRetrieveMixin.
These have nothing to do with Django prefetch_related.
@@ -131,55 +71,37 @@ def prefetch_postprocessing_hook(result, generator, request, public):
"enum": field_names,
}
- field_to_serializer = dict(
- [
- (name, prefetcher._find_serializer(field_type))
- for name, field_type in fields
- if prefetcher._find_serializer(field_type)
- ]
- )
- fields_to_refname = dict(
- [
- (name, utils.get_serializer_ref_name(serializer()))
- for name, serializer in field_to_serializer.items()
- ]
- )
- properties = dict(
- [
- (
- name,
- dict(
- [
- ("type", "object"),
- ("readOnly", True),
- (
- "additionalProperties",
- dict(
- [
- (
- "$ref",
- "#/components/schemas/"
- + fields_to_refname[
- name
- ],
- )
- ]
- ),
- ),
- ]
- ),
- )
- for name in field_names
- ]
- )
+ field_to_serializer = {
+ name: prefetcher._find_serializer(field_type)
+ for name, field_type in fields
+ if prefetcher._find_serializer(field_type)
+ }
+
+ fields_to_refname = {
+ name: get_serializer_ref_name(serializer())
+ for name, serializer in field_to_serializer.items()
+ }
+
+ properties = {
+ name: {
+ "type": "object",
+ "readOnly": True,
+ "additionalProperties": {
+ "$ref": f"#/components/schemas/{fields_to_refname[name]}"
+ }
+ }
+ for name in field_names
+ }
+
ref = paths[path]["get"]["responses"]["200"]["content"][
"application/json"
]["schema"]["$ref"]
component_name = ref.split("/")[-1]
result["components"]["schemas"][component_name][
"properties"
- ]["prefetch"] = dict(
- [("type", "object"), ("properties", properties)]
- )
+ ]["prefetch"] = {
+ "type": "object",
+ "properties": properties,
+ }
return result
diff --git a/dojo/api_v2/schema/__init__.py b/dojo/api_v2/schema/__init__.py
deleted file mode 100644
index 6a69a167022..00000000000
--- a/dojo/api_v2/schema/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from .extra_schema import (
- IdentitySchema,
- ExtraParameters,
- ExtraResponseField,
- ComposableSchema,
-)
-from .utils import LazySchemaRef, try_apply, resolve_lazy_ref
-
-__all__ = [
- "IdentitySchema",
- "ExtraParameters",
- "ExtraResponseField",
- "ComposableSchema",
- "LazySchemaRef",
- "try_apply",
- "resolve_lazy_ref",
-]
diff --git a/dojo/api_v2/schema/extra_schema.py b/dojo/api_v2/schema/extra_schema.py
deleted file mode 100644
index 86fd565e370..00000000000
--- a/dojo/api_v2/schema/extra_schema.py
+++ /dev/null
@@ -1,140 +0,0 @@
-from drf_yasg.inspectors.view import SwaggerAutoSchema
-from drf_yasg.openapi import resolve_ref, Schema
-from .utils import resolve_lazy_ref
-import copy
-
-
-class ComposableSchema:
- """A composable schema defines a transformation on drf_yasg Operation. These
- schema can then be composed with another composable schema using the composeWith method
- yielding a new composable schema whose transformation is defined as the function composition
- of the transformation of the two source schema.
- """
-
- def transform_operation(self, operation, resolver):
- """Defines an operation transformation
-
- Args:
- operation (Operation): the operation to transform
- resolver (Resolver): the schema refs resolver
- """
-
- def composeWith(self, schema):
- """Allow two schema to be composed into a new schema.
- Given the caller schema 'self' and another schema 'schema',
- this operation yields a new composable schema whose transform_operation
- if defined as
- transform_operation(op, res) = schema.transform_operation(self.transform_operation(op, res), res)
-
- Args:
- schema (ComposableSchema): The schema to compose with
-
- Returns:
- ComposableSchema: the newly composed schema
- """
- op = self.transform_operation
-
- class _Wrapper(ComposableSchema):
- def transform_operation(self, operation, resolver):
- return schema.transform_operation(
- op(operation, resolver), resolver
- )
-
- return _Wrapper()
-
- def to_schema(self):
- """Convert the composable schema into a SwaggerAutoSchema that
- can be used with the drf_yasg library code
-
- Returns:
- SwaggerAutoSchema: the swagger auto schema derived from the composable schema
- """
- op = self.transform_operation
-
- class _Schema(SwaggerAutoSchema):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
-
- def get_operation(self, operation_keys):
- operation = super().get_operation(operation_keys)
- return op(operation, self.components)
-
- return _Schema
-
-
-class IdentitySchema(ComposableSchema):
- def transform_operation(self, operation, resolver):
- return operation
-
-
-class ExtraParameters(ComposableSchema):
- """Define a schema that can add parameters to the operation"""
-
- def __init__(self, operation_name, extra_parameters, *args, **kwargs):
- """Initialize the schema
-
- Args:
- operation_name (string): the name of the operation to transform
- extra_parameters (list[Parameter]): list of openapi parameters to add
- """
- super().__init__(*args, **kwargs)
- self._extra_parameters = extra_parameters
- self._operation_name = operation_name
-
- def transform_operation(self, operation, resolver):
- operation_id = operation["operationId"]
- if not operation_id.endswith(self._operation_name):
- return operation
-
- for param in self._extra_parameters:
- operation["parameters"].append(resolve_lazy_ref(param, resolver))
- return operation
-
-
-class ExtraResponseField(ComposableSchema):
- """Define a schema that can add fields to the responses of the operation"""
-
- def __init__(self, operation_name, extra_fields, *args, **kwargs):
- """Initialize the schema
-
- Args:
- operation_name (string): the name of the operation to transform
- extra_fields (dict()): description of the fields to add to the responses. The format is
- {
- parameters: list[openapi.Parameter](params1, params2, ...),
- responses: {
- code1: {
- field1: openapi.Schema,
- field2: openapi.Schema,
- ...
- },
- code2: ...
- }
- }
- """
- super().__init__(*args, **kwargs)
- self._extra_fields = extra_fields
- self._operation_name = operation_name
-
- def transform_operation(self, operation, resolver):
- operation_id = operation["operationId"]
- if not operation_id.endswith(self._operation_name):
- return operation
-
- responses = operation["responses"]
- for code, params in self._extra_fields.items():
- if code in responses:
- original_schema = responses[code]["schema"]
- schema = (
- original_schema
- if isinstance(original_schema, Schema)
- else resolve_ref(original_schema, resolver)
- )
- schema = copy.deepcopy(schema)
-
- for name, param in params.items():
- schema["properties"][name] = resolve_lazy_ref(
- param, resolver
- )
- responses[code]["schema"] = schema
- return operation
diff --git a/dojo/api_v2/schema/utils.py b/dojo/api_v2/schema/utils.py
deleted file mode 100644
index 1276202fc81..00000000000
--- a/dojo/api_v2/schema/utils.py
+++ /dev/null
@@ -1,63 +0,0 @@
-from drf_yasg.openapi import SchemaRef, Schema
-
-
-class LazySchemaRef:
- """Utility class to support SchemaRef definition without knowing the resolver.
- The reference can be evaluated later in the context of a swagger generator
- """
-
- def __init__(self, schema_name, ignore_unresolved=False):
- # Bind curried version of the SchemaRef init
- self.schema_ref = lambda resolver: SchemaRef(
- resolver, schema_name, ignore_unresolved
- )
-
- def apply(self, resolver):
- """Resolve the LazySchemaRef with the given resolver
-
- Args:
- resolver (ReferenceResolver): resolver containing the schema refs
-
- Returns:
- SchemaRef: the corresponding SchemaRef
- """
- return self.schema_ref(resolver)
-
-
-def try_apply(obj, resolver):
- """Try to resolve a LazySchemaRef
-
- Args:
- obj (object): the object to resolve
- resolver (resolver): the resolver to use
-
- Returns:
- object: the original object if it was not resolve otherwise the resolved LazySchemaRef
- """
- if isinstance(obj, LazySchemaRef):
- return obj.apply(resolver)
- else:
- return obj
-
-
-def resolve_lazy_ref(schema, resolver):
- """Recursively evaluate the schema to unbox LazySchemaRef based on the underlying resolvers.
-
- Args:
- schema (object): the schema to evaluate
-
- Returns:
- object: the schema without LazySchemaRef
- """
- if not isinstance(schema, Schema):
- return try_apply(schema, resolver)
-
- if "properties" in schema:
- for prop_name, prop in schema["properties"].items():
- schema["properties"][prop_name] = resolve_lazy_ref(prop, resolver)
- if "additionalProperties" in schema:
- schema["additionalProperties"] = resolve_lazy_ref(
- schema["additionalProperties"], resolver
- )
-
- return schema
diff --git a/dojo/api_v2/serializers.py b/dojo/api_v2/serializers.py
index 5778f2147ca..cf6aec4a070 100644
--- a/dojo/api_v2/serializers.py
+++ b/dojo/api_v2/serializers.py
@@ -2,7 +2,6 @@
from django.contrib.auth.models import Group
from typing import List
from drf_spectacular.utils import extend_schema_field
-from drf_yasg.utils import swagger_serializer_method
from rest_framework.exceptions import NotFound
from rest_framework.fields import DictField, MultipleChoiceField
from datetime import datetime
@@ -1500,17 +1499,14 @@ class RiskAcceptanceSerializer(serializers.ModelSerializer):
path = serializers.SerializerMethodField()
@extend_schema_field(serializers.CharField())
- @swagger_serializer_method(serializers.CharField())
def get_recommendation(self, obj):
return Risk_Acceptance.TREATMENT_TRANSLATIONS.get(obj.recommendation)
@extend_schema_field(serializers.CharField())
- @swagger_serializer_method(serializers.CharField())
def get_decision(self, obj):
return Risk_Acceptance.TREATMENT_TRANSLATIONS.get(obj.decision)
@extend_schema_field(serializers.CharField())
- @swagger_serializer_method(serializers.CharField())
def get_path(self, obj):
engagement = Engagement.objects.filter(
risk_acceptance__id__in=[obj.id]
@@ -1526,7 +1522,6 @@ def get_path(self, obj):
return path
@extend_schema_field(serializers.IntegerField())
- @swagger_serializer_method(serializers.IntegerField())
def get_engagement(self, obj):
engagement = Engagement.objects.filter(
risk_acceptance__id__in=[obj.id]
@@ -1629,14 +1624,12 @@ class FindingRelatedFieldsSerializer(serializers.Serializer):
jira = serializers.SerializerMethodField()
@extend_schema_field(FindingTestSerializer)
- @swagger_serializer_method(FindingTestSerializer)
def get_test(self, obj):
return FindingTestSerializer(read_only=True).to_representation(
obj.test
)
@extend_schema_field(JIRAIssueSerializer)
- @swagger_serializer_method(JIRAIssueSerializer)
def get_jira(self, obj):
issue = jira_helper.get_jira_issue(obj)
if issue is None:
@@ -1683,17 +1676,14 @@ class Meta:
)
@extend_schema_field(serializers.DateTimeField())
- @swagger_serializer_method(serializers.DateTimeField())
def get_jira_creation(self, obj):
return jira_helper.get_jira_creation(obj)
@extend_schema_field(serializers.DateTimeField())
- @swagger_serializer_method(serializers.DateTimeField())
def get_jira_change(self, obj):
return jira_helper.get_jira_change(obj)
@extend_schema_field(FindingRelatedFieldsSerializer)
- @swagger_serializer_method(FindingRelatedFieldsSerializer)
def get_related_fields(self, obj):
request = self.context.get("request", None)
if request is None:
@@ -1798,9 +1788,6 @@ def build_relational_field(self, field_name, relation_info):
return super().build_relational_field(field_name, relation_info)
@extend_schema_field(BurpRawRequestResponseSerializer)
- @swagger_serializer_method(
- serializer_or_field=BurpRawRequestResponseSerializer
- )
def get_request_response(self, obj):
# burp_req_resp = BurpRawRequestResponse.objects.filter(finding=obj)
burp_req_resp = obj.burprawrequestresponse_set.all()
@@ -2039,12 +2026,7 @@ def validate(self, data):
def get_findings_count(self, obj) -> int:
return obj.findings_count
- # -> List[int] as return type doesn't seem enough for drf-yasg
- @swagger_serializer_method(
- serializer_or_field=serializers.ListField(
- child=serializers.IntegerField()
- )
- )
+ # TODO, maybe extend_schema_field is needed here?
def get_findings_list(self, obj) -> List[int]:
return obj.open_findings_list
@@ -3185,9 +3167,6 @@ class QuestionnaireEngagementSurveySerializer(serializers.ModelSerializer):
questions = serializers.SerializerMethodField()
@extend_schema_field(serializers.ListField(child=serializers.CharField()))
- @swagger_serializer_method(
- serializers.ListField(child=serializers.CharField())
- )
def get_questions(self, obj):
questions = obj.questions.all()
formated_questions = []
diff --git a/dojo/api_v2/views.py b/dojo/api_v2/views.py
index fceb87c7ea2..793ff5d240b 100644
--- a/dojo/api_v2/views.py
+++ b/dojo/api_v2/views.py
@@ -6,9 +6,6 @@
from django.utils import timezone
from django.contrib.auth.models import Permission
from django.core.exceptions import ValidationError
-from django.utils.decorators import method_decorator
-from drf_yasg.inspectors.base import NotHandled
-from drf_yasg.inspectors.query import CoreAPICompatInspector
from rest_framework import viewsets, mixins, status
from rest_framework.response import Response
from django.db import IntegrityError
@@ -16,8 +13,6 @@
from rest_framework.decorators import action
from rest_framework.parsers import MultiPartParser
from django_filters.rest_framework import DjangoFilterBackend
-from drf_yasg import openapi
-from drf_yasg.utils import swagger_auto_schema, no_body
import base64
import mimetypes
from dojo.engagement.services import close_engagement, reopen_engagement
@@ -119,7 +114,6 @@
serializers,
permissions,
prefetch,
- schema,
mixins as dojo_mixins,
)
import dojo.jira_link.helper as jira_helper
@@ -242,10 +236,6 @@ class DojoGroupViewSet(
queryset = Dojo_Group.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_fields = ["id", "name", "social_provider"]
- swagger_schema = prefetch.get_prefetch_schema(
- ["dojo_groups_list", "dojo_groups_read"],
- serializers.DojoGroupSerializer,
- ).to_schema()
permission_classes = (
IsAuthenticated,
permissions.UserHasDojoGroupPermission,
@@ -287,10 +277,6 @@ class DojoGroupMemberViewSet(
queryset = Dojo_Group_Member.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_fields = ["id", "group_id", "user_id"]
- swagger_schema = prefetch.get_prefetch_schema(
- ["dojo_group_members_list", "dojo_group_members_read"],
- serializers.DojoGroupMemberSerializer,
- ).to_schema()
permission_classes = (
IsAuthenticated,
permissions.UserHasDojoGroupMemberPermission,
@@ -302,9 +288,6 @@ def get_queryset(self):
@extend_schema(
exclude=True
)
- @swagger_auto_schema(
- auto_schema=None
- )
def partial_update(self, request, pk=None):
# Object authorization won't work if not all data is provided
response = {"message": "Patch function is not offered in this path."}
@@ -319,10 +302,6 @@ class GlobalRoleViewSet(
queryset = Global_Role.objects.all()
filter_backends = (DjangoFilterBackend,)
filterset_fields = ["id", "user", "group", "role"]
- swagger_schema = prefetch.get_prefetch_schema(
- ["global_roles_list", "global_roles_read"],
- serializers.GlobalRoleSerializer,
- ).to_schema()
permission_classes = (permissions.IsSuperUser, DjangoModelPermissions)
@@ -334,9 +313,7 @@ class EndPointViewSet(
queryset = Endpoint.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_class = ApiEndpointFilter
- swagger_schema = prefetch.get_prefetch_schema(
- ["endpoints_list", "endpoints_read"], serializers.EndpointSerializer
- ).to_schema()
+
permission_classes = (
IsAuthenticated,
permissions.UserHasEndpointPermission,
@@ -349,10 +326,6 @@ def get_queryset(self):
request=serializers.ReportGenerateOptionSerializer,
responses={status.HTTP_200_OK: serializers.ReportGenerateSerializer},
)
- @swagger_auto_schema(
- request_body=serializers.ReportGenerateOptionSerializer,
- responses={status.HTTP_200_OK: serializers.ReportGenerateSerializer},
- )
@action(
detail=True, methods=["post"], permission_classes=[IsAuthenticated]
)
@@ -403,10 +376,7 @@ class EndpointStatusViewSet(
"finding",
"endpoint",
]
- swagger_schema = prefetch.get_prefetch_schema(
- ["endpoint_status_list", "endpoint_status_read"],
- serializers.EndpointStatusSerializer,
- ).to_schema()
+
permission_classes = (
IsAuthenticated,
permissions.UserHasEndpointStatusPermission,
@@ -427,19 +397,7 @@ class EngagementViewSet(
queryset = Engagement.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_class = ApiEngagementFilter
- swagger_schema = (
- prefetch.get_prefetch_schema(
- ["engagements_list", "engagements_read"],
- serializers.EngagementSerializer,
- )
- .composeWith(
- prefetch.get_prefetch_schema(
- ["engagements_complete_checklist_read"],
- serializers.EngagementCheckListSerializer,
- )
- )
- .to_schema()
- )
+
permission_classes = (
IsAuthenticated,
permissions.UserHasEngagementPermission,
@@ -468,9 +426,6 @@ def get_queryset(self):
@extend_schema(
request=OpenApiTypes.NONE, responses={status.HTTP_200_OK: ""}
)
- @swagger_auto_schema(
- request_body=no_body, responses={status.HTTP_200_OK: ""}
- )
@action(detail=True, methods=["post"])
def close(self, request, pk=None):
eng = self.get_object()
@@ -480,9 +435,6 @@ def close(self, request, pk=None):
@extend_schema(
request=OpenApiTypes.NONE, responses={status.HTTP_200_OK: ""}
)
- @swagger_auto_schema(
- request_body=no_body, responses={status.HTTP_200_OK: ""}
- )
@action(detail=True, methods=["post"])
def reopen(self, request, pk=None):
eng = self.get_object()
@@ -493,10 +445,6 @@ def reopen(self, request, pk=None):
request=serializers.ReportGenerateOptionSerializer,
responses={status.HTTP_200_OK: serializers.ReportGenerateSerializer},
)
- @swagger_auto_schema(
- request_body=serializers.ReportGenerateOptionSerializer,
- responses={status.HTTP_200_OK: serializers.ReportGenerateSerializer},
- )
@action(
detail=True, methods=["post"], permission_classes=[IsAuthenticated]
)
@@ -541,17 +489,6 @@ def generate_report(self, request, pk=None):
request=serializers.AddNewNoteOptionSerializer,
responses={status.HTTP_201_CREATED: serializers.NoteSerializer},
)
- @swagger_auto_schema(
- method="get",
- responses={
- status.HTTP_200_OK: serializers.EngagementToNotesSerializer
- },
- )
- @swagger_auto_schema(
- methods=["post"],
- request_body=serializers.AddNewNoteOptionSerializer,
- responses={status.HTTP_201_CREATED: serializers.NoteSerializer},
- )
@action(detail=True, methods=["get", "post"])
def notes(self, request, pk=None):
engagement = self.get_object()
@@ -602,17 +539,6 @@ def notes(self, request, pk=None):
request=serializers.AddNewFileOptionSerializer,
responses={status.HTTP_201_CREATED: serializers.FileSerializer},
)
- @swagger_auto_schema(
- method="get",
- responses={
- status.HTTP_200_OK: serializers.EngagementToFilesSerializer
- },
- )
- @swagger_auto_schema(
- method="post",
- request_body=serializers.AddNewFileOptionSerializer,
- responses={status.HTTP_201_CREATED: serializers.FileSerializer},
- )
@action(
detail=True, methods=["get", "post"], parser_classes=(MultiPartParser,)
)
@@ -650,13 +576,6 @@ def files(self, request, pk=None):
status.HTTP_201_CREATED: serializers.EngagementCheckListSerializer
},
)
- @swagger_auto_schema(
- method="post",
- request_body=serializers.EngagementCheckListSerializer,
- responses={
- status.HTTP_201_CREATED: serializers.EngagementCheckListSerializer
- },
- )
@action(detail=True, methods=["get", "post"])
def complete_checklist(self, request, pk=None):
from dojo.api_v2.prefetch.prefetcher import _Prefetcher
@@ -702,12 +621,6 @@ def complete_checklist(self, request, pk=None):
status.HTTP_200_OK: serializers.RawFileSerializer,
},
)
- @swagger_auto_schema(
- method="get",
- responses={
- status.HTTP_200_OK: serializers.RawFileSerializer,
- },
- )
@action(
detail=True,
methods=["get"],
@@ -749,10 +662,7 @@ class RiskAcceptanceViewSet(
queryset = Risk_Acceptance.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_class = ApiRiskAcceptanceFilter
- swagger_schema = prefetch.get_prefetch_schema(
- ["risk_acceptance_list", "risk_acceptance_read"],
- serializers.RiskAcceptanceSerializer,
- ).to_schema()
+
permission_classes = (
IsAuthenticated,
permissions.UserHasRiskAcceptancePermission,
@@ -773,12 +683,6 @@ def get_queryset(self):
status.HTTP_200_OK: serializers.RiskAcceptanceProofSerializer,
},
)
- @swagger_auto_schema(
- method="get",
- responses={
- status.HTTP_200_OK: serializers.RiskAcceptanceProofSerializer,
- },
- )
@action(detail=True, methods=["get"])
def download_proof(self, request, pk=None):
risk_acceptance = self.get_object()
@@ -815,10 +719,7 @@ class AppAnalysisViewSet(
queryset = App_Analysis.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_class = ApiAppAnalysisFilter
- swagger_schema = prefetch.get_prefetch_schema(
- ["technologies_list", "technologies_read"],
- serializers.AppAnalysisSerializer,
- ).to_schema()
+
permission_classes = (
IsAuthenticated,
permissions.UserHasAppAnalysisPermission,
@@ -835,10 +736,7 @@ class CredentialsViewSet(
serializer_class = serializers.CredentialSerializer
queryset = Cred_User.objects.all()
filter_backends = (DjangoFilterBackend,)
- swagger_schema = prefetch.get_prefetch_schema(
- ["credentials_list", "credentials_read"],
- serializers.CredentialSerializer,
- ).to_schema()
+
permission_classes = (permissions.IsSuperUser, DjangoModelPermissions)
@@ -850,10 +748,7 @@ class CredentialsMappingViewSet(
queryset = Cred_Mapping.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_class = ApiCredentialsFilter
- swagger_schema = prefetch.get_prefetch_schema(
- ["credential_mappings_list", "credential_mappings_read"],
- serializers.CredentialMappingSerializer,
- ).to_schema()
+
permission_classes = (
IsAuthenticated,
permissions.UserHasCredentialPermission,
@@ -934,27 +829,6 @@ class FindingViewSet(
permissions.UserHasFindingPermission,
)
- _related_field_parameters = [
- openapi.Parameter(
- name="related_fields",
- in_=openapi.IN_QUERY,
- description="Expand finding external relations (engagement, environment, product, product_type, test, test_type)",
- type=openapi.TYPE_BOOLEAN,
- )
- ]
- swagger_schema = (
- prefetch.get_prefetch_schema(
- ["findings_list", "findings_read"], serializers.FindingSerializer
- )
- .composeWith(
- schema.ExtraParameters("findings_list", _related_field_parameters)
- )
- .composeWith(
- schema.ExtraParameters("findings_read", _related_field_parameters)
- )
- .to_schema()
- )
-
# Overriding mixins.UpdateModeMixin perform_update() method to grab push_to_jira
# data and add that as a parameter to .save()
def perform_update(self, serializer):
@@ -1003,11 +877,6 @@ def get_serializer_class(self):
request=serializers.FindingCloseSerializer,
responses={status.HTTP_200_OK: serializers.FindingCloseSerializer},
)
- @swagger_auto_schema(
- method="post",
- request_body=serializers.FindingCloseSerializer,
- responses={status.HTTP_200_OK: serializers.FindingCloseSerializer},
- )
@action(detail=True, methods=["post"])
def close(self, request, pk=None):
finding = self.get_object()
@@ -1069,14 +938,6 @@ def close(self, request, pk=None):
request=serializers.TagSerializer,
responses={status.HTTP_201_CREATED: serializers.TagSerializer},
)
- @swagger_auto_schema(
- method="get", responses={status.HTTP_200_OK: serializers.TagSerializer}
- )
- @swagger_auto_schema(
- method="post",
- request_body=serializers.TagSerializer,
- responses={status.HTTP_200_OK: serializers.TagSerializer},
- )
@action(detail=True, methods=["get", "post"])
def tags(self, request, pk=None):
finding = self.get_object()
@@ -1118,19 +979,6 @@ def tags(self, request, pk=None):
status.HTTP_201_CREATED: serializers.BurpRawRequestResponseSerializer
},
)
- @swagger_auto_schema(
- method="get",
- responses={
- status.HTTP_200_OK: serializers.BurpRawRequestResponseSerializer
- },
- )
- @swagger_auto_schema(
- method="post",
- request_body=serializers.BurpRawRequestResponseSerializer,
- responses={
- status.HTTP_200_OK: serializers.BurpRawRequestResponseSerializer
- },
- )
@action(detail=True, methods=["get", "post"])
def request_response(self, request, pk=None):
finding = self.get_object()
@@ -1177,15 +1025,6 @@ def request_response(self, request, pk=None):
request=serializers.AddNewNoteOptionSerializer,
responses={status.HTTP_201_CREATED: serializers.NoteSerializer},
)
- @swagger_auto_schema(
- method="get",
- responses={status.HTTP_200_OK: serializers.FindingToNotesSerializer},
- )
- @swagger_auto_schema(
- methods=["post"],
- request_body=serializers.AddNewNoteOptionSerializer,
- responses={status.HTTP_201_CREATED: serializers.NoteSerializer},
- )
@action(detail=True, methods=["get", "post"])
def notes(self, request, pk=None):
finding = self.get_object()
@@ -1239,15 +1078,6 @@ def notes(self, request, pk=None):
request=serializers.AddNewFileOptionSerializer,
responses={status.HTTP_201_CREATED: serializers.FileSerializer},
)
- @swagger_auto_schema(
- method="get",
- responses={status.HTTP_200_OK: serializers.FindingToFilesSerializer},
- )
- @swagger_auto_schema(
- method="post",
- request_body=serializers.AddNewFileOptionSerializer,
- responses={status.HTTP_201_CREATED: serializers.FileSerializer},
- )
@action(
detail=True, methods=["get", "post"], parser_classes=(MultiPartParser,)
)
@@ -1284,12 +1114,6 @@ def files(self, request, pk=None):
status.HTTP_200_OK: serializers.RawFileSerializer,
},
)
- @swagger_auto_schema(
- method="get",
- responses={
- status.HTTP_200_OK: serializers.RawFileSerializer,
- },
- )
@action(
detail=True,
methods=["get"],
@@ -1327,10 +1151,6 @@ def download_file(self, request, file_id, pk=None):
request=serializers.FindingNoteSerializer,
responses={status.HTTP_204_NO_CONTENT: ""},
)
- @swagger_auto_schema(
- request_body=serializers.FindingNoteSerializer,
- responses={status.HTTP_204_NO_CONTENT: ""},
- )
@action(detail=True, methods=["patch"])
def remove_note(self, request, pk=None):
"""Remove Note From Finding Note"""
@@ -1370,11 +1190,6 @@ def remove_note(self, request, pk=None):
request=serializers.TagSerializer,
responses={status.HTTP_204_NO_CONTENT: ""},
)
- @swagger_auto_schema(
- methods=["put", "patch"],
- request_body=serializers.TagSerializer,
- responses={status.HTTP_204_NO_CONTENT: ""},
- )
@action(detail=True, methods=["put", "patch"])
def remove_tags(self, request, pk=None):
"""Remove Tag(s) from finding list of tags"""
@@ -1423,11 +1238,6 @@ def remove_tags(self, request, pk=None):
status.HTTP_200_OK: serializers.FindingSerializer(many=True)
}
)
- @swagger_auto_schema(
- responses={
- status.HTTP_200_OK: serializers.FindingSerializer(many=True)
- }
- )
@action(
detail=True,
methods=["get"],
@@ -1447,10 +1257,6 @@ def get_duplicate_cluster(self, request, pk):
request=OpenApiTypes.NONE,
responses={status.HTTP_204_NO_CONTENT: ""},
)
- @swagger_auto_schema(
- request_body=no_body,
- responses={status.HTTP_204_NO_CONTENT: ""},
- )
@action(detail=True, methods=["post"], url_path=r"duplicate/reset")
def reset_finding_duplicate_status(self, request, pk):
checked_duplicate_id = reset_finding_duplicate_status_internal(
@@ -1469,9 +1275,6 @@ def reset_finding_duplicate_status(self, request, pk):
],
responses={status.HTTP_204_NO_CONTENT: ""},
)
- @swagger_auto_schema(
- responses={status.HTTP_204_NO_CONTENT: ""}, request_body=no_body
- )
@action(
detail=True, methods=["post"], url_path=r"original/(?P\d+)"
)
@@ -1485,10 +1288,6 @@ def set_finding_as_original(self, request, pk, new_fid):
request=serializers.ReportGenerateOptionSerializer,
responses={status.HTTP_200_OK: serializers.ReportGenerateSerializer},
)
- @swagger_auto_schema(
- request_body=serializers.ReportGenerateOptionSerializer,
- responses={status.HTTP_200_OK: serializers.ReportGenerateSerializer},
- )
@action(
detail=False, methods=["post"], permission_classes=[IsAuthenticated]
)
@@ -1622,10 +1421,6 @@ def _remove_metadata(self, request, finding):
description="Returned if there was a problem with the metadata information"
),
},
- # manual_parameters=[openapi.Parameter(
- # name="name", in_=openapi.IN_QUERY, type=openapi.TYPE_STRING,
- # description="name of the metadata to retrieve. If name is empty, return all the \
- # metadata associated with the finding")]
)
@extend_schema(
methods=["PUT"],
@@ -1639,9 +1434,6 @@ def _remove_metadata(self, request, finding):
description="Returned if there was a problem with the metadata information"
),
},
- # manual_parameters=[openapi.Parameter(
- # name="name", in_=openapi.IN_QUERY, required=True, type=openapi.TYPE_STRING,
- # description="name of the metadata to edit")],
)
@extend_schema(
methods=["POST"],
@@ -1656,58 +1448,6 @@ def _remove_metadata(self, request, finding):
),
},
)
- @swagger_auto_schema(
- responses={
- status.HTTP_200_OK: serializers.FindingMetaSerializer(many=True),
- status.HTTP_404_NOT_FOUND: "Returned if finding does not exist",
- },
- methods=["get"],
- )
- @swagger_auto_schema(
- responses={
- status.HTTP_200_OK: "Returned if the metadata was correctly deleted",
- status.HTTP_404_NOT_FOUND: "Returned if finding does not exist",
- status.HTTP_400_BAD_REQUEST: "Returned if there was a problem with the metadata information",
- },
- methods=["delete"],
- manual_parameters=[
- openapi.Parameter(
- name="name",
- in_=openapi.IN_QUERY,
- required=True,
- type=openapi.TYPE_STRING,
- description="name of the metadata to retrieve. If name is empty, return all the \
- metadata associated with the finding",
- )
- ],
- )
- @swagger_auto_schema(
- responses={
- status.HTTP_200_OK: serializers.FindingMetaSerializer,
- status.HTTP_404_NOT_FOUND: "Returned if finding does not exist",
- status.HTTP_400_BAD_REQUEST: "Returned if there was a problem with the metadata information",
- },
- methods=["put"],
- manual_parameters=[
- openapi.Parameter(
- name="name",
- in_=openapi.IN_QUERY,
- required=True,
- type=openapi.TYPE_STRING,
- description="name of the metadata to edit",
- )
- ],
- request_body=serializers.FindingMetaSerializer,
- )
- @swagger_auto_schema(
- responses={
- status.HTTP_200_OK: serializers.FindingMetaSerializer,
- status.HTTP_404_NOT_FOUND: "Returned if finding does not exist",
- status.HTTP_400_BAD_REQUEST: "Returned if there was a problem with the metadata information",
- },
- methods=["post"],
- request_body=serializers.FindingMetaSerializer,
- )
@action(
detail=True,
methods=["post", "put", "delete", "get"],
@@ -1759,10 +1499,7 @@ class JiraIssuesViewSet(
"engagement",
"finding_group",
]
- swagger_schema = prefetch.get_prefetch_schema(
- ["jira_finding_mappings_list", "jira_finding_mappings_read"],
- serializers.JIRAIssueSerializer,
- ).to_schema()
+
permission_classes = (
IsAuthenticated,
permissions.UserHasJiraIssuePermission,
@@ -1790,10 +1527,7 @@ class JiraProjectViewSet(
"enable_engagement_epic_mapping",
"push_notes",
]
- swagger_schema = prefetch.get_prefetch_schema(
- ["jira_projects_list", "jira_projects_read"],
- serializers.JIRAProjectSerializer,
- ).to_schema()
+
permission_classes = (
IsAuthenticated,
permissions.UserHasJiraProductPermission,
@@ -1846,13 +1580,6 @@ class ProductAPIScanConfigurationViewSet(
"service_key_2",
"service_key_3",
]
- swagger_schema = prefetch.get_prefetch_schema(
- [
- "product_api_scan_configurations_list",
- "product_api_scan_configurations_read",
- ],
- serializers.ProductAPIScanConfigurationSerializer,
- ).to_schema()
permission_classes = (
IsAuthenticated,
permissions.UserHasProductAPIScanConfigurationPermission,
@@ -1907,34 +1634,14 @@ class DojoMetaViewSet(
IsAuthenticated,
permissions.UserHasDojoMetaPermission,
)
- swagger_schema = prefetch.get_prefetch_schema(
- ["metadata_list", "metadata_read"], serializers.MetaSerializer
- ).to_schema()
+ # swagger_schema = prefetch.get_prefetch_schema(
+ # ["metadata_list", "metadata_read"], serializers.MetaSerializer
+ # ).to_schema()
def get_queryset(self):
return get_authorized_dojo_meta(Permissions.Product_View)
-# Authorization: object-based
-class DjangoFilterDescriptionInspector(CoreAPICompatInspector):
- def get_filter_parameters(self, filter_backend):
- if isinstance(filter_backend, DjangoFilterBackend):
- result = super(
- DjangoFilterDescriptionInspector, self
- ).get_filter_parameters(filter_backend)
- for param in result:
- if not param.get("description", ""):
- param.description = (
- "Filter the returned list by {field_name}".format(
- field_name=param.name
- )
- )
-
- return result
-
- return NotHandled
-
-
@extend_schema_view(
list=extend_schema(
parameters=[
@@ -1959,12 +1666,6 @@ def get_filter_parameters(self, filter_backend):
],
),
)
-@method_decorator(
- name="list",
- decorator=swagger_auto_schema(
- filter_inspectors=[DjangoFilterDescriptionInspector]
- ),
-)
class ProductViewSet(
prefetch.PrefetchListMixin,
prefetch.PrefetchRetrieveMixin,
@@ -1980,9 +1681,6 @@ class ProductViewSet(
filter_backends = (DjangoFilterBackend,)
filterset_class = ApiProductFilter
- swagger_schema = prefetch.get_prefetch_schema(
- ["products_list", "products_read"], serializers.ProductSerializer
- ).to_schema()
permission_classes = (
IsAuthenticated,
permissions.UserHasProductPermission,
@@ -2011,10 +1709,6 @@ def destroy(self, request, *args, **kwargs):
request=serializers.ReportGenerateOptionSerializer,
responses={status.HTTP_200_OK: serializers.ReportGenerateSerializer},
)
- @swagger_auto_schema(
- request_body=serializers.ReportGenerateOptionSerializer,
- responses={status.HTTP_200_OK: serializers.ReportGenerateSerializer},
- )
@action(
detail=True, methods=["post"], permission_classes=[IsAuthenticated]
)
@@ -2081,10 +1775,6 @@ class ProductMemberViewSet(
queryset = Product_Member.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_fields = ["id", "product_id", "user_id"]
- swagger_schema = prefetch.get_prefetch_schema(
- ["product_members_list", "product_members_read"],
- serializers.ProductMemberSerializer,
- ).to_schema()
permission_classes = (
IsAuthenticated,
permissions.UserHasProductMemberPermission,
@@ -2098,9 +1788,6 @@ def get_queryset(self):
@extend_schema(
exclude=True
)
- @swagger_auto_schema(
- auto_schema=None
- )
def partial_update(self, request, pk=None):
# Object authorization won't work if not all data is provided
response = {"message": "Patch function is not offered in this path."}
@@ -2139,10 +1826,6 @@ class ProductGroupViewSet(
queryset = Product_Group.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_fields = ["id", "product_id", "group_id"]
- swagger_schema = prefetch.get_prefetch_schema(
- ["product_groups_list", "product_groups_read"],
- serializers.ProductGroupSerializer,
- ).to_schema()
permission_classes = (
IsAuthenticated,
permissions.UserHasProductGroupPermission,
@@ -2156,9 +1839,6 @@ def get_queryset(self):
@extend_schema(
exclude=True
)
- @swagger_auto_schema(
- auto_schema=None
- )
def partial_update(self, request, pk=None):
# Object authorization won't work if not all data is provided
response = {"message": "Patch function is not offered in this path."}
@@ -2204,10 +1884,6 @@ class ProductTypeViewSet(
"created",
"updated",
]
- swagger_schema = prefetch.get_prefetch_schema(
- ["product_types_list", "product_types_read"],
- serializers.ProductTypeSerializer,
- ).to_schema()
permission_classes = (
IsAuthenticated,
permissions.UserHasProductTypePermission,
@@ -2243,10 +1919,6 @@ def destroy(self, request, *args, **kwargs):
request=serializers.ReportGenerateOptionSerializer,
responses={status.HTTP_200_OK: serializers.ReportGenerateSerializer},
)
- @swagger_auto_schema(
- request_body=serializers.ReportGenerateOptionSerializer,
- responses={status.HTTP_200_OK: serializers.ReportGenerateSerializer},
- )
@action(
detail=True, methods=["post"], permission_classes=[IsAuthenticated]
)
@@ -2313,10 +1985,6 @@ class ProductTypeMemberViewSet(
queryset = Product_Type_Member.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_fields = ["id", "product_type_id", "user_id"]
- swagger_schema = prefetch.get_prefetch_schema(
- ["product_type_members_list", "product_type_members_read"],
- serializers.ProductTypeMemberSerializer,
- ).to_schema()
permission_classes = (
IsAuthenticated,
permissions.UserHasProductTypeMemberPermission,
@@ -2344,9 +2012,6 @@ def destroy(self, request, *args, **kwargs):
@extend_schema(
exclude=True
)
- @swagger_auto_schema(
- auto_schema=None
- )
def partial_update(self, request, pk=None):
# Object authorization won't work if not all data is provided
response = {"message": "Patch function is not offered in this path."}
@@ -2385,10 +2050,6 @@ class ProductTypeGroupViewSet(
queryset = Product_Type_Group.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_fields = ["id", "product_type_id", "group_id"]
- swagger_schema = prefetch.get_prefetch_schema(
- ["product_type_groups_list", "product_type_groups_read"],
- serializers.ProductTypeGroupSerializer,
- ).to_schema()
permission_classes = (
IsAuthenticated,
permissions.UserHasProductTypeGroupPermission,
@@ -2402,9 +2063,6 @@ def get_queryset(self):
@extend_schema(
exclude=True
)
- @swagger_auto_schema(
- auto_schema=None
- )
def partial_update(self, request, pk=None):
# Object authorization won't work if not all data is provided
response = {"message": "Patch function is not offered in this path."}
@@ -2419,10 +2077,6 @@ class StubFindingsViewSet(
queryset = Stub_Finding.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_fields = ["id", "title", "date", "severity", "description"]
- swagger_schema = prefetch.get_prefetch_schema(
- ["stub_findings_list", "stub_findings_read"],
- serializers.StubFindingSerializer,
- ).to_schema()
permission_classes = (
IsAuthenticated,
permissions.UserHasFindingPermission,
@@ -2459,9 +2113,6 @@ class TestsViewSet(
queryset = Test.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_class = ApiTestFilter
- swagger_schema = prefetch.get_prefetch_schema(
- ["tests_list", "tests_read"], serializers.TestSerializer
- ).to_schema()
permission_classes = (IsAuthenticated, permissions.UserHasTestPermission)
@property
@@ -2496,10 +2147,6 @@ def get_serializer_class(self):
request=serializers.ReportGenerateOptionSerializer,
responses={status.HTTP_200_OK: serializers.ReportGenerateSerializer},
)
- @swagger_auto_schema(
- request_body=serializers.ReportGenerateOptionSerializer,
- responses={status.HTTP_200_OK: serializers.ReportGenerateSerializer},
- )
@action(
detail=True, methods=["post"], permission_classes=[IsAuthenticated]
)
@@ -2542,15 +2189,6 @@ def generate_report(self, request, pk=None):
request=serializers.AddNewNoteOptionSerializer,
responses={status.HTTP_201_CREATED: serializers.NoteSerializer},
)
- @swagger_auto_schema(
- method="get",
- responses={status.HTTP_200_OK: serializers.TestToNotesSerializer},
- )
- @swagger_auto_schema(
- methods=["post"],
- request_body=serializers.AddNewNoteOptionSerializer,
- responses={status.HTTP_201_CREATED: serializers.NoteSerializer},
- )
@action(detail=True, methods=["get", "post"])
def notes(self, request, pk=None):
test = self.get_object()
@@ -2599,15 +2237,6 @@ def notes(self, request, pk=None):
request=serializers.AddNewFileOptionSerializer,
responses={status.HTTP_201_CREATED: serializers.FileSerializer},
)
- @swagger_auto_schema(
- method="get",
- responses={status.HTTP_200_OK: serializers.TestToFilesSerializer},
- )
- @swagger_auto_schema(
- method="post",
- request_body=serializers.AddNewFileOptionSerializer,
- responses={status.HTTP_201_CREATED: serializers.FileSerializer},
- )
@action(
detail=True, methods=["get", "post"], parser_classes=(MultiPartParser,)
)
@@ -2644,12 +2273,6 @@ def files(self, request, pk=None):
status.HTTP_200_OK: serializers.RawFileSerializer,
},
)
- @swagger_auto_schema(
- method="get",
- responses={
- status.HTTP_200_OK: serializers.RawFileSerializer,
- },
- )
@action(
detail=True,
methods=["get"],
@@ -2740,10 +2363,6 @@ class TestImportViewSet(
"test_import_finding_action__finding",
"test_import_finding_action__created",
]
- swagger_schema = prefetch.get_prefetch_schema(
- ["test_imports_list", "test_imports_read"],
- serializers.TestImportSerializer,
- ).to_schema()
permission_classes = (
IsAuthenticated,
permissions.UserHasTestImportPermission,
@@ -2798,10 +2417,6 @@ class ToolConfigurationsViewSet(
"url",
"authentication_type",
]
- swagger_schema = prefetch.get_prefetch_schema(
- ["tool_configurations_list", "tool_configurations_read"],
- serializers.ToolConfigurationSerializer,
- ).to_schema()
permission_classes = (permissions.UserHasConfigurationPermissionSuperuser,)
@@ -2820,10 +2435,6 @@ class ToolProductSettingsViewSet(
"tool_project_id",
"url",
]
- swagger_schema = prefetch.get_prefetch_schema(
- ["tool_configurations_list", "tool_configurations_read"],
- serializers.ToolConfigurationSerializer,
- ).to_schema()
permission_classes = (
IsAuthenticated,
permissions.UserHasToolProductSettingsPermission,
@@ -2914,10 +2525,6 @@ class UserContactInfoViewSet(
):
serializer_class = serializers.UserContactInfoSerializer
queryset = UserContactInfo.objects.all()
- swagger_schema = prefetch.get_prefetch_schema(
- ["user_contact_infos_list", "user_contact_infos_read"],
- serializers.UserContactInfoSerializer,
- ).to_schema()
filter_backends = (DjangoFilterBackend,)
filterset_fields = "__all__"
permission_classes = (permissions.IsSuperUser, DjangoModelPermissions)
@@ -2929,10 +2536,6 @@ class UserProfileView(GenericAPIView):
pagination_class = None
serializer_class = serializers.UserProfileSerializer
- @swagger_auto_schema(
- method="get",
- responses={status.HTTP_200_OK: serializers.UserProfileSerializer},
- )
@action(
detail=True, methods=["get"], filter_backends=[], pagination_class=None
)
@@ -3110,9 +2713,6 @@ class LanguageViewSet(
queryset = Languages.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_fields = ["id", "language", "product"]
- swagger_schema = prefetch.get_prefetch_schema(
- ["languages_list", "languages_read"], serializers.LanguageSerializer
- ).to_schema()
permission_classes = (
IsAuthenticated,
permissions.UserHasLanguagePermission,
@@ -3592,10 +3192,6 @@ class NotificationsViewSet(
filter_backends = (DjangoFilterBackend,)
filterset_fields = ["id", "user", "product", "template"]
permission_classes = (permissions.IsSuperUser, DjangoModelPermissions)
- swagger_schema = prefetch.get_prefetch_schema(
- ["notifications_list", "notifications_read"],
- serializers.NotificationsSerializer,
- ).to_schema()
class EngagementPresetsViewset(
@@ -3605,10 +3201,6 @@ class EngagementPresetsViewset(
queryset = Engagement_Presets.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_fields = ["id", "title", "product"]
- swagger_schema = prefetch.get_prefetch_schema(
- ["engagement_presets_list", "engagement_presets_read"],
- serializers.EngagementPresetsSerializer,
- ).to_schema()
permission_classes = (
IsAuthenticated,
permissions.UserHasEngagementPresetPermission,
@@ -3624,10 +3216,6 @@ class EngagementCheckListViewset(
serializer_class = serializers.EngagementCheckListSerializer
queryset = Check_List.objects.none()
filter_backends = (DjangoFilterBackend,)
- swagger_schema = prefetch.get_prefetch_schema(
- ["engagement_checklists_list", "engagement_checklists_read"],
- serializers.EngagementCheckListSerializer,
- ).to_schema()
permission_classes = (
IsAuthenticated,
permissions.UserHasEngagementPermission,
@@ -3731,13 +3319,6 @@ class QuestionnaireAnsweredSurveyViewSet(
permissions.UserHasEngagementPermission,
DjangoModelPermissions,
)
- swagger_schema = prefetch.get_prefetch_schema(
- [
- "questionnaire_answered_questionnaires_list",
- "questionnaire_answered_questionnaires_read",
- ],
- serializers.QuestionnaireAnsweredSurveySerializer,
- ).to_schema()
# Authorization: configuration
diff --git a/dojo/risk_acceptance/api.py b/dojo/risk_acceptance/api.py
index d862453a2fc..b23a0d1dfc6 100644
--- a/dojo/risk_acceptance/api.py
+++ b/dojo/risk_acceptance/api.py
@@ -7,7 +7,6 @@
from rest_framework.decorators import action
from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response
-from drf_yasg.utils import swagger_auto_schema
from dojo.api_v2.serializers import RiskAcceptanceSerializer
from dojo.models import Risk_Acceptance, User, Vulnerability_Id
@@ -38,10 +37,6 @@ class AcceptedRisksMixin(ABC):
def risk_application_model_class(self):
pass
- @swagger_auto_schema(
- request_body=AcceptedRiskSerializer(many=True),
- responses={status.HTTP_201_CREATED: RiskAcceptanceSerializer(many=True)},
- )
@extend_schema(
request=AcceptedRiskSerializer(many=True),
responses={status.HTTP_201_CREATED: RiskAcceptanceSerializer(many=True)},
@@ -65,10 +60,6 @@ def accept_risks(self, request, pk=None):
class AcceptedFindingsMixin(ABC):
- @swagger_auto_schema(
- request_body=AcceptedRiskSerializer(many=True),
- responses={status.HTTP_201_CREATED: RiskAcceptanceSerializer(many=True)},
- )
@extend_schema(
request=AcceptedRiskSerializer(many=True),
responses={status.HTTP_201_CREATED: RiskAcceptanceSerializer(many=True)},
diff --git a/dojo/settings/settings.dist.py b/dojo/settings/settings.dist.py
index fad2454b7ca..85ea3a30319 100644
--- a/dojo/settings/settings.dist.py
+++ b/dojo/settings/settings.dist.py
@@ -758,29 +758,6 @@ def generate_url(scheme, double_slashes, user, password, host, port, path, param
if API_TOKENS_ENABLED:
REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] += ('rest_framework.authentication.TokenAuthentication',)
-SWAGGER_SETTINGS = {
- 'SECURITY_DEFINITIONS': {
- 'basicAuth': {
- 'type': 'basic'
- },
- 'cookieAuth': {
- 'type': 'apiKey',
- 'in': 'cookie',
- 'name': 'sessionid'
- },
- },
- 'DOC_EXPANSION': "none",
- 'JSON_EDITOR': True,
- 'SHOW_REQUEST_HEADERS': True,
-}
-
-if API_TOKENS_ENABLED:
- SWAGGER_SETTINGS['SECURITY_DEFINITIONS']['tokenAuth'] = {
- 'type': 'apiKey',
- 'in': 'header',
- 'name': 'Authorization'
- }
-
SPECTACULAR_SETTINGS = {
'TITLE': 'Defect Dojo API v2',
'DESCRIPTION': 'Defect Dojo - Open Source vulnerability Management made easy. Prefetch related parameters/responses not yet in the schema.',
@@ -854,7 +831,6 @@ def generate_url(scheme, double_slashes, user, password, host, port, path, param
'dbbackup',
'django_celery_results',
'social_django',
- 'drf_yasg',
'drf_spectacular',
'drf_spectacular_sidecar', # required for Django collectstatic discovery
'tagulous',
@@ -1088,11 +1064,6 @@ def saml2_attrib_map_format(dict):
('dojo.remote_user.RemoteUserAuthentication',) + \
REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES']
- SWAGGER_SETTINGS['SECURITY_DEFINITIONS']['remoteUserAuth'] = {
- 'type': 'apiKey',
- 'in': 'header',
- 'name': AUTH_REMOTEUSER_USERNAME_HEADER[5:].replace('_', '-')
- }
# ------------------------------------------------------------------------------
# CELERY
# ------------------------------------------------------------------------------
@@ -1725,6 +1696,5 @@ def saml2_attrib_map_format(dict):
from django.utils.deprecation import RemovedInDjango50Warning
warnings.filterwarnings("ignore", category=RemovedInDjango50Warning)
warnings.filterwarnings("ignore", message="invalid escape sequence.*")
- warnings.filterwarnings("ignore", message="'cgi' is deprecated and slated for removal in Python 3\\.13")
warnings.filterwarnings("ignore", message="DateTimeField .+ received a naive datetime .+ while time zone support is active\\.")
warnings.filterwarnings("ignore", message="unclosed file .+")
diff --git a/dojo/urls.py b/dojo/urls.py
index fa15f977da9..4500e1e49dd 100755
--- a/dojo/urls.py
+++ b/dojo/urls.py
@@ -4,9 +4,6 @@
from django.contrib import admin
from rest_framework.routers import DefaultRouter
from rest_framework.authtoken import views as tokenviews
-from rest_framework import permissions
-from drf_yasg.views import get_schema_view
-from drf_yasg import openapi
from django.http import HttpResponse
from dojo import views
from dojo.api_v2.views import EndPointViewSet, EngagementViewSet, \
@@ -183,20 +180,6 @@
)
]
-schema_view = get_schema_view(
- openapi.Info(
- title="Defect Dojo API",
- default_version='v2',
- description="To use the API you need be authorized.\n\n## Deprecated - Removal in v2.30.0\n#### Please use the [OpenAPI3 version](/api/v2/oa3/swagger-ui/)",
- ),
- # if public=False, includes only endpoints the current user has access to
- public=True,
- # The API of a OpenSource project should be public accessible
- permission_classes=[permissions.AllowAny],
- # url pattersns specific to the API
- patterns=api_v2_urls,
-)
-
urlpatterns = []
# sometimes urlpatterns needed be added from local_settings.py before other URLs of core dojo
@@ -208,9 +191,6 @@
re_path(r'^%shistory/(?P\d+)/(?P\d+)$' % get_system_setting('url_prefix'), views.action_history, name='action_history'),
re_path(r'^%s' % get_system_setting('url_prefix'), include(ur)),
- # drf-yasg = OpenAPI2
- re_path(r'^%sapi/v2/doc/' % get_system_setting('url_prefix'), schema_view.with_ui('swagger', cache_timeout=0), name='api_v2_schema'),
-
# drf-spectacular = OpenAPI3
re_path(r'^%sapi/v2/oa3/schema/' % get_system_setting('url_prefix'), SpectacularAPIView.as_view(), name='schema_oa3'),
re_path(r'^%sapi/v2/oa3/swagger-ui/' % get_system_setting('url_prefix'), SpectacularSwaggerView.as_view(url=get_system_setting('url_prefix') + '/api/v2/oa3/schema/?format=json'), name='swagger-ui_oa3'),
diff --git a/requirements.txt b/requirements.txt
index e7821926bba..597607d7497 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -56,7 +56,6 @@ Python-jose==3.3.0
gitpython==3.1.41
debugpy==1.8.0
python-gitlab==4.4.0
-drf_yasg==1.21.5
cpe==1.2.1
packageurl-python==0.13.4
django-crum==0.7.9
diff --git a/unittests/test_apiv2_metadata.py b/unittests/test_apiv2_metadata.py
index 3e39dc2bbc0..6da260ec32e 100644
--- a/unittests/test_apiv2_metadata.py
+++ b/unittests/test_apiv2_metadata.py
@@ -26,7 +26,7 @@ def create(self, **kwargs):
return self.client.post(reverse('metadata-list'), kwargs, format='json')
def test_docs(self):
- r = self.client.get(reverse('api_v2_schema'))
+ r = self.client.get(reverse('swagger-ui_oa3'))
self.assertEqual(r.status_code, 200)
def test_query_metadata(self):
diff --git a/unittests/test_swagger_schema.py b/unittests/test_swagger_schema.py
deleted file mode 100644
index b1263359374..00000000000
--- a/unittests/test_swagger_schema.py
+++ /dev/null
@@ -1,835 +0,0 @@
-from django.test import tag
-from rest_framework.test import APIRequestFactory
-from rest_framework.views import APIView
-from rest_framework.test import APITestCase, force_authenticate, APIClient
-from rest_framework.mixins import \
- RetrieveModelMixin, ListModelMixin, CreateModelMixin, UpdateModelMixin
-from rest_framework import status
-from drf_yasg.generators import OpenAPISchemaGenerator
-from drf_yasg.openapi import Info, SchemaRef
-from drf_yasg.openapi import \
- TYPE_ARRAY, TYPE_BOOLEAN, TYPE_INTEGER, TYPE_NUMBER, TYPE_OBJECT, TYPE_STRING
-from collections import OrderedDict
-
-from dojo.api_v2.views import \
- DevelopmentEnvironmentViewSet, EndpointStatusViewSet, EndPointViewSet, \
- EngagementViewSet, FindingTemplatesViewSet, FindingViewSet, \
- JiraInstanceViewSet, DojoMetaViewSet, NoteTypeViewSet, NotesViewSet, \
- ProductTypeViewSet, ProductViewSet, RegulationsViewSet, \
- SonarqubeIssueViewSet, ProductAPIScanConfigurationViewSet, \
- SonarqubeIssueTransitionViewSet, StubFindingsViewSet, SystemSettingsViewSet, \
- TestTypesViewSet, TestsViewSet, ToolConfigurationsViewSet, ToolProductSettingsViewSet, \
- ToolTypesViewSet, UsersViewSet, JiraIssuesViewSet, JiraProjectViewSet, AppAnalysisViewSet, \
- LanguageTypeViewSet, LanguageViewSet, AnnouncementViewSet
-
-from dojo.models import \
- Development_Environment, Endpoint_Status, Endpoint, Engagement, Finding_Template, \
- Finding, JIRA_Instance, JIRA_Issue, DojoMeta, Note_Type, Notes, Product_Type, Product, Regulation, \
- Sonarqube_Issue, Product_API_Scan_Configuration, Sonarqube_Issue_Transition, \
- Stub_Finding, System_Settings, Test_Type, Test, Tool_Configuration, Tool_Product_Settings, \
- Tool_Type, Dojo_User, JIRA_Project, App_Analysis, Language_Type, Languages, Announcement
-
-from dojo.api_v2.serializers import \
- DevelopmentEnvironmentSerializer, EndpointStatusSerializer, EndpointSerializer, \
- EngagementSerializer, FindingTemplateSerializer, FindingSerializer, \
- JIRAInstanceSerializer, JIRAIssueSerializer, JIRAProjectSerializer, MetaSerializer, NoteTypeSerializer, \
- ProductSerializer, RegulationSerializer, \
- SonarqubeIssueSerializer, ProductAPIScanConfigurationSerializer, SonarqubeIssueTransitionSerializer, \
- StubFindingSerializer, SystemSettingsSerializer, TestTypeSerializer, TestSerializer, ToolConfigurationSerializer, \
- ToolProductSettingsSerializer, ToolTypeSerializer, UserSerializer, NoteSerializer, ProductTypeSerializer, \
- AppAnalysisSerializer, LanguageTypeSerializer, LanguageSerializer, AnnouncementSerializer
-
-SWAGGER_SCHEMA_GENERATOR = OpenAPISchemaGenerator(Info("defectdojo", "v2"))
-BASE_API_URL = "/api/v2"
-
-
-def testIsBroken(method):
- return tag("broken")(method)
-
-
-def skipIfNotSubclass(baseclass):
- def decorate(f):
- def wrapper(self, *args, **kwargs):
- if not issubclass(self.viewset, baseclass):
- self.skipTest('This view is not %s' % baseclass)
- else:
- f(self, *args, **kwargs)
- return wrapper
- return decorate
-
-
-def check_response_valid(expected_code, response):
- def _data_to_str(response):
- if hasattr(response, "data"):
- return response.data
- return None
-
- assert response.status_code == expected_code, \
- f"Response invalid, returned with code {response.status_code}\nResponse Data:\n{_data_to_str(response)}"
-
-
-def format_url(path):
- return f"{BASE_API_URL}{path}"
-
-
-class SchemaChecker():
- def __init__(self, definitions):
- self._prefix = []
- self._has_failed = False
- self._definitions = definitions
- self._errors = []
-
- def _register_error(self, error):
- self._errors += [error]
-
- def _check_or_fail(self, condition, message):
- if not condition:
- self._has_failed = True
- self._register_error(message)
-
- def _get_prefix(self):
- return '#'.join(self._prefix)
-
- def _push_prefix(self, prefix):
- self._prefix += [prefix]
-
- def _pop_prefix(self):
- self._prefix = self._prefix if len(self._prefix) == 0 else self._prefix[:-1]
-
- def _resolve_if_ref(self, schema):
- if type(schema) is not SchemaRef:
- return schema
-
- ref_name = schema["$ref"]
- ref_name = ref_name[ref_name.rfind("/") + 1:]
- return self._definitions[ref_name]
-
- def _check_has_required_fields(self, required_fields, obj):
- for required_field in required_fields:
- # passwords are writeOnly, but this is not supported by Swagger / OpenAPIv2
- if required_field != 'password':
- field = f"{self._get_prefix()}#{required_field}"
- self._check_or_fail(obj is not None and required_field in obj, f"{field} is required but was not returned")
-
- def _check_type(self, schema, obj):
- schema_type = schema["type"]
- is_nullable = schema.get("x-nullable", False) or schema.get("readOnly", False)
-
- def _check_helper(check):
- self._check_or_fail(check, f"{self._get_prefix()} should be of type {schema_type} but value was of type {type(obj)}")
-
- if obj is None:
- self._check_or_fail(is_nullable, f"{self._get_prefix()} is not nullable yet the value returned was null")
- elif schema_type is TYPE_BOOLEAN:
- _check_helper(isinstance(obj, bool))
- elif schema_type is TYPE_INTEGER:
- _check_helper(isinstance(obj, int))
- elif schema_type is TYPE_NUMBER:
- _check_helper(obj.isdecimal())
- elif schema_type is TYPE_ARRAY:
- _check_helper(isinstance(obj, list))
- elif schema_type is TYPE_OBJECT:
- _check_helper(isinstance(obj, OrderedDict) or isinstance(obj, dict))
- elif schema_type is TYPE_STRING:
- _check_helper(isinstance(obj, str))
- else:
- # Default case
- _check_helper(False)
-
- def _with_prefix(self, prefix, callable, *args):
- self._push_prefix(prefix)
- callable(*args)
- self._pop_prefix()
-
- def check(self, schema, obj):
- def _check(schema, obj):
- schema = self._resolve_if_ref(schema)
- self._check_type(schema, obj)
-
- required_fields = schema.get("required", [])
- self._check_has_required_fields(required_fields, obj)
-
- if obj is None:
- return
-
- properties = schema.get("properties", None)
- if properties is not None:
- for name, prop in properties.items():
- # print('property: ', name)
- # print('obj ', obj)
- obj_child = obj.get(name, None)
- if obj_child is not None:
- self._with_prefix(name, _check, prop, obj_child)
-
- for child_name in obj.keys():
- # TODO prefetch mixins not picked up by spectcular?
- if child_name not in ['prefetch']:
- if not properties or child_name not in properties.keys():
- self._has_failed = True
- self._register_error(f'unexpected property "{child_name}" found')
-
- additional_properties = schema.get("additionalProperties", None)
- if additional_properties is not None:
- for name, obj_child in obj.items():
- self._with_prefix(f"additionalProp<{name}>", _check, additional_properties, obj_child)
-
- if schema["type"] is TYPE_ARRAY:
- items_schema = schema["items"]
- for index in range(len(obj)):
- self._with_prefix(f"item{index}", _check, items_schema, obj[index])
-
- self._has_failed = False
- self._errors = []
- self._prefix = []
- _check(schema, obj)
- assert not self._has_failed, "\n" + '\n'.join(self._errors) + "\nFailed with " + str(len(self._errors)) + " errors"
-
-
-class BaseClass():
- class SchemaTest(APITestCase):
- fixtures = ['dojo_testdata.json']
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewset = None
- self.viewname = None
- self.model = None
- self.serializer = None
- self.field_transformers = dict()
-
- def setUp(self):
- super().setUp()
- testuser = Dojo_User.objects.get(username='admin')
-
- factory = APIRequestFactory()
- request = factory.get('/')
- force_authenticate(request, user=testuser)
- request = APIView().initialize_request(request)
-
- self.schema = SWAGGER_SCHEMA_GENERATOR.get_schema(request, public=True)
- self.client = APIClient()
- self.client.force_authenticate(user=testuser)
-
- def check_schema(self, schema, obj):
- schema_checker = SchemaChecker(self.schema["definitions"])
- # print(vars(schema_checker))
- schema_checker.check(schema, obj)
-
- def get_valid_object_id(self):
- response = self.client.get(format_url(f"/{self.viewname}/"))
- check_response_valid(status.HTTP_200_OK, response)
- if len(response.data["results"]) == 0:
- return None
-
- return response.data["results"][0].get('id', None)
-
- def get_endpoint_schema(self, path, method):
- paths = self.schema["paths"]
- methods = paths.get(path, None)
- assert methods is not None, f"{path} not found in {[path for path in paths.keys()]}"
-
- endpoint = methods.get(method, None)
- assert endpoint is not None, f"Method {method} not found in {[method for method in methods.keys()]}"
-
- return endpoint
-
- def construct_response_data(self, obj_id):
- obj = self.model.objects.get(id=obj_id)
- request = APIView().initialize_request(APIRequestFactory().request())
- serialized_obj = self.serializer(context={"request": request}).to_representation(obj)
-
- for name, transformer in self.field_transformers.items():
- serialized_obj[name] = transformer(serialized_obj[name])
-
- return serialized_obj
-
- @skipIfNotSubclass(ListModelMixin)
- def test_list_endpoint(self, extra_args=None):
- endpoints = self.schema["paths"][f"/{self.viewname}/"]
- response = self.client.get(format_url(f"/{self.viewname}/"), extra_args)
- check_response_valid(status.HTTP_200_OK, response)
-
- schema = endpoints['get']['responses']['200']['schema']
- obj = response.data
-
- self.check_schema(schema, obj)
-
- @skipIfNotSubclass(RetrieveModelMixin)
- def test_retrieve_endpoint(self, extra_args=None):
- endpoints = self.schema["paths"][f"/{self.viewname}/{{id}}/"]
- response = self.client.get(format_url(f"/{self.viewname}/"))
- check_response_valid(status.HTTP_200_OK, response)
- ids = [obj['id'] for obj in response.data["results"]]
-
- schema = endpoints['get']['responses']['200']['schema']
- for id in ids:
- print('id:', id)
- response = self.client.get(format_url(f"/{self.viewname}/{id}/"), extra_args)
- print('response type:', type(response))
- print('response data:', response.data)
- check_response_valid(status.HTTP_200_OK, response)
- obj = response.data
- self.check_schema(schema, obj)
-
- @skipIfNotSubclass(UpdateModelMixin)
- def test_patch_endpoint(self, extra_args=None):
- operation = self.schema["paths"][f"/{self.viewname}/{{id}}/"]["patch"]
-
- id = self.get_valid_object_id()
- if id is None:
- self.skipTest("No data exists to test endpoint")
-
- data = self.construct_response_data(id)
-
- schema = operation['responses']['200']['schema']
- response = self.client.patch(format_url(f"/{self.viewname}/{id}/"), data, format='json')
- check_response_valid(status.HTTP_200_OK, response)
-
- obj = response.data
- self.check_schema(schema, obj)
-
- @skipIfNotSubclass(UpdateModelMixin)
- def test_put_endpoint(self, extra_data={}, extra_args=None):
- operation = self.schema["paths"][f"/{self.viewname}/{{id}}/"]['put']
-
- id = self.get_valid_object_id()
- if id is None:
- self.skipTest("No data exists to test endpoint")
-
- data = self.construct_response_data(id)
- data.update(extra_data)
-
- schema = operation['responses']['200']['schema']
- response = self.client.put(format_url(f"/{self.viewname}/{id}/"), data, format='json')
- check_response_valid(status.HTTP_200_OK, response)
-
- obj = response.data
- self.check_schema(schema, obj)
-
- @skipIfNotSubclass(CreateModelMixin)
- def test_post_endpoint(self, extra_data=[], extra_args=None):
- operation = self.schema["paths"][f"/{self.viewname}/"]["post"]
-
- id = self.get_valid_object_id()
- if id is None:
- self.skipTest("No data exists to test endpoint")
-
- data = self.construct_response_data(id)
- data.update(extra_data)
-
- print('data:', data)
-
- schema = operation['responses']['201']['schema']
- response = self.client.post(format_url(f"/{self.viewname}/"), data, format='json')
- check_response_valid(status.HTTP_201_CREATED, response)
-
- print('response.data:', response.data)
-
- obj = response.data
- self.check_schema(schema, obj)
-
-
-class DevelopmentEnvironmentTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "development_environments"
- self.viewset = DevelopmentEnvironmentViewSet
- self.model = Development_Environment
- self.serializer = DevelopmentEnvironmentSerializer
-
-
-# Test will only work when FEATURE_AUTHENTICATION_V2 is the default
-# class DojoGroupTest(BaseClass.SchemaTest):
-# def __init__(self, *args, **kwargs):
-# super().__init__(*args, **kwargs)
-# self.viewname = "group"
-# self.viewset = DojoGroupViewSet
-# self.model = Dojo_Group
-# self.serializer = DojoGroupSerializer
-
-
-class EndpointStatusTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "endpoint_status"
- self.viewset = EndpointStatusViewSet
- self.model = Endpoint_Status
- self.serializer = EndpointStatusSerializer
-
- # We can not simulate creating of the endpoint-finding relation with the same parameters as existing one. We will use another finding for this case
- def test_post_endpoint(self):
- super().test_post_endpoint(extra_data={"finding": "3"})
-
-
-class EndpointTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "endpoints"
- self.viewset = EndPointViewSet
- self.model = Endpoint
- self.serializer = EndpointSerializer
- self.field_transformers = {
- "path": lambda v: (v if v else '') + "transformed/"
- }
-
-
-class EngagementTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "engagements"
- self.viewset = EngagementViewSet
- self.model = Engagement
- self.serializer = EngagementSerializer
-
- # @testIsBroken
- # fixed
- def test_accept_risks(self):
- operation = self.get_endpoint_schema("/engagements/{id}/accept_risks/", "post")
- schema = operation['responses']['201']['schema']
- print(schema)
- id = self.get_valid_object_id()
- if id is None:
- self.skipTest("No data exists to test endpoint")
-
- data = [
- {
- "vulnerability_id": 1,
- "justification": "test",
- "accepted_by": "2"
- }
- ]
-
- response = self.client.post(format_url(f"/engagements/{id}/accept_risks/"), data, format='json')
- check_response_valid(201, response)
- print('response.data')
- # print(vars(response))
- print(response.content)
- obj = response.data
- self.check_schema(schema, obj)
-
- # fixed
- def test_notes_read(self):
- operation = self.get_endpoint_schema("/engagements/{id}/notes/", "get")
- schema = operation['responses']['200']['schema']
- id = self.get_valid_object_id()
- if id is None:
- self.skipTest("No data exists to test endpoint")
-
- response = self.client.get(format_url(f"/engagements/{id}/notes/"))
- check_response_valid(200, response)
- obj = response.data
- self.check_schema(schema, obj)
-
- # fixed
- def test_notes_create(self):
- operation = self.get_endpoint_schema("/engagements/{id}/notes/", "post")
- schema = operation['responses']['201']['schema']
- id = self.get_valid_object_id()
- if id is None:
- self.skipTest("No data exists to test endpoint")
-
- data = {
- "entry": "test",
- "author": 2,
- }
-
- response = self.client.post(format_url(f"/engagements/{id}/notes/"), data, format='json')
- check_response_valid(201, response)
- obj = response.data
- self.check_schema(schema, obj)
-
-
-class FindingTemplateTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "finding_templates"
- self.viewset = FindingTemplatesViewSet
- self.model = Finding_Template
- self.serializer = FindingTemplateSerializer
-
- # fixed
- def test_post_endpoint(self):
- super().test_post_endpoint()
-
- # fixed
- def test_patch_endpoint(self):
- super().test_patch_endpoint()
-
- # fixed
- def test_put_endpoint(self):
- super().test_put_endpoint()
-
-
-class FindingTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "findings"
- self.viewset = FindingViewSet
- self.model = Finding
- self.serializer = FindingSerializer
-
- # fixed
- def test_list_endpoint(self):
- super().test_list_endpoint({
- "related_fields": True
- })
-
- # fixed
- def test_patch_endpoint(self):
- super().test_patch_endpoint()
-
- # fixed
- def test_put_endpoint(self):
- super().test_put_endpoint()
-
- # fixed
- def test_retrieve_endpoint(self):
- super().test_retrieve_endpoint({
- "related_fields": True
- })
-
-
-class JiraInstanceTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "jira_instances"
- self.viewset = JiraInstanceViewSet
- self.model = JIRA_Instance
- self.serializer = JIRAInstanceSerializer
-
- # fixed
- def test_list_endpoint(self):
- super().test_list_endpoint()
-
- # fixed
- def test_patch_endpoint(self):
- super().test_patch_endpoint()
-
- # fixed
- def test_put_endpoint(self):
- super().test_put_endpoint(extra_data={"password": "12345"})
-
- # fixed
- def test_retrieve_endpoint(self):
- super().test_retrieve_endpoint()
-
- # fixed
- def test_post_endpoint(self):
- super().test_post_endpoint(extra_data={"password": "12345"})
-
-
-class JiraFindingMappingsTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "jira_finding_mappings"
- self.viewset = JiraIssuesViewSet
- self.model = JIRA_Issue
- self.serializer = JIRAIssueSerializer
- self.field_transformers = {
- "finding": lambda v: 3,
- }
-
-
-class JiraProjectTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "jira_projects"
- self.viewset = JiraProjectViewSet
- self.model = JIRA_Project
- self.serializer = JIRAProjectSerializer
-
-
-class MetadataTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "metadata"
- self.viewset = DojoMetaViewSet
- self.model = DojoMeta
- self.serializer = MetaSerializer
-
-
-class NoteTypeTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "note_type"
- self.viewset = NoteTypeViewSet
- self.model = Note_Type
- self.serializer = NoteTypeSerializer
- self.field_transformers = {
- "name": lambda v: v + "_new"
- }
-
-
-class NoteTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "notes"
- self.viewset = NotesViewSet
- self.model = Notes
- self.serializer = NoteSerializer
-
-
-class ProductTypeTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "product_types"
- self.viewset = ProductTypeViewSet
- self.model = Product_Type
- self.serializer = ProductTypeSerializer
- self.field_transformers = {
- "name": lambda v: v + "_new"
- }
-
-
-# Test will only work when FEATURE_AUTHENTICATION_V2 is the default
-# class ProductTypeMemberTest(BaseClass.SchemaTest):
-# def __init__(self, *args, **kwargs):
-# super().__init__(*args, **kwargs)
-# self.viewname = "product_type_members"
-# self.viewset = ProductTypeMemberViewSet
-# self.model = Product_Type_Member
-# self.serializer = ProductTypeMemberSerializer
-
-
-# Test will only work when FEATURE_AUTHENTICATION_V2 is the default
-# class ProductTypeGroupTest(BaseClass.SchemaTest):
-# def __init__(self, *args, **kwargs):
-# super().__init__(*args, **kwargs)
-# self.viewname = "product_type_groups"
-# self.viewset = ProductTypeGroupViewSet
-# self.model = Product_Type_Group
-# self.serializer = ProductTypeGroupSerializer
-
-
-class ProductTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "products"
- self.viewset = ProductViewSet
- self.model = Product
- self.serializer = ProductSerializer
- self.field_transformers = {
- "name": lambda v: v + "_new"
- }
-
- # fixed
- def test_list_endpoint(self):
- super().test_list_endpoint()
-
- # fixed
- def test_patch_endpoint(self):
- super().test_patch_endpoint()
-
- # fixed
- def test_put_endpoint(self):
- super().test_put_endpoint()
-
- # fixed
- def test_retrieve_endpoint(self):
- super().test_retrieve_endpoint()
-
- # fixed
- def test_post_endpoint(self):
- super().test_post_endpoint()
-
-
-# Test will only work when FEATURE_AUTHENTICATION_V2 is the default
-# class ProductMemberTest(BaseClass.SchemaTest):
-# def __init__(self, *args, **kwargs):
-# super().__init__(*args, **kwargs)
-# self.viewname = "product_members"
-# self.viewset = ProductMemberViewSet
-# self.model = Product_Member
-# self.serializer = ProductMemberSerializer
-
-# @testIsBroken
-# def test_post_endpoint(self):
-# super().test_post_endpoint()
-
-# @testIsBroken
-# def test_patch_endpoint(self):
-# super().test_post_endpoint()
-
-
-# Test will only work when FEATURE_AUTHENTICATION_V2 is the default
-# class ProductGroupTest(BaseClass.SchemaTest):
-# def __init__(self, *args, **kwargs):
-# super().__init__(*args, **kwargs)
-# self.viewname = "product_groups"
-# self.viewset = ProductGroupViewSet
-# self.model = Product_Group
-# self.serializer = ProductGroupSerializer
-
-
-class RegulationTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "regulations"
- self.viewset = RegulationsViewSet
- self.model = Regulation
- self.serializer = RegulationSerializer
-
-
-class SonarqubeIssuesTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "sonarqube_issues"
- self.viewset = SonarqubeIssueViewSet
- self.model = Sonarqube_Issue
- self.serializer = SonarqubeIssueSerializer
- self.field_transformers = {
- "key": lambda v: v + "_new"
- }
-
-
-class ProductAPIScanConfigurationTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "product_api_scan_configurations"
- self.viewset = ProductAPIScanConfigurationViewSet
- self.model = Product_API_Scan_Configuration
- self.serializer = ProductAPIScanConfigurationSerializer
-
-
-class SonarqubeTransitionTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "sonarqube_transitions"
- self.viewset = SonarqubeIssueTransitionViewSet
- self.model = Sonarqube_Issue_Transition
- self.serializer = SonarqubeIssueTransitionSerializer
-
-
-class StubFindingTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "stub_findings"
- self.viewset = StubFindingsViewSet
- self.model = Stub_Finding
- self.serializer = StubFindingSerializer
-
-
-class SystemSettingTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "system_settings"
- self.viewset = SystemSettingsViewSet
- self.model = System_Settings
- self.serializer = SystemSettingsSerializer
-
-
-class AppAnalysisTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "technologies"
- self.viewset = AppAnalysisViewSet
- self.model = App_Analysis
- self.serializer = AppAnalysisSerializer
-
- # fixed
- def test_patch_endpoint(self):
- super().test_patch_endpoint()
-
- # fixed
- def test_put_endpoint(self):
- super().test_put_endpoint()
-
- # fixed
- def test_post_endpoint(self):
- super().test_post_endpoint()
-
-
-class TestTypeTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "test_types"
- self.viewset = TestTypesViewSet
- self.model = Test_Type
- self.serializer = TestTypeSerializer
- self.field_transformers = {
- "name": lambda v: v + "_new"
- }
-
-
-class TestsTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "tests"
- self.viewset = TestsViewSet
- self.model = Test
- self.serializer = TestSerializer
-
-
-class ToolConfigurationTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "tool_configurations"
- self.viewset = ToolConfigurationsViewSet
- self.model = Tool_Configuration
- self.serializer = ToolConfigurationSerializer
-
-
-class ToolProductSettingTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "tool_product_settings"
- self.viewset = ToolProductSettingsViewSet
- self.model = Tool_Product_Settings
- self.serializer = ToolProductSettingsSerializer
-
-
-class ToolTypeTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "tool_types"
- self.viewset = ToolTypesViewSet
- self.model = Tool_Type
- self.serializer = ToolTypeSerializer
- self.field_transformers = {
- "name": lambda v: v + "_new"
- }
-
-
-class UserTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "users"
- self.viewset = UsersViewSet
- self.model = Dojo_User
- self.serializer = UserSerializer
- self.field_transformers = {
- "username": lambda v: v + "_transformed"
- }
-
-
-class LanguageTypeTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "language_types"
- self.viewset = LanguageTypeViewSet
- self.model = Language_Type
- self.serializer = LanguageTypeSerializer
-
-
-class LanguageTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "languages"
- self.viewset = LanguageViewSet
- self.model = Languages
- self.serializer = LanguageSerializer
-
- def test_post_endpoint(self):
- super().test_post_endpoint(extra_data={"language": 2})
-
-
-class AnnouncementTest(BaseClass.SchemaTest):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.viewname = "announcements"
- self.viewset = AnnouncementViewSet
- self.model = Announcement
- self.serializer = AnnouncementSerializer
-
- def test_post_endpoint(self, extra_data=[], extra_args=None):
- self.skipTest('Only one Announcement can exists')