diff --git a/dojo/announcement/views.py b/dojo/announcement/views.py index 26160c3236b..7afe915210b 100644 --- a/dojo/announcement/views.py +++ b/dojo/announcement/views.py @@ -7,9 +7,6 @@ from django.utils.translation import gettext from django.utils.translation import gettext_lazy as _ -from dojo.authorization.authorization_decorators import ( - user_is_configuration_authorized, -) from dojo.forms import AnnouncementCreateForm, AnnouncementRemoveForm from dojo.models import Announcement, UserAnnouncement from dojo.utils import add_breadcrumb @@ -17,7 +14,6 @@ logger = logging.getLogger(__name__) -@user_is_configuration_authorized("dojo.change_announcement") def configure_announcement(request): remove = False if request.method == "GET": diff --git a/dojo/api_v2/serializers.py b/dojo/api_v2/serializers.py index 0fe27f0255c..6e85ee6a3fb 100644 --- a/dojo/api_v2/serializers.py +++ b/dojo/api_v2/serializers.py @@ -26,6 +26,15 @@ import dojo.finding.helper as finding_helper import dojo.risk_acceptance.helper as ra_helper from dojo.authorization.authorization import user_has_permission +from dojo.authorization.models import ( + Dojo_Group_Member, + Global_Role, + Product_Group, + Product_Member, + Product_Type_Group, + Product_Type_Member, + Role, +) from dojo.authorization.roles_permissions import Permissions from dojo.celery_dispatch import dojo_dispatch_task from dojo.endpoint.utils import endpoint_filter, endpoint_meta_import @@ -61,7 +70,6 @@ Cred_User, Development_Environment, Dojo_Group, - Dojo_Group_Member, Dojo_User, DojoMeta, Endpoint, @@ -75,7 +83,6 @@ Finding_Group, Finding_Template, General_Survey, - Global_Role, Language_Type, Languages, Network_Locations, @@ -86,15 +93,10 @@ Notifications, Product, Product_API_Scan_Configuration, - Product_Group, - Product_Member, Product_Type, - Product_Type_Group, - Product_Type_Member, Question, Regulation, Risk_Acceptance, - Role, SLA_Configuration, Sonarqube_Issue, Sonarqube_Issue_Transition, diff --git a/dojo/api_v2/views.py b/dojo/api_v2/views.py index 4da5a02885c..a1f73687a64 100644 --- a/dojo/api_v2/views.py +++ b/dojo/api_v2/views.py @@ -41,12 +41,21 @@ mixins as dojo_mixins, ) from dojo.api_v2 import ( - permissions, prefetch, serializers, ) from dojo.api_v2.prefetch.prefetcher import _Prefetcher +from dojo.authorization import api_permissions as permissions from dojo.authorization.authorization import user_has_permission_or_403 +from dojo.authorization.models import ( + Dojo_Group_Member, + Global_Role, + Product_Group, + Product_Member, + Product_Type_Group, + Product_Type_Member, + Role, +) from dojo.authorization.roles_permissions import Permissions from dojo.celery_dispatch import dojo_dispatch_task from dojo.cred.queries import get_authorized_cred_mappings @@ -100,7 +109,6 @@ Cred_User, Development_Environment, Dojo_Group, - Dojo_Group_Member, Dojo_User, DojoMeta, Endpoint, @@ -112,7 +120,6 @@ Finding, Finding_Template, General_Survey, - Global_Role, Language_Type, Languages, Network_Locations, @@ -123,15 +130,10 @@ Notifications, Product, Product_API_Scan_Configuration, - Product_Group, - Product_Member, Product_Type, - Product_Type_Group, - Product_Type_Member, Question, Regulation, Risk_Acceptance, - Role, SLA_Configuration, Sonarqube_Issue, Sonarqube_Issue_Transition, diff --git a/dojo/asset/api/filters.py b/dojo/asset/api/filters.py index 91281a9ebe2..b4cdfa182ae 100644 --- a/dojo/asset/api/filters.py +++ b/dojo/asset/api/filters.py @@ -3,6 +3,10 @@ from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import extend_schema_field +from dojo.authorization.models import ( + Product_Group, + Product_Member, +) from dojo.filters import ( CharFieldFilterANDExpression, CharFieldInFilter, @@ -16,8 +20,6 @@ from dojo.models import ( Product, Product_API_Scan_Configuration, - Product_Group, - Product_Member, ) labels = get_labels() diff --git a/dojo/asset/api/serializers.py b/dojo/asset/api/serializers.py index 4b3bd0e9d5d..3af93d6f52f 100644 --- a/dojo/asset/api/serializers.py +++ b/dojo/asset/api/serializers.py @@ -3,13 +3,15 @@ from dojo.api_v2.serializers import ProductMetaSerializer, TagListSerializerField from dojo.authorization.authorization import user_has_permission +from dojo.authorization.models import ( + Product_Group, + Product_Member, +) from dojo.authorization.roles_permissions import Permissions from dojo.models import ( Dojo_User, Product, Product_API_Scan_Configuration, - Product_Group, - Product_Member, ) from dojo.organization.api.serializers import RelatedOrganizationField from dojo.product.queries import get_authorized_products diff --git a/dojo/asset/api/views.py b/dojo/asset/api/views.py index 0e01499c466..c998252710c 100644 --- a/dojo/asset/api/views.py +++ b/dojo/asset/api/views.py @@ -6,7 +6,7 @@ from rest_framework.response import Response import dojo.api_v2.mixins as dojo_mixins -from dojo.api_v2 import permissions, prefetch +from dojo.api_v2 import prefetch from dojo.api_v2.serializers import ReportGenerateOptionSerializer, ReportGenerateSerializer from dojo.api_v2.views import PrefetchDojoModelViewSet, report_generate, schema_with_prefetch from dojo.asset.api import serializers @@ -16,12 +16,15 @@ AssetGroupFilterSet, AssetMemberFilterSet, ) +from dojo.authorization import api_permissions as permissions +from dojo.authorization.models import ( + Product_Group, + Product_Member, +) from dojo.authorization.roles_permissions import Permissions from dojo.models import ( Product, Product_API_Scan_Configuration, - Product_Group, - Product_Member, ) from dojo.product.queries import ( get_authorized_product_api_scan_configurations, diff --git a/dojo/authorization/__init__.py b/dojo/authorization/__init__.py index e69de29bb2d..85523329804 100644 --- a/dojo/authorization/__init__.py +++ b/dojo/authorization/__init__.py @@ -0,0 +1,11 @@ +# Import query_registrations to trigger RBAC filter registration at startup +from dojo.authorization import query_registrations # noqa: F401 +from dojo.authorization.authorization import ( # noqa: F401 + user_has_configuration_permission, + user_has_global_permission, + user_has_global_permission_or_403, + user_has_permission, + user_has_permission_or_403, + user_is_superuser_or_global_owner, +) +from dojo.authorization.roles_permissions import Permissions, Roles # noqa: F401 diff --git a/dojo/api_v2/permissions.py b/dojo/authorization/api_permissions.py similarity index 97% rename from dojo/api_v2/permissions.py rename to dojo/authorization/api_permissions.py index 10e012ede5b..16c69e1822f 100644 --- a/dojo/api_v2/permissions.py +++ b/dojo/authorization/api_permissions.py @@ -1285,3 +1285,41 @@ class UserHasConfigurationPermissionSuperuser( def has_permission(self, request, view): return super().has_permission(request, view) + + +class LocationFindingReferencePermission(permissions.BasePermission): + def has_permission(self, request, view): + return check_post_permission( + request, + Finding, + "finding", + Permissions.Finding_Edit, + ) + + def has_object_permission(self, request, view, obj): + return check_object_permission( + request, + obj.finding, + Permissions.Finding_View, + Permissions.Finding_Edit, + Permissions.Finding_Edit, + ) + + +class LocationProductReferencePermission(permissions.BasePermission): + def has_permission(self, request, view): + return check_post_permission( + request, + Product, + "product", + Permissions.Product_Edit, + ) + + def has_object_permission(self, request, view, obj): + return check_object_permission( + request, + obj.product, + Permissions.Product_View, + Permissions.Product_Edit, + Permissions.Product_Edit, + ) diff --git a/dojo/authorization/authorization.py b/dojo/authorization/authorization.py index 313288f4ba8..f2f862ff906 100644 --- a/dojo/authorization/authorization.py +++ b/dojo/authorization/authorization.py @@ -1,6 +1,13 @@ from django.core.exceptions import PermissionDenied from django.db.models import Model, QuerySet +from dojo.authorization.models import ( + Dojo_Group_Member, + Product_Group, + Product_Member, + Product_Type_Group, + Product_Type_Member, +) from dojo.authorization.roles_permissions import ( Permissions, Roles, @@ -12,7 +19,6 @@ App_Analysis, Cred_Mapping, Dojo_Group, - Dojo_Group_Member, Dojo_User, Endpoint, Engagement, @@ -21,11 +27,7 @@ Languages, Product, Product_API_Scan_Configuration, - Product_Group, - Product_Member, Product_Type, - Product_Type_Group, - Product_Type_Member, Risk_Acceptance, Stub_Finding, Test, diff --git a/dojo/authorization/middleware.py b/dojo/authorization/middleware.py new file mode 100644 index 00000000000..f5543753a0c --- /dev/null +++ b/dojo/authorization/middleware.py @@ -0,0 +1,50 @@ +from django.core.exceptions import PermissionDenied +from django.shortcuts import get_object_or_404 + +from dojo.authorization.authorization import ( + user_has_configuration_permission, + user_has_global_permission_or_403, + user_has_permission_or_403, +) +from dojo.authorization.url_permissions import URL_PERMISSIONS + + +class AuthorizationMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + return self.get_response(request) + + def process_view(self, request, view_func, view_args, view_kwargs): + # Skip API paths -- DRF has its own permission classes + if request.path.startswith("/api/"): + return + + resolver_match = request.resolver_match + if resolver_match is None: + return + + url_name = resolver_match.url_name + checks = URL_PERMISSIONS.get(url_name) + if not checks: + return + + for check in checks: + check_type = check[0] + if check_type == "global": + _, permission = check + user_has_global_permission_or_403(request.user, permission) + elif check_type == "config": + _, permission = check + if not user_has_configuration_permission(request.user, permission): + raise PermissionDenied + elif check_type == "object": + _, model, permission, arg_name = check + lookup_value = view_kwargs.get(arg_name) + if lookup_value is None: + continue # kwarg not present, skip this check + obj = get_object_or_404(model, pk=lookup_value) + user_has_permission_or_403(request.user, obj, permission) + + return diff --git a/dojo/authorization/models.py b/dojo/authorization/models.py new file mode 100644 index 00000000000..7165876645b --- /dev/null +++ b/dojo/authorization/models.py @@ -0,0 +1,68 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + + +class Role(models.Model): + name = models.CharField(max_length=255, unique=True) + is_owner = models.BooleanField(default=False) + + class Meta: + app_label = "dojo" + ordering = ("name",) + + def __str__(self): + return self.name + + +class Dojo_Group_Member(models.Model): + group = models.ForeignKey("dojo.Dojo_Group", on_delete=models.CASCADE) + user = models.ForeignKey("dojo.Dojo_User", on_delete=models.CASCADE) + role = models.ForeignKey(Role, on_delete=models.CASCADE, help_text=_("This role determines the permissions of the user to manage the group."), verbose_name=_("Group role")) + + class Meta: + app_label = "dojo" + + +class Global_Role(models.Model): + user = models.OneToOneField("dojo.Dojo_User", null=True, blank=True, on_delete=models.CASCADE) + group = models.OneToOneField("dojo.Dojo_Group", null=True, blank=True, on_delete=models.CASCADE) + role = models.ForeignKey(Role, on_delete=models.CASCADE, null=True, blank=True, help_text=_("The global role will be applied to all product types and products."), verbose_name=_("Global role")) + + class Meta: + app_label = "dojo" + + +class Product_Member(models.Model): + product = models.ForeignKey("dojo.Product", on_delete=models.CASCADE) + user = models.ForeignKey("dojo.Dojo_User", on_delete=models.CASCADE) + role = models.ForeignKey(Role, on_delete=models.CASCADE) + + class Meta: + app_label = "dojo" + + +class Product_Group(models.Model): + product = models.ForeignKey("dojo.Product", on_delete=models.CASCADE) + group = models.ForeignKey("dojo.Dojo_Group", on_delete=models.CASCADE) + role = models.ForeignKey(Role, on_delete=models.CASCADE) + + class Meta: + app_label = "dojo" + + +class Product_Type_Member(models.Model): + product_type = models.ForeignKey("dojo.Product_Type", on_delete=models.CASCADE) + user = models.ForeignKey("dojo.Dojo_User", on_delete=models.CASCADE) + role = models.ForeignKey(Role, on_delete=models.CASCADE) + + class Meta: + app_label = "dojo" + + +class Product_Type_Group(models.Model): + product_type = models.ForeignKey("dojo.Product_Type", on_delete=models.CASCADE) + group = models.ForeignKey("dojo.Dojo_Group", on_delete=models.CASCADE) + role = models.ForeignKey(Role, on_delete=models.CASCADE) + + class Meta: + app_label = "dojo" diff --git a/dojo/authorization/query_filters.py b/dojo/authorization/query_filters.py new file mode 100644 index 00000000000..1a734a35ea0 --- /dev/null +++ b/dojo/authorization/query_filters.py @@ -0,0 +1,9 @@ +_AUTH_FILTER_REGISTRY = {} + + +def register_auth_filter(key, func): + _AUTH_FILTER_REGISTRY[key] = func + + +def get_auth_filter(key): + return _AUTH_FILTER_REGISTRY.get(key) diff --git a/dojo/authorization/query_registrations.py b/dojo/authorization/query_registrations.py new file mode 100644 index 00000000000..0f9deceba0a --- /dev/null +++ b/dojo/authorization/query_registrations.py @@ -0,0 +1,1878 @@ +from crum import get_current_user +from django.db.models import Exists, OuterRef, Q, Subquery + +from dojo.authorization.authorization import ( + get_roles_for_permission, + role_has_permission, + user_has_configuration_permission, + user_has_global_permission, + user_has_permission, +) +from dojo.authorization.models import ( + Dojo_Group_Member, + Global_Role, + Product_Group, + Product_Member, + Product_Type_Group, + Product_Type_Member, +) +from dojo.authorization.query_filters import register_auth_filter +from dojo.authorization.roles_permissions import Permissions +from dojo.location.models import Location, LocationFindingReference, LocationProductReference +from dojo.models import ( + App_Analysis, + Cred_Mapping, + Dojo_Group, + Dojo_User, + DojoMeta, + Endpoint, + Endpoint_Status, + Engagement, + Engagement_Presets, + Finding, + Finding_Group, + JIRA_Issue, + JIRA_Project, + Languages, + Product, + Product_API_Scan_Configuration, + Product_Type, + Risk_Acceptance, + Stub_Finding, + Test, + Test_Import, + Tool_Product_Settings, + Vulnerability_Id, +) + +# ============================================================================ +# Product queries +# ============================================================================ + + +def _get_authorized_products(permission, user=None): + + if user is None: + user = get_current_user() + + if user is None: + return Product.objects.none() + + if user.is_superuser: + return Product.objects.all().order_by("name") + + if user_has_global_permission(user, permission): + return Product.objects.all().order_by("name") + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return Product.objects.filter( + Q(prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(pk__in=Subquery(authorized_product_roles)) + | Q(prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(pk__in=Subquery(authorized_product_groups)), + ).order_by("name") + + +register_auth_filter("product.get_authorized_products", _get_authorized_products) + + +def _get_authorized_members_for_product(product, permission): + user = get_current_user() + + if user.is_superuser or user_has_permission(user, product, permission): + return Product_Member.objects.filter(product=product).order_by("user__first_name", "user__last_name").select_related("role", "user") + return Product_Member.objects.none() + + +register_auth_filter("product.get_authorized_members_for_product", _get_authorized_members_for_product) + + +def _get_authorized_global_members_for_product(product, permission): + user = get_current_user() + + if user.is_superuser or user_has_permission(user, product, permission): + return Global_Role.objects.filter(group=None, role__isnull=False).order_by("user__first_name", "user__last_name").select_related("role", "user") + return Global_Role.objects.none() + + +register_auth_filter("product.get_authorized_global_members_for_product", _get_authorized_global_members_for_product) + + +def _get_authorized_groups_for_product(product, permission): + from dojo.group.queries import get_authorized_groups # noqa: PLC0415 + + user = get_current_user() + + if user.is_superuser or user_has_permission(user, product, permission): + authorized_groups = get_authorized_groups(Permissions.Group_View) + return Product_Group.objects.filter(product=product, group__in=authorized_groups).order_by("group__name").select_related("role") + return Product_Group.objects.none() + + +register_auth_filter("product.get_authorized_groups_for_product", _get_authorized_groups_for_product) + + +def _get_authorized_global_groups_for_product(product, permission): + user = get_current_user() + + if user.is_superuser or user_has_permission(user, product, permission): + return Global_Role.objects.filter(user=None, role__isnull=False).order_by("group__name").select_related("role") + return Global_Role.objects.none() + + +register_auth_filter("product.get_authorized_global_groups_for_product", _get_authorized_global_groups_for_product) + + +def _get_authorized_product_members(permission): + from dojo.product.queries import get_authorized_products # noqa: PLC0415 + + user = get_current_user() + + if user is None: + return Product_Member.objects.none() + + if user.is_superuser: + return Product_Member.objects.all().order_by("id").select_related("role") + + if user_has_global_permission(user, permission): + return Product_Member.objects.all().order_by("id").select_related("role") + + products = get_authorized_products(permission) + return Product_Member.objects.filter(product__in=products).order_by("id").select_related("role") + + +register_auth_filter("product.get_authorized_product_members", _get_authorized_product_members) + + +def _get_authorized_product_members_for_user(user, permission): + from dojo.product.queries import get_authorized_products # noqa: PLC0415 + + request_user = get_current_user() + + if request_user is None: + return Product_Member.objects.none() + + if request_user.is_superuser: + return Product_Member.objects.filter(user=user).select_related("role", "product") + + if hasattr(request_user, "global_role") and request_user.global_role.role is not None and role_has_permission(request_user.global_role.role.id, permission): + return Product_Member.objects.filter(user=user).select_related("role", "product") + + products = get_authorized_products(permission) + return Product_Member.objects.filter(user=user, product__in=products).select_related("role", "product") + + +register_auth_filter("product.get_authorized_product_members_for_user", _get_authorized_product_members_for_user) + + +def _get_authorized_product_groups(permission): + from dojo.product.queries import get_authorized_products # noqa: PLC0415 + + user = get_current_user() + + if user is None: + return Product_Group.objects.none() + + if user.is_superuser: + return Product_Group.objects.all().order_by("id").select_related("role") + + products = get_authorized_products(permission) + return Product_Group.objects.filter(product__in=products).order_by("id").select_related("role") + + +register_auth_filter("product.get_authorized_product_groups", _get_authorized_product_groups) + + +def _get_authorized_app_analysis(permission): + user = get_current_user() + + if user is None: + return App_Analysis.objects.none() + + if user.is_superuser: + return App_Analysis.objects.all().order_by("id") + + if user_has_global_permission(user, permission): + return App_Analysis.objects.all().order_by("id") + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return App_Analysis.objects.filter( + Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(product_id__in=Subquery(authorized_product_roles)) + | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(product_id__in=Subquery(authorized_product_groups)), + ).order_by("id") + + +register_auth_filter("product.get_authorized_app_analysis", _get_authorized_app_analysis) + + +def _get_authorized_dojo_meta(permission): + user = get_current_user() + + if user is None: + return DojoMeta.objects.none() + + if user.is_superuser: + return DojoMeta.objects.all().order_by("id") + + if user_has_global_permission(user, permission): + return DojoMeta.objects.all().order_by("id") + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries for all three paths + # Product path + product_authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + product_authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + product_authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + product_authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + # DojoMeta can be attached to product, endpoint, or finding + return DojoMeta.objects.filter( + # Product path + Q(product__prod_type_id__in=Subquery(product_authorized_product_type_roles)) + | Q(product_id__in=Subquery(product_authorized_product_roles)) + | Q(product__prod_type_id__in=Subquery(product_authorized_product_type_groups)) + | Q(product_id__in=Subquery(product_authorized_product_groups)) + # Endpoint path + | Q(endpoint__product__prod_type_id__in=Subquery(product_authorized_product_type_roles)) + | Q(endpoint__product_id__in=Subquery(product_authorized_product_roles)) + | Q(endpoint__product__prod_type_id__in=Subquery(product_authorized_product_type_groups)) + | Q(endpoint__product_id__in=Subquery(product_authorized_product_groups)) + # Finding path + | Q(finding__test__engagement__product__prod_type_id__in=Subquery(product_authorized_product_type_roles)) + | Q(finding__test__engagement__product_id__in=Subquery(product_authorized_product_roles)) + | Q(finding__test__engagement__product__prod_type_id__in=Subquery(product_authorized_product_type_groups)) + | Q(finding__test__engagement__product_id__in=Subquery(product_authorized_product_groups)), + ).order_by("id") + + +register_auth_filter("product.get_authorized_dojo_meta", _get_authorized_dojo_meta) + + +def _get_authorized_languages(permission): + user = get_current_user() + + if user is None: + return Languages.objects.none() + + if user.is_superuser: + return Languages.objects.all().order_by("id") + + if user_has_global_permission(user, permission): + return Languages.objects.all().order_by("id") + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return Languages.objects.filter( + Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(product_id__in=Subquery(authorized_product_roles)) + | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(product_id__in=Subquery(authorized_product_groups)), + ).order_by("id") + + +register_auth_filter("product.get_authorized_languages", _get_authorized_languages) + + +def _get_authorized_engagement_presets(permission): + user = get_current_user() + + if user is None: + return Engagement_Presets.objects.none() + + if user.is_superuser: + return Engagement_Presets.objects.all().order_by("id") + + if user_has_global_permission(user, permission): + return Engagement_Presets.objects.all().order_by("id") + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return Engagement_Presets.objects.filter( + Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(product_id__in=Subquery(authorized_product_roles)) + | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(product_id__in=Subquery(authorized_product_groups)), + ).order_by("id") + + +register_auth_filter("product.get_authorized_engagement_presets", _get_authorized_engagement_presets) + + +def _get_authorized_product_api_scan_configurations(permission): + user = get_current_user() + + if user is None: + return Product_API_Scan_Configuration.objects.none() + + if user.is_superuser: + return Product_API_Scan_Configuration.objects.all().order_by("id") + + if user_has_global_permission(user, permission): + return Product_API_Scan_Configuration.objects.all().order_by("id") + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return Product_API_Scan_Configuration.objects.filter( + Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(product_id__in=Subquery(authorized_product_roles)) + | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(product_id__in=Subquery(authorized_product_groups)), + ).order_by("id") + + +register_auth_filter("product.get_authorized_product_api_scan_configurations", _get_authorized_product_api_scan_configurations) + + +# ============================================================================ +# Product Type queries +# ============================================================================ + +def _get_authorized_product_types(permission): + user = get_current_user() + + if user is None: + return Product_Type.objects.none() + + if user.is_superuser: + return Product_Type.objects.all().order_by("name") + + if user_has_global_permission(user, permission): + return Product_Type.objects.all().order_by("name") + + roles = get_roles_for_permission(permission) + + # Get authorized product_type IDs via subqueries + authorized_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + # Filter using IN with Subquery - no annotations needed + return Product_Type.objects.filter( + Q(pk__in=Subquery(authorized_roles)) + | Q(pk__in=Subquery(authorized_groups)), + ).order_by("name") + + +register_auth_filter("product_type.get_authorized_product_types", _get_authorized_product_types) + + +def _get_authorized_members_for_product_type(product_type, permission): + user = get_current_user() + + if user.is_superuser or user_has_permission(user, product_type, permission): + return Product_Type_Member.objects.filter(product_type=product_type).order_by("user__first_name", "user__last_name").select_related("role", "product_type", "user") + return Product_Type_Member.objects.none() + + +register_auth_filter("product_type.get_authorized_members_for_product_type", _get_authorized_members_for_product_type) + + +def _get_authorized_global_members_for_product_type(product_type, permission): + user = get_current_user() + + if user.is_superuser or user_has_permission(user, product_type, permission): + return Global_Role.objects.filter(group=None, role__isnull=False).order_by("user__first_name", "user__last_name").select_related("role", "user") + return Global_Role.objects.none() + + +register_auth_filter("product_type.get_authorized_global_members_for_product_type", _get_authorized_global_members_for_product_type) + + +def _get_authorized_groups_for_product_type(product_type, permission): + from dojo.group.queries import get_authorized_groups # noqa: PLC0415 + + user = get_current_user() + + if user.is_superuser or user_has_permission(user, product_type, permission): + authorized_groups = get_authorized_groups(Permissions.Group_View) + return Product_Type_Group.objects.filter(product_type=product_type, group__in=authorized_groups).order_by("group__name").select_related("role", "group") + return Product_Type_Group.objects.none() + + +register_auth_filter("product_type.get_authorized_groups_for_product_type", _get_authorized_groups_for_product_type) + + +def _get_authorized_global_groups_for_product_type(product_type, permission): + user = get_current_user() + + if user.is_superuser or user_has_permission(user, product_type, permission): + return Global_Role.objects.filter(user=None, role__isnull=False).order_by("group__name").select_related("role", "group") + return Global_Role.objects.none() + + +register_auth_filter("product_type.get_authorized_global_groups_for_product_type", _get_authorized_global_groups_for_product_type) + + +def _get_authorized_product_type_members(permission): + from dojo.product_type.queries import get_authorized_product_types # noqa: PLC0415 + + user = get_current_user() + + if user is None: + return Product_Type_Member.objects.none() + + if user.is_superuser: + return Product_Type_Member.objects.all().order_by("id").select_related("role") + + if user_has_global_permission(user, permission): + return Product_Type_Member.objects.all().order_by("id").select_related("role") + + product_types = get_authorized_product_types(permission) + return Product_Type_Member.objects.filter(product_type__in=product_types).order_by("id").select_related("role") + + +register_auth_filter("product_type.get_authorized_product_type_members", _get_authorized_product_type_members) + + +def _get_authorized_product_type_members_for_user(user, permission): + from dojo.product_type.queries import get_authorized_product_types # noqa: PLC0415 + + request_user = get_current_user() + + if request_user is None: + return Product_Type_Member.objects.none() + + if request_user.is_superuser: + return Product_Type_Member.objects.filter(user=user).select_related("role", "product_type") + + if hasattr(request_user, "global_role") and request_user.global_role.role is not None and role_has_permission(request_user.global_role.role.id, permission): + return Product_Type_Member.objects.filter(user=user).select_related("role", "product_type") + + product_types = get_authorized_product_types(permission) + return Product_Type_Member.objects.filter(user=user, product_type__in=product_types).select_related("role", "product_type") + + +register_auth_filter("product_type.get_authorized_product_type_members_for_user", _get_authorized_product_type_members_for_user) + + +def _get_authorized_product_type_groups(permission): + from dojo.product_type.queries import get_authorized_product_types # noqa: PLC0415 + + user = get_current_user() + + if user is None: + return Product_Type_Group.objects.none() + + if user.is_superuser: + return Product_Type_Group.objects.all().order_by("id").select_related("role") + + product_types = get_authorized_product_types(permission) + return Product_Type_Group.objects.filter(product_type__in=product_types).order_by("id").select_related("role") + + +register_auth_filter("product_type.get_authorized_product_type_groups", _get_authorized_product_type_groups) + + +# ============================================================================ +# Finding queries +# ============================================================================ + +def _get_authorized_findings(permission, user=None): + """Cached - returns all findings the user is authorized to see.""" + if user is None: + user = get_current_user() + if user is None: + return Finding.objects.none() + findings = Finding.objects.all().order_by("id") + + if user.is_superuser: + return findings + + if user_has_global_permission(user, permission): + return findings + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return findings.filter( + Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(test__engagement__product_id__in=Subquery(authorized_product_roles)) + | Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(test__engagement__product_id__in=Subquery(authorized_product_groups)), + ) + + +register_auth_filter("finding.get_authorized_findings", _get_authorized_findings) + + +def _get_authorized_findings_for_queryset(permission, queryset, user=None): + """Filters a provided queryset for authorization. Not cached due to dynamic queryset parameter.""" + if user is None: + user = get_current_user() + if user is None: + return Finding.objects.none() + findings = Finding.objects.all().order_by("id") if queryset is None else queryset + + if user.is_superuser: + return findings + + if user_has_global_permission(user, permission): + return findings + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return findings.filter( + Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(test__engagement__product_id__in=Subquery(authorized_product_roles)) + | Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(test__engagement__product_id__in=Subquery(authorized_product_groups)), + ) + + +register_auth_filter("finding.get_authorized_findings_for_queryset", _get_authorized_findings_for_queryset) + + +def _get_authorized_stub_findings(permission): + user = get_current_user() + + if user is None: + return Stub_Finding.objects.none() + + if user.is_superuser: + return Stub_Finding.objects.all().order_by("id") + + if user_has_global_permission(user, permission): + return Stub_Finding.objects.all().order_by("id") + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return Stub_Finding.objects.filter( + Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(test__engagement__product_id__in=Subquery(authorized_product_roles)) + | Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(test__engagement__product_id__in=Subquery(authorized_product_groups)), + ).order_by("id") + + +register_auth_filter("finding.get_authorized_stub_findings", _get_authorized_stub_findings) + + +def _get_authorized_vulnerability_ids(permission, user=None): + """Cached - returns all vulnerability IDs the user is authorized to see.""" + if user is None: + user = get_current_user() + + if user is None: + return Vulnerability_Id.objects.none() + + vulnerability_ids = Vulnerability_Id.objects.all() + + if user.is_superuser: + return vulnerability_ids + + if user_has_global_permission(user, permission): + return vulnerability_ids + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return vulnerability_ids.filter( + Q(finding__test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(finding__test__engagement__product_id__in=Subquery(authorized_product_roles)) + | Q(finding__test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(finding__test__engagement__product_id__in=Subquery(authorized_product_groups)), + ) + + +register_auth_filter("finding.get_authorized_vulnerability_ids", _get_authorized_vulnerability_ids) + + +def _get_authorized_vulnerability_ids_for_queryset(permission, queryset, user=None): + """Filters a provided queryset for authorization. Not cached due to dynamic queryset parameter.""" + if user is None: + user = get_current_user() + + if user is None: + return Vulnerability_Id.objects.none() + + if user.is_superuser: + return queryset + + if user_has_global_permission(user, permission): + return queryset + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return queryset.filter( + Q(finding__test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(finding__test__engagement__product_id__in=Subquery(authorized_product_roles)) + | Q(finding__test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(finding__test__engagement__product_id__in=Subquery(authorized_product_groups)), + ) + + +register_auth_filter("finding.get_authorized_vulnerability_ids_for_queryset", _get_authorized_vulnerability_ids_for_queryset) + + +# ============================================================================ +# Endpoint queries +# ============================================================================ + +def _get_authorized_endpoints(permission, user=None): + """Cached - returns all endpoints the user is authorized to see.""" + if user is None: + user = get_current_user() + + if user is None: + return Endpoint.objects.none() + + endpoints = Endpoint.objects.all().order_by("id") + + if user.is_superuser: + return endpoints + + if user_has_global_permission(user, permission): + return endpoints + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return endpoints.filter( + Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(product_id__in=Subquery(authorized_product_roles)) + | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(product_id__in=Subquery(authorized_product_groups)), + ) + + +register_auth_filter("endpoint.get_authorized_endpoints", _get_authorized_endpoints) + + +def _get_authorized_endpoints_for_queryset(permission, queryset, user=None): + """Filters a provided queryset for authorization. Not cached due to dynamic queryset parameter.""" + if user is None: + user = get_current_user() + + if user is None: + return Endpoint.objects.none() + + if user.is_superuser: + return queryset + + if user_has_global_permission(user, permission): + return queryset + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return queryset.filter( + Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(product_id__in=Subquery(authorized_product_roles)) + | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(product_id__in=Subquery(authorized_product_groups)), + ) + + +register_auth_filter("endpoint.get_authorized_endpoints_for_queryset", _get_authorized_endpoints_for_queryset) + + +def _get_authorized_endpoint_status(permission, user=None): + """Cached - returns all endpoint statuses the user is authorized to see.""" + if user is None: + user = get_current_user() + + if user is None: + return Endpoint_Status.objects.none() + + endpoint_status = Endpoint_Status.objects.all().order_by("id") + + if user.is_superuser: + return endpoint_status + + if user_has_global_permission(user, permission): + return endpoint_status + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return endpoint_status.filter( + Q(endpoint__product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(endpoint__product_id__in=Subquery(authorized_product_roles)) + | Q(endpoint__product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(endpoint__product_id__in=Subquery(authorized_product_groups)), + ) + + +register_auth_filter("endpoint.get_authorized_endpoint_status", _get_authorized_endpoint_status) + + +def _get_authorized_endpoint_status_for_queryset(permission, queryset, user=None): + """Filters a provided queryset for authorization. Not cached due to dynamic queryset parameter.""" + if user is None: + user = get_current_user() + + if user is None: + return Endpoint_Status.objects.none() + + if user.is_superuser: + return queryset + + if user_has_global_permission(user, permission): + return queryset + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return queryset.filter( + Q(endpoint__product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(endpoint__product_id__in=Subquery(authorized_product_roles)) + | Q(endpoint__product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(endpoint__product_id__in=Subquery(authorized_product_groups)), + ) + + +register_auth_filter("endpoint.get_authorized_endpoint_status_for_queryset", _get_authorized_endpoint_status_for_queryset) + + +# ============================================================================ +# User queries +# ============================================================================ + +def _get_authorized_users_for_product_type(users, product_type, permission): + roles = get_roles_for_permission(permission) + + # Get user IDs via subqueries instead of materializing into Python lists + product_type_member_users = Product_Type_Member.objects.filter( + product_type=product_type, role__in=roles, + ).values("user_id") + + # Get group IDs that have access to this product type + product_type_group_ids = Product_Type_Group.objects.filter( + product_type=product_type, role__in=roles, + ).values("group_id") + + global_role_group_ids = Global_Role.objects.filter( + role__in=roles, group__isnull=False, + ).values("group_id") + + # Get users from those groups + group_member_users = Dojo_Group_Member.objects.filter( + Q(group_id__in=Subquery(product_type_group_ids)) + | Q(group_id__in=Subquery(global_role_group_ids)), + ).values("user_id") + + return users.filter( + Q(id__in=Subquery(product_type_member_users)) + | Q(id__in=Subquery(group_member_users)) + | Q(global_role__role__in=roles) + | Q(is_superuser=True), + ) + + +register_auth_filter("user.get_authorized_users_for_product_type", _get_authorized_users_for_product_type) + + +def _get_authorized_users_for_product_and_product_type(users, product, permission): + if users is None: + users = Dojo_User.objects.filter(is_active=True) + + roles = get_roles_for_permission(permission) + + # Get user IDs via subqueries instead of materializing into Python lists + product_member_users = Product_Member.objects.filter( + product=product, role__in=roles, + ).values("user_id") + + product_type_member_users = Product_Type_Member.objects.filter( + product_type=product.prod_type, role__in=roles, + ).values("user_id") + + # Get group IDs that have access to this product or product type + product_group_ids = Product_Group.objects.filter( + product=product, role__in=roles, + ).values("group_id") + + product_type_group_ids = Product_Type_Group.objects.filter( + product_type=product.prod_type, role__in=roles, + ).values("group_id") + + global_role_group_ids = Global_Role.objects.filter( + role__in=roles, group__isnull=False, + ).values("group_id") + + # Get users from those groups + group_member_users = Dojo_Group_Member.objects.filter( + Q(group_id__in=Subquery(product_group_ids)) + | Q(group_id__in=Subquery(product_type_group_ids)) + | Q(group_id__in=Subquery(global_role_group_ids)), + ).values("user_id") + + return users.filter( + Q(id__in=Subquery(product_member_users)) + | Q(id__in=Subquery(product_type_member_users)) + | Q(id__in=Subquery(group_member_users)) + | Q(global_role__role__in=roles) + | Q(is_superuser=True), + ) + + +register_auth_filter("user.get_authorized_users_for_product_and_product_type", _get_authorized_users_for_product_and_product_type) + + +def _get_authorized_users(permission, user=None): + from dojo.product.queries import get_authorized_products # noqa: PLC0415 + from dojo.product_type.queries import get_authorized_product_types # noqa: PLC0415 + + if user is None: + user = get_current_user() + + if user is None: + return Dojo_User.objects.none() + + if user.is_anonymous: + return Dojo_User.objects.none() + + users = Dojo_User.objects.all().order_by("first_name", "last_name", "username") + + if user.is_superuser or user_has_global_permission(user, permission): + return users + + authorized_products = get_authorized_products(permission).values("id") + authorized_product_types = get_authorized_product_types(permission).values("id") + + roles = get_roles_for_permission(permission) + + # Get user IDs via subqueries instead of materializing into Python lists + product_member_users = Product_Member.objects.filter( + product_id__in=Subquery(authorized_products), role__in=roles, + ).values("user_id") + + product_type_member_users = Product_Type_Member.objects.filter( + product_type_id__in=Subquery(authorized_product_types), role__in=roles, + ).values("user_id") + + # Get group IDs that have access to authorized products/product types + product_group_ids = Product_Group.objects.filter( + product_id__in=Subquery(authorized_products), role__in=roles, + ).values("group_id") + + product_type_group_ids = Product_Type_Group.objects.filter( + product_type_id__in=Subquery(authorized_product_types), role__in=roles, + ).values("group_id") + + # Get users from those groups + group_member_users = Dojo_Group_Member.objects.filter( + Q(group_id__in=Subquery(product_group_ids)) + | Q(group_id__in=Subquery(product_type_group_ids)), + ).values("user_id") + + return users.filter( + Q(id__in=Subquery(product_member_users)) + | Q(id__in=Subquery(product_type_member_users)) + | Q(id__in=Subquery(group_member_users)) + | Q(global_role__role__in=roles) + | Q(is_superuser=True), + ) + + +register_auth_filter("user.get_authorized_users", _get_authorized_users) + + +# ============================================================================ +# Group queries +# ============================================================================ + +def _get_authorized_groups(permission): + user = get_current_user() + + if user is None: + return Dojo_Group.objects.none() + + if user.is_superuser: + return Dojo_Group.objects.all().order_by("name") + + # Check for the case of the view_group config permission + if user_has_configuration_permission(user, "auth.view_group") or user_has_configuration_permission(user, "auth.add_group"): + return Dojo_Group.objects.all().order_by("name") + + roles = get_roles_for_permission(permission) + + # Get authorized group IDs via subquery + authorized_roles = Dojo_Group_Member.objects.filter( + user=user, role__in=roles, + ).values("group_id") + + # Filter using IN with Subquery - no annotations needed + return Dojo_Group.objects.filter( + pk__in=Subquery(authorized_roles), + ).order_by("name") + + +register_auth_filter("group.get_authorized_groups", _get_authorized_groups) + + +def _get_authorized_group_members(permission): + from dojo.group.queries import get_authorized_groups # noqa: PLC0415 + + user = get_current_user() + + if user is None: + return Dojo_Group_Member.objects.none() + + if user.is_superuser: + return Dojo_Group_Member.objects.all().order_by("id").select_related("role") + + groups = get_authorized_groups(permission) + return Dojo_Group_Member.objects.filter(group__in=groups).order_by("id").select_related("role") + + +register_auth_filter("group.get_authorized_group_members", _get_authorized_group_members) + + +def _get_authorized_group_members_for_user(user): + from dojo.group.queries import get_authorized_groups # noqa: PLC0415 + + groups = get_authorized_groups(Permissions.Group_View) + return Dojo_Group_Member.objects.filter(user=user, group__in=groups).order_by("group__name").select_related("role", "group") + + +register_auth_filter("group.get_authorized_group_members_for_user", _get_authorized_group_members_for_user) + + +# ============================================================================ +# Location queries +# ============================================================================ + +def _get_authorized_locations(permission, queryset=None, user=None): + + if user is None: + user = get_current_user() + + if user is None: + return Location.objects.none() + + locations = Location.objects.all().order_by("id") if queryset is None else queryset + + if user.is_superuser: + return locations + + if user_has_global_permission(user, permission): + return locations + + roles = get_roles_for_permission(permission) + authorized_product_type_roles = Product_Type_Member.objects.filter( + product_type=OuterRef("products__product__prod_type_id"), + user=user, + role__in=roles) + authorized_product_roles = Product_Member.objects.filter( + product=OuterRef("products__product_id"), + user=user, + role__in=roles) + authorized_product_type_groups = Product_Type_Group.objects.filter( + product_type=OuterRef("products__product__prod_type_id"), + group__users=user, + role__in=roles) + authorized_product_groups = Product_Group.objects.filter( + product=OuterRef("products__product_id"), + group__users=user, + role__in=roles) + locations = locations.annotate( + product__prod_type__member=Exists(authorized_product_type_roles), + product__member=Exists(authorized_product_roles), + product__prod_type__authorized_group=Exists(authorized_product_type_groups), + product__authorized_group=Exists(authorized_product_groups)) + return locations.filter( + Q(product__prod_type__member=True) | Q(product__member=True) + | Q(product__prod_type__authorized_group=True) | Q(product__authorized_group=True)) + + +register_auth_filter("location.get_authorized_locations", _get_authorized_locations) + + +def _get_authorized_location_finding_reference(permission, queryset=None, user=None): + + if user is None: + user = get_current_user() + + if user is None: + return LocationFindingReference.objects.none() + + location_finding_reference = LocationFindingReference.objects.all().order_by("id") if queryset is None else queryset + + if user.is_superuser: + return location_finding_reference + + if user_has_global_permission(user, permission): + return location_finding_reference + + roles = get_roles_for_permission(permission) + authorized_product_type_roles = Product_Type_Member.objects.filter( + product_type=OuterRef("location__products__product__prod_type_id"), + user=user, + role__in=roles) + authorized_product_roles = Product_Member.objects.filter( + product=OuterRef("location__products__product_id"), + user=user, + role__in=roles) + authorized_product_type_groups = Product_Type_Group.objects.filter( + product_type=OuterRef("location__products__product__prod_type_id"), + group__users=user, + role__in=roles) + authorized_product_groups = Product_Group.objects.filter( + product=OuterRef("location__products__product_id"), + group__users=user, + role__in=roles) + location_finding_reference = location_finding_reference.annotate( + location__product__prod_type__member=Exists(authorized_product_type_roles), + location__product__member=Exists(authorized_product_roles), + location__product__prod_type__authorized_group=Exists(authorized_product_type_groups), + location__product__authorized_group=Exists(authorized_product_groups)) + return location_finding_reference.filter( + Q(location__product__prod_type__member=True) | Q(location__product__member=True) + | Q(location__product__prod_type__authorized_group=True) | Q(location__product__authorized_group=True)) + + +register_auth_filter("location.get_authorized_location_finding_reference", _get_authorized_location_finding_reference) + + +def _get_authorized_location_product_reference(permission, queryset=None, user=None): + + if user is None: + user = get_current_user() + + if user is None: + return LocationProductReference.objects.none() + + location_product_reference = LocationProductReference.objects.all().order_by("id") if queryset is None else queryset + + if user.is_superuser: + return location_product_reference + + if user_has_global_permission(user, permission): + return location_product_reference + + roles = get_roles_for_permission(permission) + authorized_product_type_roles = Product_Type_Member.objects.filter( + product_type=OuterRef("product__prod_type_id"), + user=user, + role__in=roles) + authorized_product_roles = Product_Member.objects.filter( + product=OuterRef("product_id"), + user=user, + role__in=roles) + authorized_product_type_groups = Product_Type_Group.objects.filter( + product_type=OuterRef("product__prod_type_id"), + group__users=user, + role__in=roles) + authorized_product_groups = Product_Group.objects.filter( + product=OuterRef("product_id"), + group__users=user, + role__in=roles) + location_product_reference = location_product_reference.annotate( + location__product__prod_type__member=Exists(authorized_product_type_roles), + location__product__member=Exists(authorized_product_roles), + location__product__prod_type__authorized_group=Exists(authorized_product_type_groups), + location__product__authorized_group=Exists(authorized_product_groups)) + return location_product_reference.filter( + Q(location__product__prod_type__member=True) | Q(location__product__member=True) + | Q(location__product__prod_type__authorized_group=True) | Q(location__product__authorized_group=True)) + + +register_auth_filter("location.get_authorized_location_product_reference", _get_authorized_location_product_reference) + + +# ============================================================================ +# Test queries +# ============================================================================ + +def _get_authorized_tests(permission, product=None): + user = get_current_user() + + if user is None: + return Test.objects.none() + + tests = Test.objects.all().order_by("id") + if product: + tests = tests.filter(engagement__product=product) + + if user.is_superuser: + return tests + + if user_has_global_permission(user, permission): + return Test.objects.all().order_by("id") + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return tests.filter( + Q(engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(engagement__product_id__in=Subquery(authorized_product_roles)) + | Q(engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(engagement__product_id__in=Subquery(authorized_product_groups)), + ) + + +register_auth_filter("test.get_authorized_tests", _get_authorized_tests) + + +def _get_authorized_test_imports(permission): + user = get_current_user() + + if user is None: + return Test_Import.objects.none() + + if user.is_superuser: + return Test_Import.objects.all().order_by("id") + + if user_has_global_permission(user, permission): + return Test_Import.objects.all().order_by("id") + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return Test_Import.objects.filter( + Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(test__engagement__product_id__in=Subquery(authorized_product_roles)) + | Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(test__engagement__product_id__in=Subquery(authorized_product_groups)), + ).order_by("id") + + +register_auth_filter("test.get_authorized_test_imports", _get_authorized_test_imports) + + +# ============================================================================ +# Jira Link queries +# ============================================================================ + +def _get_authorized_jira_projects(permission, user=None): + + if user is None: + user = get_current_user() + + if user is None: + return JIRA_Project.objects.none() + + jira_projects = JIRA_Project.objects.all().order_by("id") + + if user.is_superuser: + return jira_projects + + if user_has_global_permission(user, permission): + return jira_projects + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + # JIRA projects can be attached via engagement or product path + return jira_projects.filter( + # Engagement path + Q(engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(engagement__product_id__in=Subquery(authorized_product_roles)) + | Q(engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(engagement__product_id__in=Subquery(authorized_product_groups)) + # Product path + | Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(product_id__in=Subquery(authorized_product_roles)) + | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(product_id__in=Subquery(authorized_product_groups)), + ) + + +register_auth_filter("jira_link.get_authorized_jira_projects", _get_authorized_jira_projects) + + +def _get_authorized_jira_issues(permission): + user = get_current_user() + + if user is None: + return JIRA_Issue.objects.none() + + jira_issues = JIRA_Issue.objects.all().order_by("id") + + if user.is_superuser: + return jira_issues + + if user_has_global_permission(user, permission): + return jira_issues + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + # JIRA issues can be attached via engagement, finding_group, or finding path + return jira_issues.filter( + # Engagement path + Q(engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(engagement__product_id__in=Subquery(authorized_product_roles)) + | Q(engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(engagement__product_id__in=Subquery(authorized_product_groups)) + # Finding group path + | Q(finding_group__test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(finding_group__test__engagement__product_id__in=Subquery(authorized_product_roles)) + | Q(finding_group__test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(finding_group__test__engagement__product_id__in=Subquery(authorized_product_groups)) + # Finding path + | Q(finding__test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(finding__test__engagement__product_id__in=Subquery(authorized_product_roles)) + | Q(finding__test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(finding__test__engagement__product_id__in=Subquery(authorized_product_groups)), + ) + + +register_auth_filter("jira_link.get_authorized_jira_issues", _get_authorized_jira_issues) + + +# ============================================================================ +# Engagement queries +# ============================================================================ + +def _get_authorized_engagements(permission): + user = get_current_user() + + if user is None: + return Engagement.objects.none() + + if user.is_superuser: + return Engagement.objects.all().order_by("id") + + if user_has_global_permission(user, permission): + return Engagement.objects.all().order_by("id") + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return Engagement.objects.filter( + Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(product_id__in=Subquery(authorized_product_roles)) + | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(product_id__in=Subquery(authorized_product_groups)), + ).order_by("id") + + +register_auth_filter("engagement.get_authorized_engagements", _get_authorized_engagements) + + +# ============================================================================ +# Risk Acceptance queries +# ============================================================================ + +def _get_authorized_risk_acceptances(permission): + user = get_current_user() + + if user is None: + return Risk_Acceptance.objects.none() + + if user.is_superuser: + return Risk_Acceptance.objects.all().order_by("id") + + if user_has_global_permission(user, permission): + return Risk_Acceptance.objects.all().order_by("id") + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return Risk_Acceptance.objects.filter( + Q(engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(engagement__product_id__in=Subquery(authorized_product_roles)) + | Q(engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(engagement__product_id__in=Subquery(authorized_product_groups)), + ).order_by("id") + + +register_auth_filter("risk_acceptance.get_authorized_risk_acceptances", _get_authorized_risk_acceptances) + + +# ============================================================================ +# Finding Group queries +# ============================================================================ + +def _get_authorized_finding_groups(permission, user=None): + """Cached - returns all finding groups the user is authorized to see.""" + if user is None: + user = get_current_user() + + if user is None: + return Finding_Group.objects.none() + + finding_groups = Finding_Group.objects.all() + + if user.is_superuser: + return finding_groups + + if user_has_global_permission(user, permission): + return finding_groups + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return finding_groups.filter( + Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(test__engagement__product_id__in=Subquery(authorized_product_roles)) + | Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(test__engagement__product_id__in=Subquery(authorized_product_groups)), + ) + + +register_auth_filter("finding_group.get_authorized_finding_groups", _get_authorized_finding_groups) + + +def _get_authorized_finding_groups_for_queryset(permission, queryset, user=None): + """Filters a provided queryset for authorization. Not cached due to dynamic queryset parameter.""" + if user is None: + user = get_current_user() + + if user is None: + return Finding_Group.objects.none() + + if user.is_superuser: + return queryset + + if user_has_global_permission(user, permission): + return queryset + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return queryset.filter( + Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(test__engagement__product_id__in=Subquery(authorized_product_roles)) + | Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(test__engagement__product_id__in=Subquery(authorized_product_groups)), + ) + + +register_auth_filter("finding_group.get_authorized_finding_groups_for_queryset", _get_authorized_finding_groups_for_queryset) + + +# ============================================================================ +# Tool Product queries +# ============================================================================ + +def _get_authorized_tool_product_settings(permission): + user = get_current_user() + + if user is None: + return Tool_Product_Settings.objects.none() + + if user.is_superuser: + return Tool_Product_Settings.objects.all().order_by("id") + + if user_has_global_permission(user, permission): + return Tool_Product_Settings.objects.all().order_by("id") + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return Tool_Product_Settings.objects.filter( + Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(product_id__in=Subquery(authorized_product_roles)) + | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(product_id__in=Subquery(authorized_product_groups)), + ).order_by("id") + + +register_auth_filter("tool_product.get_authorized_tool_product_settings", _get_authorized_tool_product_settings) + + +# ============================================================================ +# Cred queries +# ============================================================================ + +def _get_authorized_cred_mappings(permission): + """Cached - returns all cred mappings the user is authorized to see.""" + user = get_current_user() + + if user is None: + return Cred_Mapping.objects.none() + + cred_mappings = Cred_Mapping.objects.all().order_by("id") + + if user.is_superuser: + return cred_mappings + + if user_has_global_permission(user, permission): + return cred_mappings + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return cred_mappings.filter( + Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(product_id__in=Subquery(authorized_product_roles)) + | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(product_id__in=Subquery(authorized_product_groups)), + ) + + +register_auth_filter("cred.get_authorized_cred_mappings", _get_authorized_cred_mappings) + + +def _get_authorized_cred_mappings_for_queryset(permission, queryset): + """Filters a provided queryset for authorization. Not cached due to dynamic queryset parameter.""" + user = get_current_user() + + if user is None: + return Cred_Mapping.objects.none() + + if user.is_superuser: + return queryset + + if user_has_global_permission(user, permission): + return queryset + + roles = get_roles_for_permission(permission) + + # Get authorized product/product_type IDs via subqueries + authorized_product_type_roles = Product_Type_Member.objects.filter( + user=user, role__in=roles, + ).values("product_type_id") + + authorized_product_roles = Product_Member.objects.filter( + user=user, role__in=roles, + ).values("product_id") + + authorized_product_type_groups = Product_Type_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_type_id") + + authorized_product_groups = Product_Group.objects.filter( + group__users=user, role__in=roles, + ).values("product_id") + + # Filter using IN with Subquery - no annotations needed + return queryset.filter( + Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) + | Q(product_id__in=Subquery(authorized_product_roles)) + | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) + | Q(product_id__in=Subquery(authorized_product_groups)), + ) + + +register_auth_filter("cred.get_authorized_cred_mappings_for_queryset", _get_authorized_cred_mappings_for_queryset) diff --git a/dojo/authorization/template_filters.py b/dojo/authorization/template_filters.py new file mode 100644 index 00000000000..f9f5d968ec4 --- /dev/null +++ b/dojo/authorization/template_filters.py @@ -0,0 +1,45 @@ +import crum + +from dojo.authorization.authorization import user_has_configuration_permission as configuration_permission +from dojo.authorization.authorization import user_has_global_permission, user_has_permission +from dojo.authorization.roles_permissions import Permissions +from dojo.request_cache import cache_for_request + + +def has_object_permission(obj, permission): + return user_has_permission(crum.get_current_user(), obj, Permissions[permission]) + + +def has_global_permission(permission): + return user_has_global_permission(crum.get_current_user(), Permissions[permission]) + + +def has_configuration_permission(permission, request): + user = crum.get_current_user() if request is None else crum.get_current_user() or request.user + return configuration_permission(user, permission) + + +@cache_for_request +def get_user_permissions(user): + return user.user_permissions.all() + + +def user_has_configuration_permission_without_group(user, codename): + permissions = get_user_permissions(user) + return any(permission.codename == codename for permission in permissions) + + +@cache_for_request +def get_group_permissions(group): + return group.permissions.all() + + +def group_has_configuration_permission(group, codename): + return any(permission.codename == codename for permission in get_group_permissions(group)) + + +def user_can_clear_peer_review(finding, user): + finding_under_review = finding.under_review + user_requesting_review = user == finding.review_requested_by + user_is_reviewer = user in finding.reviewers.all() + return finding_under_review and (user_requesting_review or user_is_reviewer) diff --git a/dojo/authorization/url_permissions.py b/dojo/authorization/url_permissions.py new file mode 100644 index 00000000000..e5b72d06d0e --- /dev/null +++ b/dojo/authorization/url_permissions.py @@ -0,0 +1,381 @@ +from dojo.authorization.models import ( + Dojo_Group_Member, + Product_Group, + Product_Member, + Product_Type_Group, + Product_Type_Member, +) +from dojo.authorization.roles_permissions import Permissions +from dojo.models import ( + App_Analysis, + Cred_Mapping, + Dojo_Group, + Endpoint, + Engagement, + Finding, + Finding_Group, + Product, + Product_API_Scan_Configuration, + Product_Type, + Stub_Finding, + Test, +) + +# --------------------------------------------------------------------------- +# URL_PERMISSIONS: maps Django URL names to authorization checks. +# +# Each key is a URL name (from urls.py). +# Each value is a list of check tuples. ALL checks in the list must pass. +# +# Check tuple formats: +# ("object", ModelClass, Permissions.Perm, "kwarg_name") +# ("global", Permissions.Perm) +# ("config", "permission.string") +# --------------------------------------------------------------------------- + +URL_PERMISSIONS = { + # ----------------------------------------------------------------------- + # Product Type (dojo/product_type/views.py -> dojo/organization/urls.py) + # ----------------------------------------------------------------------- + "add_product_type": [("global", Permissions.Product_Type_Add)], + "view_product_type": [("object", Product_Type, Permissions.Product_Type_View, "ptid")], + "edit_product_type": [("object", Product_Type, Permissions.Product_Type_Edit, "ptid")], + "delete_product_type": [("object", Product_Type, Permissions.Product_Type_Delete, "ptid")], + "add_product_type_member": [("object", Product_Type, Permissions.Product_Type_Manage_Members, "ptid")], + "edit_product_type_member": [("object", Product_Type_Member, Permissions.Product_Type_Manage_Members, "memberid")], + "delete_product_type_member": [("object", Product_Type_Member, Permissions.Product_Type_Member_Delete, "memberid")], + "add_product_type_group": [("object", Product_Type, Permissions.Product_Type_Group_Add, "ptid")], + "edit_product_type_group": [("object", Product_Type_Group, Permissions.Product_Type_Group_Edit, "groupid")], + "delete_product_type_group": [("object", Product_Type_Group, Permissions.Product_Type_Group_Delete, "groupid")], + + # ----------------------------------------------------------------------- + # Product (dojo/product/views.py -> dojo/asset/urls.py) + # ----------------------------------------------------------------------- + "view_product": [("object", Product, Permissions.Product_View, "pid")], + "view_product_components": [("object", Product, Permissions.Component_View, "pid")], + "view_product_metrics": [("object", Product, Permissions.Product_View, "pid")], + "async_burndown_metrics": [("object", Product, Permissions.Product_View, "pid")], + "view_engagements": [("object", Product, Permissions.Engagement_View, "pid")], + "edit_product": [("object", Product, Permissions.Product_Edit, "pid")], + "delete_product": [("object", Product, Permissions.Product_Delete, "pid")], + "new_eng_for_prod": [("object", Product, Permissions.Engagement_Add, "pid")], + "new_eng_for_prod_cicd": [("object", Product, Permissions.Engagement_Add, "pid")], + "new_tech_for_prod": [("object", Product, Permissions.Technology_Add, "pid")], + "edit_technology": [("object", App_Analysis, Permissions.Technology_Edit, "tid")], + "delete_technology": [("object", App_Analysis, Permissions.Technology_Delete, "tid")], + "add_meta_data": [("object", Product, Permissions.Product_Edit, "pid")], + "edit_meta_data": [("object", Product, Permissions.Product_Edit, "pid")], + "edit_notifications": [("object", Product, Permissions.Product_View, "pid")], + "engagement_presets": [("object", Product, Permissions.Product_View, "pid")], + "edit_engagement_presets": [("object", Product, Permissions.Product_Edit, "pid")], + "add_engagement_presets": [("object", Product, Permissions.Product_Edit, "pid")], + "delete_engagement_presets": [("object", Product, Permissions.Product_Edit, "pid")], + "add_product_member": [("object", Product, Permissions.Product_Manage_Members, "pid")], + "edit_product_member": [("object", Product_Member, Permissions.Product_Manage_Members, "memberid")], + "delete_product_member": [("object", Product_Member, Permissions.Product_Member_Delete, "memberid")], + "add_api_scan_configuration": [("object", Product, Permissions.Product_API_Scan_Configuration_Add, "pid")], + "view_api_scan_configurations": [("object", Product, Permissions.Product_View, "pid")], + "edit_api_scan_configuration": [("object", Product_API_Scan_Configuration, Permissions.Product_API_Scan_Configuration_Edit, "pascid")], + "delete_api_scan_configuration": [("object", Product_API_Scan_Configuration, Permissions.Product_API_Scan_Configuration_Delete, "pascid")], + "add_product_group": [("object", Product, Permissions.Product_Group_Add, "pid")], + "edit_product_group": [("object", Product_Group, Permissions.Product_Group_Edit, "groupid")], + "delete_product_group": [("object", Product_Group, Permissions.Product_Group_Delete, "groupid")], + + # ----------------------------------------------------------------------- + # Engagement (dojo/engagement/views.py -> dojo/engagement/urls.py) + # ----------------------------------------------------------------------- + "edit_engagement": [("object", Engagement, Permissions.Engagement_Edit, "eid")], + "delete_engagement": [("object", Engagement, Permissions.Engagement_Delete, "eid")], + "copy_engagement": [("object", Engagement, Permissions.Engagement_Edit, "eid")], + "add_tests": [("object", Engagement, Permissions.Test_Add, "eid")], + "close_engagement": [("object", Engagement, Permissions.Engagement_Edit, "eid")], + "engagement_unlink_jira": [("object", Engagement, Permissions.Engagement_Edit, "eid")], + "reopen_engagement": [("object", Engagement, Permissions.Engagement_Edit, "eid")], + "complete_checklist": [("object", Engagement, Permissions.Engagement_Edit, "eid")], + "add_risk_acceptance": [("object", Engagement, Permissions.Risk_Acceptance, "eid")], + "view_risk_acceptance": [("object", Engagement, Permissions.Engagement_View, "eid")], + "edit_risk_acceptance": [("object", Engagement, Permissions.Risk_Acceptance, "eid")], + "expire_risk_acceptance": [("object", Engagement, Permissions.Risk_Acceptance, "eid")], + "reinstate_risk_acceptance": [("object", Engagement, Permissions.Risk_Acceptance, "eid")], + "delete_risk_acceptance": [("object", Engagement, Permissions.Risk_Acceptance, "eid")], + "download_risk_acceptance": [("object", Engagement, Permissions.Engagement_View, "eid")], + "upload_threatmodel": [("object", Engagement, Permissions.Engagement_Edit, "eid")], + "view_threatmodel": [("object", Engagement, Permissions.Engagement_View, "eid")], + "engagement_ics": [("object", Engagement, Permissions.Engagement_View, "eid")], + + # ----------------------------------------------------------------------- + # Test (dojo/test/views.py -> dojo/test/urls.py) + # ----------------------------------------------------------------------- + "edit_test": [("object", Test, Permissions.Test_Edit, "tid")], + "delete_test": [("object", Test, Permissions.Test_Delete, "tid")], + "copy_test": [("object", Test, Permissions.Test_Edit, "tid")], + "test_ics": [("object", Test, Permissions.Test_View, "tid")], + "add_finding_from_template": [("object", Test, Permissions.Finding_Add, "tid")], + "search": [("object", Test, Permissions.Test_View, "tid")], + + # ----------------------------------------------------------------------- + # Finding (dojo/finding/views.py -> dojo/finding/urls.py) + # ----------------------------------------------------------------------- + "close_finding": [("object", Finding, Permissions.Finding_Edit, "fid")], + "verify_finding": [("object", Finding, Permissions.Finding_Edit, "fid")], + "defect_finding_review": [("object", Finding, Permissions.Finding_Edit, "fid")], + "reopen_finding": [("object", Finding, Permissions.Finding_Edit, "fid")], + "copy_finding": [("object", Finding, Permissions.Finding_Edit, "fid")], + "remediation_date": [("object", Finding, Permissions.Finding_Edit, "fid")], + "touch_finding": [("object", Finding, Permissions.Finding_Edit, "fid")], + "simple_risk_accept_finding": [("object", Finding, Permissions.Risk_Acceptance, "fid")], + "risk_unaccept_finding": [("object", Finding, Permissions.Risk_Acceptance, "fid")], + "request_finding_review": [("object", Finding, Permissions.Finding_View, "fid")], + "clear_finding_review": [("object", Finding, Permissions.Finding_Edit, "fid")], + "mktemplate": [("global", Permissions.Finding_Add)], + "find_template_to_apply": [("object", Finding, Permissions.Finding_Edit, "fid")], + "choose_finding_template_options": [("object", Finding, Permissions.Finding_Edit, "fid")], + "apply_template_to_finding": [("object", Finding, Permissions.Finding_Edit, "fid")], + "merge_finding": [("object", Product, Permissions.Finding_Edit, "pid")], + "merge_finding_product": [("object", Product, Permissions.Finding_Edit, "pid")], + "mark_finding_duplicate": [("object", Finding, Permissions.Finding_Edit, "original_id")], + "reset_finding_duplicate_status": [("object", Finding, Permissions.Finding_Edit, "duplicate_id")], + "set_finding_as_original": [("object", Finding, Permissions.Finding_Edit, "finding_id")], + "finding_unlink_jira": [("object", Finding, Permissions.Finding_Edit, "fid")], + "finding_push_to_jira": [("object", Finding, Permissions.Finding_Edit, "fid")], + + # Finding templates + "templates": [("global", Permissions.Finding_Edit)], + "export_template": [("global", Permissions.Finding_Edit)], + "add_template": [("global", Permissions.Finding_Add)], + "edit_template": [("global", Permissions.Finding_Edit)], + "delete_template": [("global", Permissions.Finding_Delete)], + + # Stub findings + "add_stub_finding": [("object", Test, Permissions.Finding_Add, "tid")], + "delete_stub_finding": [("object", Stub_Finding, Permissions.Finding_Delete, "fid")], + "promote_to_finding": [("object", Stub_Finding, Permissions.Finding_Edit, "fid")], + + # ----------------------------------------------------------------------- + # Finding Group (dojo/finding_group/views.py -> dojo/finding_group/urls.py) + # ----------------------------------------------------------------------- + "view_finding_group": [("object", Finding_Group, Permissions.Finding_Group_View, "fgid")], + "delete_finding_group": [("object", Finding_Group, Permissions.Finding_Group_Delete, "fgid")], + "finding_group_push_to_jira": [("object", Finding_Group, Permissions.Finding_Group_Edit, "fgid")], + "finding_group_unlink_jira": [("object", Finding_Group, Permissions.Finding_Group_Edit, "fgid")], + + # ----------------------------------------------------------------------- + # Endpoint (dojo/endpoint/views.py -> dojo/endpoint/urls.py) + # ----------------------------------------------------------------------- + "view_endpoint": [("object", Endpoint, Permissions.Location_View, "eid")], + "view_endpoint_host": [("object", Endpoint, Permissions.Location_View, "eid")], + "edit_endpoint": [("object", Endpoint, Permissions.Location_Edit, "eid")], + "add_endpoint": [("object", Product, Permissions.Location_Add, "pid")], + "delete_endpoint": [("object", Endpoint, Permissions.Location_Delete, "eid")], + "add_endpoint_meta_data": [("object", Endpoint, Permissions.Location_Edit, "eid")], + "edit_endpoint_meta_data": [("object", Endpoint, Permissions.Location_Edit, "eid")], + "endpoints_status_bulk": [("object", Finding, Permissions.Finding_Edit, "fid")], + "import_endpoint_meta": [("object", Product, Permissions.Location_Edit, "pid")], + "endpoint_report": [("object", Endpoint, Permissions.Location_View, "eid")], + "endpoint_host_report": [("object", Endpoint, Permissions.Location_View, "eid")], + + # ----------------------------------------------------------------------- + # URL / Location UI (dojo/url/ui/views.py -> dojo/url/ui/urls.py) + # + # These URL names overlap with the endpoint module above. Since Django + # uses the last-registered pattern for reverse() and the middleware reads + # view_kwargs from the matched pattern, the kwarg names from the actually + # matched URL are used. The endpoint entries above use "eid"; if the + # url/ui pattern matched instead, "location_id" will be present and the + # middleware will fall back (skip checks where the kwarg is missing). + # + # Unique URL names from url/ui: + # ----------------------------------------------------------------------- + "add_endpoint_to_product": [("object", Product, Permissions.Location_Add, "product_id")], + "add_endpoint_to_finding": [("object", Product, Permissions.Location_Add, "finding_id")], + + # ----------------------------------------------------------------------- + # Credential (dojo/cred/views.py -> dojo/cred/urls.py) + # ----------------------------------------------------------------------- + "add_cred": [("config", Permissions.Credential_Add)], + "view_cred_details": [("config", Permissions.Credential_View)], + "edit_cred": [("config", Permissions.Credential_Edit)], + "delete_cred": [("config", Permissions.Credential_Delete)], + "cred": [("config", Permissions.Credential_View)], + "all_cred_product": [("object", Product, Permissions.Product_Edit, "pid")], + "new_cred_product": [("object", Product, Permissions.Product_Edit, "pid")], + "view_cred_product": [ + ("object", Product, Permissions.Product_View, "pid"), + ("object", Cred_Mapping, Permissions.Credential_View, "ttid"), + ], + "edit_cred_product": [ + ("object", Product, Permissions.Product_Edit, "pid"), + ("object", Cred_Mapping, Permissions.Credential_Edit, "ttid"), + ], + "delete_cred_product": [ + ("object", Product, Permissions.Product_Edit, "pid"), + ("object", Cred_Mapping, Permissions.Credential_Delete, "ttid"), + ], + "new_cred_product_engagement": [("object", Engagement, Permissions.Engagement_Edit, "eid")], + "view_cred_product_engagement": [ + ("object", Engagement, Permissions.Engagement_View, "eid"), + ("object", Cred_Mapping, Permissions.Credential_View, "ttid"), + ], + "delete_cred_engagement": [ + ("object", Engagement, Permissions.Engagement_Edit, "eid"), + ("object", Cred_Mapping, Permissions.Credential_Delete, "ttid"), + ], + "new_cred_engagement_test": [("object", Test, Permissions.Test_Edit, "tid")], + "view_cred_engagement_test": [ + ("object", Test, Permissions.Test_View, "tid"), + ("object", Cred_Mapping, Permissions.Credential_View, "ttid"), + ], + "delete_cred_test": [ + ("object", Test, Permissions.Test_Edit, "tid"), + ("object", Cred_Mapping, Permissions.Credential_Delete, "ttid"), + ], + "new_cred_finding": [("object", Finding, Permissions.Finding_Edit, "fid")], + "view_cred_finding": [ + ("object", Finding, Permissions.Finding_View, "fid"), + ("object", Cred_Mapping, Permissions.Credential_View, "ttid"), + ], + "delete_cred_finding": [ + ("object", Finding, Permissions.Finding_Edit, "fid"), + ("object", Cred_Mapping, Permissions.Credential_Delete, "ttid"), + ], + + # ----------------------------------------------------------------------- + # Group (dojo/group/views.py -> dojo/group/urls.py) + # ----------------------------------------------------------------------- + "add_group_member": [("object", Dojo_Group, Permissions.Group_Manage_Members, "gid")], + "edit_group_member": [("object", Dojo_Group_Member, Permissions.Group_Manage_Members, "mid")], + "delete_group_member": [("object", Dojo_Group_Member, Permissions.Group_Member_Delete, "mid")], + "edit_group_permissions": [("config", "auth.change_permission")], + + # ----------------------------------------------------------------------- + # Reports (dojo/reports/views.py -> dojo/reports/urls.py) + # ----------------------------------------------------------------------- + "product_type_report": [("object", Product_Type, Permissions.Product_Type_View, "ptid")], + "product_report": [("object", Product, Permissions.Product_View, "pid")], + "product_endpoint_report": [("object", Product, Permissions.Product_View, "pid")], + "engagement_report": [("object", Engagement, Permissions.Engagement_View, "eid")], + "test_report": [("object", Test, Permissions.Test_View, "tid")], + + # ----------------------------------------------------------------------- + # Tool Product (dojo/tool_product/views.py -> dojo/tool_product/urls.py) + # ----------------------------------------------------------------------- + "new_tool_product": [("object", Product, Permissions.Product_Edit, "pid")], + "all_tool_product": [("object", Product, Permissions.Product_Edit, "pid")], + "edit_tool_product": [("object", Product, Permissions.Product_Edit, "pid")], + "delete_tool_product": [("object", Product, Permissions.Product_Edit, "pid")], + + # ----------------------------------------------------------------------- + # Tool Type (dojo/tool_type/views.py -> dojo/tool_type/urls.py) + # ----------------------------------------------------------------------- + "add_tool_type": [("config", "dojo.add_tool_type")], + "edit_tool_type": [("config", "dojo.change_tool_type")], + "tool_type": [("config", "dojo.view_tool_type")], + + # ----------------------------------------------------------------------- + # Tool Config (dojo/tool_config/views.py -> dojo/tool_config/urls.py) + # ----------------------------------------------------------------------- + "add_tool_config": [("config", "dojo.add_tool_configuration")], + "edit_tool_config": [("config", "dojo.change_tool_configuration")], + "tool_config": [("config", "dojo.view_tool_configuration")], + + # ----------------------------------------------------------------------- + # Benchmark (dojo/benchmark/views.py -> dojo/benchmark/urls.py) + # ----------------------------------------------------------------------- + "view_product_benchmark": [("object", Product, Permissions.Benchmark_Edit, "pid")], + "edit_benchmark": [("object", Product, Permissions.Benchmark_Edit, "pid")], + "delete_product_benchmark": [("object", Product, Permissions.Benchmark_Delete, "pid")], + "update_product_benchmark": [("object", Product, Permissions.Benchmark_Edit, "pid")], + "update_product_benchmark_summary": [("object", Product, Permissions.Benchmark_Edit, "pid")], + + # ----------------------------------------------------------------------- + # Object / Tracked Files (dojo/object/views.py -> dojo/object/urls.py) + # ----------------------------------------------------------------------- + "new_object": [("object", Product, Permissions.Product_Tracking_Files_Add, "pid")], + "view_objects": [("object", Product, Permissions.Product_Tracking_Files_View, "pid")], + "edit_object": [("object", Product, Permissions.Product_Tracking_Files_Edit, "pid")], + "delete_object": [("object", Product, Permissions.Product_Tracking_Files_Delete, "pid")], + + # ----------------------------------------------------------------------- + # Note Type (dojo/note_type/views.py -> dojo/note_type/urls.py) + # ----------------------------------------------------------------------- + "note_type": [("config", "dojo.view_note_type")], + "edit_note_type": [("config", "dojo.change_note_type")], + "disable_note_type": [("config", "dojo.change_note_type")], + "enable_note_type": [("config", "dojo.change_note_type")], + "add_note_type": [("config", "dojo.add_note_type")], + + # ----------------------------------------------------------------------- + # SLA Config (dojo/sla_config/views.py -> dojo/sla_config/urls.py) + # ----------------------------------------------------------------------- + "new_sla_config": [("config", "dojo.add_sla_configuration")], + "edit_sla_config": [("config", "dojo.change_sla_configuration")], + "sla_config": [("config", "dojo.view_sla_configuration")], + + # ----------------------------------------------------------------------- + # Regulations (dojo/regulations/views.py -> dojo/regulations/urls.py) + # ----------------------------------------------------------------------- + "new_regulation": [("config", "dojo.add_regulation")], + "edit_regulations": [("config", "dojo.change_regulation")], + + # ----------------------------------------------------------------------- + # Development Environment (dojo/development_environment/views.py) + # ----------------------------------------------------------------------- + "add_dev_env": [("config", "dojo.add_development_environment")], + "edit_dev_env": [("config", "dojo.change_development_environment")], + + # ----------------------------------------------------------------------- + # GitHub Issue Link (dojo/github_issue_link/views.py) + # ----------------------------------------------------------------------- + "add_github": [("config", "dojo.add_github_conf")], + "github": [("config", "dojo.view_github_conf")], + "delete_github": [("config", "dojo.delete_github_conf")], + + # ----------------------------------------------------------------------- + # Test Type (dojo/test_type/views.py -> dojo/test_type/urls.py) + # ----------------------------------------------------------------------- + "add_test_type": [("config", "dojo.add_test_type")], + "edit_test_type": [("config", "dojo.change_test_type")], + + # ----------------------------------------------------------------------- + # Announcement (dojo/announcement/views.py) + # ----------------------------------------------------------------------- + "configure_announcement": [("config", "dojo.change_announcement")], + + # ----------------------------------------------------------------------- + # Banner (dojo/banner/views.py) + # ----------------------------------------------------------------------- + "configure_banner": [("config", "dojo.change_bannerconf")], + + # ----------------------------------------------------------------------- + # User (dojo/user/views.py -> dojo/user/urls.py) + # ----------------------------------------------------------------------- + "users": [("config", "auth.view_user")], + "add_user": [("config", "auth.add_user")], + "view_user": [("config", "auth.view_user")], + "edit_user": [("config", "auth.change_user")], + "delete_user": [("config", "auth.delete_user")], + "edit_user_permissions": [("config", "auth.change_permission")], + + # ----------------------------------------------------------------------- + # Survey / Questionnaire (dojo/survey/views.py -> dojo/survey/urls.py) + # ----------------------------------------------------------------------- + # Engagement-scoped questionnaire views + "delete_engagement_survey": [("object", Engagement, Permissions.Engagement_Edit, "eid")], + "assign_questionnaire": [("object", Engagement, Permissions.Engagement_Edit, "eid")], + "view_questionnaire": [("object", Engagement, Permissions.Engagement_View, "eid")], + "add_questionnaire": [("object", Engagement, Permissions.Engagement_Edit, "eid")], + + # Global questionnaire management + "edit_questionnaire": [("config", "dojo.change_engagement_survey")], + "delete_questionnaire": [("config", "dojo.delete_engagement_survey")], + "create_questionnaire": [("config", "dojo.add_engagement_survey")], + "questionnaire": [("config", "dojo.view_engagement_survey")], + "questions": [("config", "dojo.view_question")], + "create_question": [("config", "dojo.add_question")], + "edit_question": [("config", "dojo.change_question")], + "add_choices": [("config", "dojo.change_question")], + "add_empty_questionnaire": [("config", "dojo.add_engagement_survey")], + "view_empty_survey": [("config", "dojo.view_engagement_survey")], + "delete_empty_questionnaire": [("config", "dojo.delete_engagement_survey")], + "delete_general_questionnaire": [("config", "dojo.delete_engagement_survey")], +} diff --git a/dojo/banner/views.py b/dojo/banner/views.py index dcdccc77cc5..1bdf8ce2e68 100644 --- a/dojo/banner/views.py +++ b/dojo/banner/views.py @@ -5,9 +5,6 @@ from django.shortcuts import get_object_or_404, render from django.urls import reverse -from dojo.authorization.authorization_decorators import ( - user_is_configuration_authorized, -) from dojo.forms import LoginBanner from dojo.models import BannerConf from dojo.utils import add_breadcrumb @@ -15,7 +12,6 @@ logger = logging.getLogger(__name__) -@user_is_configuration_authorized("dojo.change_bannerconf") def configure_banner(request): banner_config = get_object_or_404(BannerConf, id=1) if request.method == "POST": diff --git a/dojo/benchmark/views.py b/dojo/benchmark/views.py index c694955136c..b1dc065692e 100644 --- a/dojo/benchmark/views.py +++ b/dojo/benchmark/views.py @@ -8,8 +8,6 @@ from django.urls import reverse from django.utils.translation import gettext as _ -from dojo.authorization.authorization_decorators import user_is_authorized -from dojo.authorization.roles_permissions import Permissions from dojo.forms import Benchmark_Product_SummaryForm, DeleteBenchmarkForm from dojo.models import ( Benchmark_Category, @@ -39,7 +37,6 @@ def add_benchmark(queryset, product): Benchmark_Product.objects.bulk_create(requirements) -@user_is_authorized(Product, Permissions.Benchmark_Edit, "pid") def update_benchmark(request, pid, _type): if request.method == "POST": bench_id = request.POST.get("bench_id") @@ -88,7 +85,6 @@ def update_benchmark(request, pid, _type): ) -@user_is_authorized(Product, Permissions.Benchmark_Edit, "pid") def update_benchmark_summary(request, pid, _type, summary): if request.method == "POST": product = get_object_or_404(Product, id=pid) @@ -190,7 +186,6 @@ def score_asvs(product, benchmark_type): benchmark_product_summary.save() -@user_is_authorized(Product, Permissions.Benchmark_Edit, "pid") def benchmark_view(request, pid, benchmark_type, cat=None): product = get_object_or_404(Product, id=pid) benchmark_type = get_object_or_404(Benchmark_Type, id=benchmark_type) @@ -289,7 +284,6 @@ def benchmark_view(request, pid, benchmark_type, cat=None): ) -@user_is_authorized(Product, Permissions.Benchmark_Delete, "pid") def delete(request, pid, benchmark_type): product = get_object_or_404(Product, id=pid) benchmark_product_summary = get_object_or_404( diff --git a/dojo/cred/queries.py b/dojo/cred/queries.py index a9dab0fb58e..7db3c36dc27 100644 --- a/dojo/cred/queries.py +++ b/dojo/cred/queries.py @@ -1,92 +1,23 @@ -from crum import get_current_user -from django.db.models import Q, Subquery +try: + from dojo.authorization.query_filters import get_auth_filter +except ImportError: + def get_auth_filter(key): return None -from dojo.authorization.authorization import get_roles_for_permission, user_has_global_permission -from dojo.models import Cred_Mapping, Product_Group, Product_Member, Product_Type_Group, Product_Type_Member +from dojo.models import Cred_Mapping from dojo.request_cache import cache_for_request # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_cred_mappings(permission): - """Cached - returns all cred mappings the user is authorized to see.""" - user = get_current_user() - - if user is None: - return Cred_Mapping.objects.none() - - cred_mappings = Cred_Mapping.objects.all().order_by("id") - - if user.is_superuser: - return cred_mappings - - if user_has_global_permission(user, permission): - return cred_mappings - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return cred_mappings.filter( - Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(product_id__in=Subquery(authorized_product_roles)) - | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(product_id__in=Subquery(authorized_product_groups)), - ) + impl = get_auth_filter("cred.get_authorized_cred_mappings") + if impl: + return impl(permission) + return Cred_Mapping.objects.all().order_by("id") def get_authorized_cred_mappings_for_queryset(permission, queryset): - """Filters a provided queryset for authorization. Not cached due to dynamic queryset parameter.""" - user = get_current_user() - - if user is None: - return Cred_Mapping.objects.none() - - if user.is_superuser: - return queryset - - if user_has_global_permission(user, permission): - return queryset - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return queryset.filter( - Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(product_id__in=Subquery(authorized_product_roles)) - | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(product_id__in=Subquery(authorized_product_groups)), - ) + impl = get_auth_filter("cred.get_authorized_cred_mappings_for_queryset") + if impl: + return impl(permission, queryset) + return queryset diff --git a/dojo/cred/views.py b/dojo/cred/views.py index 1ba33939733..0853ef28203 100644 --- a/dojo/cred/views.py +++ b/dojo/cred/views.py @@ -6,7 +6,6 @@ from django.urls import reverse from django.utils import timezone -from dojo.authorization.authorization_decorators import user_is_authorized, user_is_configuration_authorized from dojo.authorization.roles_permissions import Permissions from dojo.cred.queries import get_authorized_cred_mappings_for_queryset from dojo.decorators import deprecated_view @@ -17,7 +16,6 @@ logger = logging.getLogger(__name__) -@user_is_configuration_authorized(Permissions.Credential_Add) @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def new_cred(request): if request.method == "POST": @@ -40,7 +38,6 @@ def new_cred(request): return render(request, "dojo/new_cred.html", {"tform": tform}) -@user_is_authorized(Product, Permissions.Product_Edit, "pid") @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def all_cred_product(request, pid): prod = get_object_or_404(Product, id=pid) @@ -50,7 +47,6 @@ def all_cred_product(request, pid): return render(request, "dojo/view_cred_prod.html", {"product_tab": product_tab, "creds": creds, "prod": prod}) -@user_is_configuration_authorized(Permissions.Credential_Edit) @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def edit_cred(request, ttid): tool_config = Cred_User.objects.get(pk=ttid) @@ -83,7 +79,6 @@ def edit_cred(request, ttid): }) -@user_is_configuration_authorized(Permissions.Credential_View) @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def view_cred_details(request, ttid): cred = Cred_User.objects.get(pk=ttid) @@ -122,7 +117,6 @@ def view_cred_details(request, ttid): }) -@user_is_configuration_authorized(Permissions.Credential_View) @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def cred(request): confs = Cred_User.objects.all().order_by("name", "environment", "username") @@ -132,8 +126,6 @@ def cred(request): }) -@user_is_authorized(Product, Permissions.Product_View, "pid") -@user_is_authorized(Cred_Mapping, Permissions.Credential_View, "ttid") @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def view_cred_product(request, pid, ttid): cred = get_object_or_404( @@ -189,8 +181,6 @@ def view_cred_product(request, pid, ttid): }) -@user_is_authorized(Engagement, Permissions.Engagement_View, "eid") -@user_is_authorized(Cred_Mapping, Permissions.Credential_View, "ttid") @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def view_cred_product_engagement(request, eid, ttid): cred = get_object_or_404( @@ -239,8 +229,6 @@ def view_cred_product_engagement(request, eid, ttid): }) -@user_is_authorized(Test, Permissions.Test_View, "tid") -@user_is_authorized(Cred_Mapping, Permissions.Credential_View, "ttid") @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def view_cred_engagement_test(request, tid, ttid): cred = get_object_or_404( @@ -291,8 +279,6 @@ def view_cred_engagement_test(request, tid, ttid): }) -@user_is_authorized(Finding, Permissions.Finding_View, "fid") -@user_is_authorized(Cred_Mapping, Permissions.Credential_View, "ttid") @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def view_cred_finding(request, fid, ttid): cred = get_object_or_404( @@ -343,8 +329,6 @@ def view_cred_finding(request, fid, ttid): }) -@user_is_authorized(Product, Permissions.Product_Edit, "pid") -@user_is_authorized(Cred_Mapping, Permissions.Credential_Edit, "ttid") @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def edit_cred_product(request, pid, ttid): cred = get_object_or_404( @@ -372,8 +356,6 @@ def edit_cred_product(request, pid, ttid): }) -@user_is_authorized(Engagement, Permissions.Engagement_Edit, "eid") -@user_is_authorized(Cred_Mapping, Permissions.Credential_Edit, "ttid") @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def edit_cred_product_engagement(request, eid, ttid): cred = get_object_or_404( @@ -407,7 +389,6 @@ def edit_cred_product_engagement(request, eid, ttid): }) -@user_is_authorized(Product, Permissions.Product_Edit, "pid") @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def new_cred_product(request, pid): prod = get_object_or_404(Product, pk=pid) @@ -443,7 +424,6 @@ def new_cred_product(request, pid): }) -@user_is_authorized(Engagement, Permissions.Engagement_Edit, "eid") @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def new_cred_product_engagement(request, eid): eng = get_object_or_404(Engagement, pk=eid) @@ -495,7 +475,6 @@ def new_cred_product_engagement(request, eid): }) -@user_is_authorized(Test, Permissions.Test_Edit, "tid") @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def new_cred_engagement_test(request, tid): test = get_object_or_404(Test, pk=tid) @@ -546,7 +525,6 @@ def new_cred_engagement_test(request, tid): }) -@user_is_authorized(Finding, Permissions.Finding_Edit, "fid") @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def new_cred_finding(request, fid): finding = get_object_or_404(Finding, pk=fid) @@ -677,35 +655,26 @@ def delete_cred_controller(request, destination_url, elem_id, ttid): }) -@user_is_configuration_authorized(Permissions.Credential_Delete) @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def delete_cred(request, ttid): return delete_cred_controller(request, "cred", 0, ttid=ttid) -@user_is_authorized(Product, Permissions.Product_Edit, "pid") -@user_is_authorized(Cred_Mapping, Permissions.Credential_Delete, "ttid") @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def delete_cred_product(request, pid, ttid): return delete_cred_controller(request, "all_cred_product", pid, ttid) -@user_is_authorized(Engagement, Permissions.Engagement_Edit, "eid") -@user_is_authorized(Cred_Mapping, Permissions.Credential_Delete, "ttid") @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def delete_cred_engagement(request, eid, ttid): return delete_cred_controller(request, "view_engagement", eid, ttid) -@user_is_authorized(Test, Permissions.Test_Edit, "tid") -@user_is_authorized(Cred_Mapping, Permissions.Credential_Delete, "ttid") @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def delete_cred_test(request, tid, ttid): return delete_cred_controller(request, "view_test", tid, ttid) -@user_is_authorized(Finding, Permissions.Finding_Edit, "fid") -@user_is_authorized(Cred_Mapping, Permissions.Credential_Delete, "ttid") @deprecated_view("Credential Manager", removal_version="2.59.0", removal_date="June 1, 2026") def delete_cred_finding(request, fid, ttid): return delete_cred_controller(request, "view_finding", fid, ttid) diff --git a/dojo/development_environment/views.py b/dojo/development_environment/views.py index 4a0b3b20dfb..8705fdd4c7c 100644 --- a/dojo/development_environment/views.py +++ b/dojo/development_environment/views.py @@ -9,7 +9,6 @@ from django.urls import reverse from dojo.authorization.authorization import user_has_configuration_permission_or_403 -from dojo.authorization.authorization_decorators import user_is_configuration_authorized from dojo.filters import DevelopmentEnvironmentFilter from dojo.forms import Delete_Dev_EnvironmentForm, Development_EnvironmentForm from dojo.models import Development_Environment @@ -35,7 +34,6 @@ def dev_env(request): "name_words": name_words}) -@user_is_configuration_authorized("dojo.add_development_environment") def add_dev_env(request): form = Development_EnvironmentForm() if request.method == "POST": @@ -56,7 +54,6 @@ def add_dev_env(request): }) -@user_is_configuration_authorized("dojo.change_development_environment") def edit_dev_env(request, deid): de = get_object_or_404(Development_Environment, pk=deid) form1 = Development_EnvironmentForm(instance=de) diff --git a/dojo/endpoint/queries.py b/dojo/endpoint/queries.py index f8336b75f75..e2b43be1051 100644 --- a/dojo/endpoint/queries.py +++ b/dojo/endpoint/queries.py @@ -1,14 +1,11 @@ -from crum import get_current_user -from django.db.models import Q, Subquery +try: + from dojo.authorization.query_filters import get_auth_filter +except ImportError: + def get_auth_filter(key): return None -from dojo.authorization.authorization import get_roles_for_permission, user_has_global_permission from dojo.models import ( Endpoint, Endpoint_Status, - Product_Group, - Product_Member, - Product_Type_Group, - Product_Type_Member, ) from dojo.request_cache import cache_for_request @@ -16,174 +13,30 @@ # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_endpoints(permission, user=None): - """Cached - returns all endpoints the user is authorized to see.""" - if user is None: - user = get_current_user() - - if user is None: - return Endpoint.objects.none() - - endpoints = Endpoint.objects.all().order_by("id") - - if user.is_superuser: - return endpoints - - if user_has_global_permission(user, permission): - return endpoints - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return endpoints.filter( - Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(product_id__in=Subquery(authorized_product_roles)) - | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(product_id__in=Subquery(authorized_product_groups)), - ) + impl = get_auth_filter("endpoint.get_authorized_endpoints") + if impl: + return impl(permission, user=user) + return Endpoint.objects.all().order_by("id") def get_authorized_endpoints_for_queryset(permission, queryset, user=None): - """Filters a provided queryset for authorization. Not cached due to dynamic queryset parameter.""" - if user is None: - user = get_current_user() - - if user is None: - return Endpoint.objects.none() - - if user.is_superuser: - return queryset - - if user_has_global_permission(user, permission): - return queryset - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return queryset.filter( - Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(product_id__in=Subquery(authorized_product_roles)) - | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(product_id__in=Subquery(authorized_product_groups)), - ) + impl = get_auth_filter("endpoint.get_authorized_endpoints_for_queryset") + if impl: + return impl(permission, queryset, user=user) + return queryset # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_endpoint_status(permission, user=None): - """Cached - returns all endpoint statuses the user is authorized to see.""" - if user is None: - user = get_current_user() - - if user is None: - return Endpoint_Status.objects.none() - - endpoint_status = Endpoint_Status.objects.all().order_by("id") - - if user.is_superuser: - return endpoint_status - - if user_has_global_permission(user, permission): - return endpoint_status - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return endpoint_status.filter( - Q(endpoint__product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(endpoint__product_id__in=Subquery(authorized_product_roles)) - | Q(endpoint__product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(endpoint__product_id__in=Subquery(authorized_product_groups)), - ) + impl = get_auth_filter("endpoint.get_authorized_endpoint_status") + if impl: + return impl(permission, user=user) + return Endpoint_Status.objects.all().order_by("id") def get_authorized_endpoint_status_for_queryset(permission, queryset, user=None): - """Filters a provided queryset for authorization. Not cached due to dynamic queryset parameter.""" - if user is None: - user = get_current_user() - - if user is None: - return Endpoint_Status.objects.none() - - if user.is_superuser: - return queryset - - if user_has_global_permission(user, permission): - return queryset - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return queryset.filter( - Q(endpoint__product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(endpoint__product_id__in=Subquery(authorized_product_roles)) - | Q(endpoint__product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(endpoint__product_id__in=Subquery(authorized_product_groups)), - ) + impl = get_auth_filter("endpoint.get_authorized_endpoint_status_for_queryset") + if impl: + return impl(permission, queryset, user=user) + return queryset diff --git a/dojo/endpoint/views.py b/dojo/endpoint/views.py index fa57eeddc77..8d4065f2833 100644 --- a/dojo/endpoint/views.py +++ b/dojo/endpoint/views.py @@ -16,7 +16,6 @@ from django.utils import timezone from dojo.authorization.authorization import user_has_permission_or_403 -from dojo.authorization.authorization_decorators import user_is_authorized from dojo.authorization.roles_permissions import Permissions from dojo.celery_dispatch import dojo_dispatch_task from dojo.endpoint.queries import get_authorized_endpoints_for_queryset @@ -181,17 +180,14 @@ def process_endpoint_view(request, eid, *, host_view=False): }) -@user_is_authorized(Endpoint, Permissions.Location_View, "eid") def view_endpoint(request, eid): return process_endpoint_view(request, eid, host_view=False) -@user_is_authorized(Endpoint, Permissions.Location_View, "eid") def view_endpoint_host(request, eid): return process_endpoint_view(request, eid, host_view=True) -@user_is_authorized(Endpoint, Permissions.Location_Edit, "eid") def edit_endpoint(request, eid): endpoint = get_object_or_404(Endpoint, id=eid) @@ -219,7 +215,6 @@ def edit_endpoint(request, eid): }) -@user_is_authorized(Endpoint, Permissions.Location_Delete, "eid") def delete_endpoint(request, eid): endpoint = get_object_or_404(Endpoint, pk=eid) product = endpoint.product @@ -254,7 +249,6 @@ def delete_endpoint(request, eid): }) -@user_is_authorized(Product, Permissions.Location_Add, "pid") def add_endpoint(request, pid): product = get_object_or_404(Product, id=pid) template = "dojo/add_endpoint.html" @@ -306,7 +300,6 @@ def add_product_endpoint(request): }) -@user_is_authorized(Endpoint, Permissions.Location_Edit, "eid") def manage_meta_data(request, eid): endpoint = Endpoint.objects.get(id=eid) meta_data_query = DojoMeta.objects.filter(endpoint=endpoint) @@ -396,7 +389,6 @@ def endpoint_bulk_update_all(request, pid=None): return HttpResponseRedirect(reverse("endpoint", args=())) -@user_is_authorized(Finding, Permissions.Finding_Edit, "fid") def endpoint_status_bulk_update(request, fid): if request.method == "POST": post = request.POST @@ -471,7 +463,6 @@ def migrate_endpoints_view(request): }) -@user_is_authorized(Product, Permissions.Location_Edit, "pid") def import_endpoint_meta(request, pid): product = get_object_or_404(Product, id=pid) form = ImportEndpointMetaForm() @@ -506,13 +497,11 @@ def import_endpoint_meta(request, pid): }) -@user_is_authorized(Endpoint, Permissions.Location_View, "eid") def endpoint_report(request, eid): endpoint = get_object_or_404(Endpoint, id=eid) return generate_report(request, endpoint, host_view=False) -@user_is_authorized(Endpoint, Permissions.Location_View, "eid") def endpoint_host_report(request, eid): endpoint = get_object_or_404(Endpoint, id=eid) return generate_report(request, endpoint, host_view=True) diff --git a/dojo/engagement/queries.py b/dojo/engagement/queries.py index cd720eb4251..b46bd51aebf 100644 --- a/dojo/engagement/queries.py +++ b/dojo/engagement/queries.py @@ -1,48 +1,16 @@ -from crum import get_current_user -from django.db.models import Q, Subquery +try: + from dojo.authorization.query_filters import get_auth_filter +except ImportError: + def get_auth_filter(key): return None -from dojo.authorization.authorization import get_roles_for_permission, user_has_global_permission -from dojo.models import Engagement, Product_Group, Product_Member, Product_Type_Group, Product_Type_Member +from dojo.models import Engagement from dojo.request_cache import cache_for_request # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_engagements(permission): - user = get_current_user() - - if user is None: - return Engagement.objects.none() - - if user.is_superuser: - return Engagement.objects.all().order_by("id") - - if user_has_global_permission(user, permission): - return Engagement.objects.all().order_by("id") - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return Engagement.objects.filter( - Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(product_id__in=Subquery(authorized_product_roles)) - | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(product_id__in=Subquery(authorized_product_groups)), - ).order_by("id") + impl = get_auth_filter("engagement.get_authorized_engagements") + if impl: + return impl(permission) + return Engagement.objects.all().order_by("id") diff --git a/dojo/engagement/views.py b/dojo/engagement/views.py index 34c516bcc7b..d8f538e6020 100644 --- a/dojo/engagement/views.py +++ b/dojo/engagement/views.py @@ -34,7 +34,6 @@ import dojo.risk_acceptance.helper as ra_helper from dojo.authorization.authorization import user_has_permission_or_403 -from dojo.authorization.authorization_decorators import user_is_authorized from dojo.authorization.roles_permissions import Permissions from dojo.celery_dispatch import dojo_dispatch_task from dojo.endpoint.utils import save_endpoints_to_add @@ -271,7 +270,6 @@ def engagements_all(request): }) -@user_is_authorized(Engagement, Permissions.Engagement_Edit, "eid") def edit_engagement(request, eid): engagement = Engagement.objects.get(pk=eid) is_ci_cd = engagement.engagement_type == "CI/CD" @@ -347,7 +345,6 @@ def edit_engagement(request, eid): }) -@user_is_authorized(Engagement, Permissions.Engagement_Delete, "eid") def delete_engagement(request, eid): engagement = get_object_or_404(Engagement, pk=eid) product = engagement.product @@ -389,7 +386,6 @@ def delete_engagement(request, eid): }) -@user_is_authorized(Engagement, Permissions.Engagement_Edit, "eid") def copy_engagement(request, eid): engagement = get_object_or_404(Engagement, id=eid) product = engagement.product @@ -634,7 +630,6 @@ def prefetch_for_view_tests(tests): ) -@user_is_authorized(Engagement, Permissions.Test_Add, "eid") def add_tests(request, eid): eng = Engagement.objects.get(id=eid) cred_form = CredMappingForm() @@ -1182,7 +1177,6 @@ def post( return self.success_redirect(request, context) -@user_is_authorized(Engagement, Permissions.Engagement_Edit, "eid") def close_eng(request, eid): eng = Engagement.objects.get(id=eid) close_engagement(eng) @@ -1194,7 +1188,6 @@ def close_eng(request, eid): return HttpResponseRedirect(reverse("view_engagements", args=(eng.product.id, ))) -@user_is_authorized(Engagement, Permissions.Engagement_Edit, "eid") @require_POST def unlink_jira(request, eid): eng = get_object_or_404(Engagement, id=eid) @@ -1228,7 +1221,6 @@ def unlink_jira(request, eid): return HttpResponse(status=400) -@user_is_authorized(Engagement, Permissions.Engagement_Edit, "eid") def reopen_eng(request, eid): eng = Engagement.objects.get(id=eid) reopen_engagement(eng) @@ -1247,7 +1239,6 @@ def reopen_eng(request, eid): """ -@user_is_authorized(Engagement, Permissions.Engagement_Edit, "eid") def complete_checklist(request, eid): eng = get_object_or_404(Engagement, id=eid) try: @@ -1297,7 +1288,6 @@ def complete_checklist(request, eid): }) -@user_is_authorized(Engagement, Permissions.Risk_Acceptance, "eid") def add_risk_acceptance(request, eid, fid=None): eng = get_object_or_404(Engagement, id=eid) finding = None @@ -1370,12 +1360,10 @@ def add_risk_acceptance(request, eid, fid=None): }) -@user_is_authorized(Engagement, Permissions.Engagement_View, "eid") def view_risk_acceptance(request, eid, raid): return view_edit_risk_acceptance(request, eid=eid, raid=raid, edit_mode=False) -@user_is_authorized(Engagement, Permissions.Risk_Acceptance, "eid") def edit_risk_acceptance(request, eid, raid): return view_edit_risk_acceptance(request, eid=eid, raid=raid, edit_mode=True) @@ -1541,7 +1529,6 @@ def view_edit_risk_acceptance(request, eid, raid, *, edit_mode=False): }) -@user_is_authorized(Engagement, Permissions.Risk_Acceptance, "eid") def expire_risk_acceptance(request, eid, raid): risk_acceptance = get_object_or_404(prefetch_for_expiration(Risk_Acceptance.objects.all()), pk=raid) # Validate the engagement ID exists before moving forward @@ -1552,7 +1539,6 @@ def expire_risk_acceptance(request, eid, raid): return redirect_to_return_url_or_else(request, reverse("view_risk_acceptance", args=(eid, raid))) -@user_is_authorized(Engagement, Permissions.Risk_Acceptance, "eid") def reinstate_risk_acceptance(request, eid, raid): risk_acceptance = get_object_or_404(prefetch_for_expiration(Risk_Acceptance.objects.all()), pk=raid) eng = get_object_or_404(Engagement.objects.filter(risk_acceptance=risk_acceptance), pk=eid) @@ -1564,7 +1550,6 @@ def reinstate_risk_acceptance(request, eid, raid): return redirect_to_return_url_or_else(request, reverse("view_risk_acceptance", args=(eid, raid))) -@user_is_authorized(Engagement, Permissions.Risk_Acceptance, "eid") def delete_risk_acceptance(request, eid, raid): risk_acceptance = get_object_or_404(Risk_Acceptance, pk=raid) eng = get_object_or_404(Engagement.objects.filter(risk_acceptance=risk_acceptance), pk=eid) @@ -1578,7 +1563,6 @@ def delete_risk_acceptance(request, eid, raid): return HttpResponseRedirect(reverse("view_engagement", args=(eng.id, ))) -@user_is_authorized(Engagement, Permissions.Engagement_View, "eid") def download_risk_acceptance(request, eid, raid): mimetypes.init() risk_acceptance = get_object_or_404(Risk_Acceptance, pk=raid) @@ -1603,7 +1587,6 @@ def download_risk_acceptance(request, eid, raid): """ -@user_is_authorized(Engagement, Permissions.Engagement_Edit, "eid") def upload_threatmodel(request, eid): eng = Engagement.objects.get(id=eid) add_breadcrumb( @@ -1636,13 +1619,11 @@ def upload_threatmodel(request, eid): }) -@user_is_authorized(Engagement, Permissions.Engagement_View, "eid") def view_threatmodel(request, eid): eng = get_object_or_404(Engagement, pk=eid) return generate_file_response_from_file_path(eng.tmodel_path) -@user_is_authorized(Engagement, Permissions.Engagement_View, "eid") def engagement_ics(request, eid): eng = get_object_or_404(Engagement, id=eid) start_date = datetime.combine(eng.target_start, datetime.min.time()) diff --git a/dojo/finding/queries.py b/dojo/finding/queries.py index 7044f3347ec..2130b91fd02 100644 --- a/dojo/finding/queries.py +++ b/dojo/finding/queries.py @@ -1,22 +1,21 @@ import logging -from crum import get_current_user from django.conf import settings -from django.db.models import Case, CharField, Count, Exists, F, Q, Subquery, Value, When +from django.db.models import Case, CharField, Count, Exists, F, Q, Value, When from django.db.models.functions import Concat from django.db.models.query import Prefetch, QuerySet -from dojo.authorization.authorization import get_roles_for_permission, user_has_global_permission +try: + from dojo.authorization.query_filters import get_auth_filter +except ImportError: + def get_auth_filter(key): return None + from dojo.location.models import LocationFindingReference from dojo.location.status import FindingLocationStatus from dojo.models import ( IMPORT_UNTOUCHED_FINDING, Endpoint_Status, Finding, - Product_Group, - Product_Member, - Product_Type_Group, - Product_Type_Member, Stub_Finding, Test_Import_Finding_Action, Vulnerability_Id, @@ -29,217 +28,42 @@ # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_findings(permission, user=None): - """Cached - returns all findings the user is authorized to see.""" - if user is None: - user = get_current_user() - if user is None: - return Finding.objects.none() - findings = Finding.objects.all().order_by("id") - - if user.is_superuser: - return findings - - if user_has_global_permission(user, permission): - return findings - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return findings.filter( - Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(test__engagement__product_id__in=Subquery(authorized_product_roles)) - | Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(test__engagement__product_id__in=Subquery(authorized_product_groups)), - ) + impl = get_auth_filter("finding.get_authorized_findings") + if impl: + return impl(permission, user=user) + return Finding.objects.all().order_by("id") def get_authorized_findings_for_queryset(permission, queryset, user=None): - """Filters a provided queryset for authorization. Not cached due to dynamic queryset parameter.""" - if user is None: - user = get_current_user() - if user is None: - return Finding.objects.none() - findings = Finding.objects.all().order_by("id") if queryset is None else queryset - - if user.is_superuser: - return findings - - if user_has_global_permission(user, permission): - return findings - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return findings.filter( - Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(test__engagement__product_id__in=Subquery(authorized_product_roles)) - | Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(test__engagement__product_id__in=Subquery(authorized_product_groups)), - ) + impl = get_auth_filter("finding.get_authorized_findings_for_queryset") + if impl: + return impl(permission, queryset, user=user) + return Finding.objects.all().order_by("id") if queryset is None else queryset # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_stub_findings(permission): - user = get_current_user() - - if user is None: - return Stub_Finding.objects.none() - - if user.is_superuser: - return Stub_Finding.objects.all().order_by("id") - - if user_has_global_permission(user, permission): - return Stub_Finding.objects.all().order_by("id") - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return Stub_Finding.objects.filter( - Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(test__engagement__product_id__in=Subquery(authorized_product_roles)) - | Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(test__engagement__product_id__in=Subquery(authorized_product_groups)), - ).order_by("id") + impl = get_auth_filter("finding.get_authorized_stub_findings") + if impl: + return impl(permission) + return Stub_Finding.objects.all().order_by("id") # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_vulnerability_ids(permission, user=None): - """Cached - returns all vulnerability IDs the user is authorized to see.""" - if user is None: - user = get_current_user() - - if user is None: - return Vulnerability_Id.objects.none() - - vulnerability_ids = Vulnerability_Id.objects.all() - - if user.is_superuser: - return vulnerability_ids - - if user_has_global_permission(user, permission): - return vulnerability_ids - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return vulnerability_ids.filter( - Q(finding__test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(finding__test__engagement__product_id__in=Subquery(authorized_product_roles)) - | Q(finding__test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(finding__test__engagement__product_id__in=Subquery(authorized_product_groups)), - ) + impl = get_auth_filter("finding.get_authorized_vulnerability_ids") + if impl: + return impl(permission, user=user) + return Vulnerability_Id.objects.all() def get_authorized_vulnerability_ids_for_queryset(permission, queryset, user=None): - """Filters a provided queryset for authorization. Not cached due to dynamic queryset parameter.""" - if user is None: - user = get_current_user() - - if user is None: - return Vulnerability_Id.objects.none() - - if user.is_superuser: - return queryset - - if user_has_global_permission(user, permission): - return queryset - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return queryset.filter( - Q(finding__test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(finding__test__engagement__product_id__in=Subquery(authorized_product_roles)) - | Q(finding__test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(finding__test__engagement__product_id__in=Subquery(authorized_product_groups)), - ) + impl = get_auth_filter("finding.get_authorized_vulnerability_ids_for_queryset") + if impl: + return impl(permission, queryset, user=user) + return queryset def prefetch_for_findings(findings, prefetch_type="all", *, exclude_untouched=True): diff --git a/dojo/finding/views.py b/dojo/finding/views.py index e64bea6a29f..5dfeb9da65e 100644 --- a/dojo/finding/views.py +++ b/dojo/finding/views.py @@ -32,10 +32,6 @@ import dojo.finding.helper as finding_helper import dojo.risk_acceptance.helper as ra_helper from dojo.authorization.authorization import user_has_global_permission_or_403, user_has_permission_or_403 -from dojo.authorization.authorization_decorators import ( - user_has_global_permission, - user_is_authorized, -) from dojo.authorization.roles_permissions import Permissions from dojo.celery_dispatch import dojo_dispatch_task from dojo.decorators import deprecated_view @@ -1164,7 +1160,6 @@ def post(self, request: HttpRequest, finding_id): raise PermissionDenied -@user_is_authorized(Finding, Permissions.Finding_Edit, "fid") def close_finding(request, fid): finding = get_object_or_404(Finding, id=fid) # in order to close a finding, we need to capture why it was closed @@ -1234,7 +1229,6 @@ def close_finding(request, fid): ) -@user_is_authorized(Finding, Permissions.Finding_Edit, "fid") def verify_finding(request, fid): finding = get_object_or_404(Finding, id=fid) @@ -1293,7 +1287,6 @@ def verify_finding(request, fid): ) -@user_is_authorized(Finding, Permissions.Finding_Edit, "fid") def defect_finding_review(request, fid): finding = get_object_or_404(Finding, id=fid) # in order to close a finding, we need to capture why it was closed @@ -1379,11 +1372,6 @@ def defect_finding_review(request, fid): ) -@user_is_authorized( - Finding, - Permissions.Finding_Edit, - "fid", -) def reopen_finding(request, fid): finding = get_object_or_404(Finding, id=fid) finding.active = True @@ -1430,7 +1418,6 @@ def reopen_finding(request, fid): return HttpResponseRedirect(reverse("view_finding", args=(finding.id,))) -@user_is_authorized(Finding, Permissions.Finding_Edit, "fid") def copy_finding(request, fid): finding = get_object_or_404(Finding, id=fid) product = finding.test.engagement.product @@ -1487,7 +1474,6 @@ def copy_finding(request, fid): ) -@user_is_authorized(Finding, Permissions.Finding_Edit, "fid") def remediation_date(request, fid): finding = get_object_or_404(Finding, id=fid) user = get_object_or_404(Dojo_User, id=request.user.id) @@ -1524,7 +1510,6 @@ def remediation_date(request, fid): ) -@user_is_authorized(Finding, Permissions.Finding_Edit, "fid") def touch_finding(request, fid): finding = get_object_or_404(Finding, id=fid) finding.last_reviewed = timezone.now() @@ -1535,7 +1520,6 @@ def touch_finding(request, fid): ) -@user_is_authorized(Finding, Permissions.Risk_Acceptance, "fid") def simple_risk_accept(request, fid): finding = get_object_or_404(Finding, id=fid) @@ -1553,7 +1537,6 @@ def simple_risk_accept(request, fid): ) -@user_is_authorized(Finding, Permissions.Risk_Acceptance, "fid") def risk_unaccept(request, fid): finding = get_object_or_404(Finding, id=fid) ra_helper.risk_unaccept(request.user, finding) @@ -1570,7 +1553,6 @@ def risk_unaccept(request, fid): ) -@user_is_authorized(Finding, Permissions.Finding_View, "fid") def request_finding_review(request, fid): finding = get_object_or_404(Finding, id=fid) user = get_object_or_404(Dojo_User, id=request.user.id) @@ -1660,7 +1642,6 @@ def request_finding_review(request, fid): ) -@user_is_authorized(Finding, Permissions.Finding_Edit, "fid") def clear_finding_review(request, fid): finding = get_object_or_404(Finding, id=fid) user = get_object_or_404(Dojo_User, id=request.user.id) @@ -1740,8 +1721,8 @@ def clear_finding_review(request, fid): ) -@user_has_global_permission(Permissions.Finding_Add) def mktemplate(request, fid): + user_has_global_permission_or_403(request.user, Permissions.Finding_Add) finding = get_object_or_404(Finding, id=fid) templates = Finding_Template.objects.filter(title=finding.title) if len(templates) > 0: @@ -1800,7 +1781,6 @@ def mktemplate(request, fid): return HttpResponseRedirect(reverse("view_finding", args=(finding.id,))) -@user_is_authorized(Finding, Permissions.Finding_Edit, "fid") def find_template_to_apply(request, fid): # Templates may contain sensitive data from any product; require global permission # to match the authorization level of the /template list view @@ -1871,9 +1851,9 @@ def find_template_to_apply(request, fid): ) -@user_is_authorized(Finding, Permissions.Finding_Edit, "fid") def choose_finding_template_options(request, tid, fid): finding = get_object_or_404(Finding, id=fid) + user_has_permission_or_403(request.user, finding, Permissions.Finding_Edit) template = get_object_or_404(Finding_Template, id=tid) data = finding.__dict__.copy() # Remove tags and other non-serializable fields @@ -1959,9 +1939,9 @@ def choose_finding_template_options(request, tid, fid): ) -@user_is_authorized(Finding, Permissions.Finding_Edit, "fid") def apply_template_to_finding(request, fid, tid): finding = get_object_or_404(Finding, id=fid) + user_has_permission_or_403(request.user, finding, Permissions.Finding_Edit) template = get_object_or_404(Finding_Template, id=tid) if request.method == "POST": @@ -2025,7 +2005,6 @@ def apply_template_to_finding(request, fid, tid): return HttpResponseRedirect(reverse("view_finding", args=(finding.id,))) -@user_is_authorized(Test, Permissions.Finding_Add, "tid") def add_stub_finding(request, tid): test = get_object_or_404(Test, id=tid) if request.method == "POST": @@ -2066,7 +2045,6 @@ def add_stub_finding(request, tid): return HttpResponseRedirect(reverse("view_test", args=(tid,))) -@user_is_authorized(Stub_Finding, Permissions.Finding_Delete, "fid") @deprecated_view("Stub Findings", removal_version="2.59.0", removal_date="June 1, 2026") def delete_stub_finding(request, fid): finding = get_object_or_404(Stub_Finding, id=fid) @@ -2093,7 +2071,6 @@ def delete_stub_finding(request, fid): raise PermissionDenied -@user_is_authorized(Stub_Finding, Permissions.Finding_Edit, "fid") @deprecated_view("Stub Findings", removal_version="2.59.0", removal_date="June 1, 2026") def promote_to_finding(request, fid): finding = get_object_or_404(Stub_Finding, id=fid) @@ -2249,7 +2226,6 @@ def promote_to_finding(request, fid): ) -@user_has_global_permission(Permissions.Finding_Edit) def templates(request): templates = Finding_Template.objects.all().order_by("cwe") templates = TemplateFindingFilter(request.GET, queryset=templates) @@ -2269,7 +2245,6 @@ def templates(request): ) -@user_has_global_permission(Permissions.Finding_Edit) def export_templates_to_json(request): leads_as_json = serializers.serialize("json", Finding_Template.objects.all()) return HttpResponse(leads_as_json, content_type="application/json") @@ -2363,7 +2338,6 @@ def apply_cwe_mitigation(apply_to_findings, template, *, update=True): return count -@user_has_global_permission(Permissions.Finding_Add) def add_template(request): form = FindingTemplateForm() if request.method == "POST": @@ -2405,7 +2379,6 @@ def add_template(request): ) -@user_has_global_permission(Permissions.Finding_Edit) def edit_template(request, tid): template = get_object_or_404(Finding_Template, id=tid) initial_data = {"vulnerability_ids": "\n".join(template.vulnerability_ids)} @@ -2467,7 +2440,6 @@ def edit_template(request, tid): ) -@user_has_global_permission(Permissions.Finding_Delete) def delete_template(request, tid): template = get_object_or_404(Finding_Template, id=tid) if request.method == "POST": @@ -2545,7 +2517,6 @@ class Original(ImageSpec): return response -@user_is_authorized(Product, Permissions.Finding_Edit, "pid") def merge_finding_product(request, pid): product = get_object_or_404(Product, pk=pid) finding_to_update = request.GET.getlist("finding_to_update") @@ -2725,7 +2696,6 @@ def merge_finding_product(request, pid): }, ) - # bulk update and delete are combined, so we can't have the nice user_is_authorized decorator @@ -3320,7 +3290,6 @@ def get_missing_mandatory_notetypes(finding): return Note_Type.objects.filter(id__in=notes_to_be_added) -@user_is_authorized(Finding, Permissions.Finding_Edit, "original_id") @require_POST def mark_finding_duplicate(request, original_id, duplicate_id): @@ -3389,7 +3358,6 @@ def reset_finding_duplicate_status_internal(user, duplicate_id): return duplicate.id -@user_is_authorized(Finding, Permissions.Finding_Edit, "duplicate_id") @require_POST def reset_finding_duplicate_status(request, duplicate_id): checked_duplicate_id = reset_finding_duplicate_status_internal( @@ -3471,7 +3439,6 @@ def set_finding_as_original_internal(user, finding_id, new_original_id): return True -@user_is_authorized(Finding, Permissions.Finding_Edit, "finding_id") @require_POST def set_finding_as_original(request, finding_id, new_original_id): success = set_finding_as_original_internal( @@ -3491,7 +3458,6 @@ def set_finding_as_original(request, finding_id, new_original_id): ) -@user_is_authorized(Finding, Permissions.Finding_Edit, "fid") @require_POST def unlink_jira(request, fid): finding = get_object_or_404(Finding, id=fid) @@ -3527,7 +3493,6 @@ def unlink_jira(request, fid): return HttpResponse(status=400) -@user_is_authorized(Finding, Permissions.Finding_Edit, "fid") @require_POST def push_to_jira(request, fid): finding = get_object_or_404(Finding, id=fid) diff --git a/dojo/finding_group/queries.py b/dojo/finding_group/queries.py index 030342521b5..c615ca6be61 100644 --- a/dojo/finding_group/queries.py +++ b/dojo/finding_group/queries.py @@ -1,94 +1,23 @@ -from crum import get_current_user -from django.db.models import Q, Subquery +try: + from dojo.authorization.query_filters import get_auth_filter +except ImportError: + def get_auth_filter(key): return None -from dojo.authorization.authorization import get_roles_for_permission, user_has_global_permission -from dojo.models import Finding_Group, Product_Group, Product_Member, Product_Type_Group, Product_Type_Member +from dojo.models import Finding_Group from dojo.request_cache import cache_for_request # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_finding_groups(permission, user=None): - """Cached - returns all finding groups the user is authorized to see.""" - if user is None: - user = get_current_user() - - if user is None: - return Finding_Group.objects.none() - - finding_groups = Finding_Group.objects.all() - - if user.is_superuser: - return finding_groups - - if user_has_global_permission(user, permission): - return finding_groups - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return finding_groups.filter( - Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(test__engagement__product_id__in=Subquery(authorized_product_roles)) - | Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(test__engagement__product_id__in=Subquery(authorized_product_groups)), - ) + impl = get_auth_filter("finding_group.get_authorized_finding_groups") + if impl: + return impl(permission, user=user) + return Finding_Group.objects.all() def get_authorized_finding_groups_for_queryset(permission, queryset, user=None): - """Filters a provided queryset for authorization. Not cached due to dynamic queryset parameter.""" - if user is None: - user = get_current_user() - - if user is None: - return Finding_Group.objects.none() - - if user.is_superuser: - return queryset - - if user_has_global_permission(user, permission): - return queryset - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return queryset.filter( - Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(test__engagement__product_id__in=Subquery(authorized_product_roles)) - | Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(test__engagement__product_id__in=Subquery(authorized_product_groups)), - ) + impl = get_auth_filter("finding_group.get_authorized_finding_groups_for_queryset") + if impl: + return impl(permission, queryset, user=user) + return queryset diff --git a/dojo/finding_group/views.py b/dojo/finding_group/views.py index afdb897e98a..3de4af6674f 100644 --- a/dojo/finding_group/views.py +++ b/dojo/finding_group/views.py @@ -13,7 +13,7 @@ from django.views.decorators.http import require_POST from dojo.authorization.authorization import user_has_permission_or_403 -from dojo.authorization.authorization_decorators import user_is_authorized +from dojo.authorization.models import Global_Role from dojo.authorization.roles_permissions import Permissions from dojo.filters import ( FindingFilter, @@ -23,14 +23,13 @@ from dojo.finding.queries import prefetch_for_findings from dojo.forms import DeleteFindingGroupForm, EditFindingGroupForm, FindingBulkUpdateForm from dojo.jira import services as jira_services -from dojo.models import Engagement, Finding, Finding_Group, GITHUB_PKey, Global_Role, Product +from dojo.models import Engagement, Finding, Finding_Group, GITHUB_PKey, Product from dojo.product.queries import get_authorized_products from dojo.utils import Product_Tab, add_breadcrumb, get_page_items, get_setting, get_system_setting, get_words_for_field logger = logging.getLogger(__name__) -@user_is_authorized(Finding_Group, Permissions.Finding_Group_View, "fgid") def view_finding_group(request, fgid): finding_group = get_object_or_404(Finding_Group, pk=fgid) findings = finding_group.findings.all() @@ -121,7 +120,6 @@ def view_finding_group(request, fgid): }) -@user_is_authorized(Finding_Group, Permissions.Finding_Group_Delete, "fgid") @require_POST def delete_finding_group(request, fgid): finding_group = get_object_or_404(Finding_Group, pk=fgid) @@ -154,7 +152,6 @@ def delete_finding_group(request, fgid): }) -@user_is_authorized(Finding_Group, Permissions.Finding_Group_Edit, "fgid") @require_POST def unlink_jira(request, fgid): logger.debug("/finding_group/%s/jira/unlink", fgid) @@ -189,7 +186,6 @@ def unlink_jira(request, fgid): return HttpResponse(status=400) -@user_is_authorized(Finding_Group, Permissions.Finding_Group_Edit, "fgid") @require_POST def push_to_jira(request, fgid): logger.debug("/finding_group/%s/jira/push", fgid) diff --git a/dojo/forms.py b/dojo/forms.py index 211314915b3..c4e24093200 100644 --- a/dojo/forms.py +++ b/dojo/forms.py @@ -28,6 +28,14 @@ from tagulous.forms import TagField from dojo.authorization.authorization import user_has_configuration_permission, user_is_superuser_or_global_owner +from dojo.authorization.models import ( + Dojo_Group_Member, + Global_Role, + Product_Group, + Product_Member, + Product_Type_Group, + Product_Type_Member, +) from dojo.authorization.roles_permissions import Permissions from dojo.endpoint.utils import endpoint_filter, endpoint_get_or_create, validate_endpoints_to_add from dojo.engagement.queries import get_authorized_engagements @@ -67,7 +75,6 @@ Cred_User, Development_Environment, Dojo_Group, - Dojo_Group_Member, Dojo_User, DojoMeta, Endpoint, @@ -82,7 +89,6 @@ GITHUB_Conf, GITHUB_Issue, GITHUB_PKey, - Global_Role, Note_Type, Notes, Notification_Webhooks, @@ -90,11 +96,7 @@ Objects_Product, Product, Product_API_Scan_Configuration, - Product_Group, - Product_Member, Product_Type, - Product_Type_Group, - Product_Type_Member, Question, Regulation, Risk_Acceptance, diff --git a/dojo/github_issue_link/views.py b/dojo/github_issue_link/views.py index e0ddabd1deb..add1ee63e00 100644 --- a/dojo/github_issue_link/views.py +++ b/dojo/github_issue_link/views.py @@ -11,8 +11,6 @@ from django.views.decorators.csrf import csrf_exempt from github import Github -from dojo.authorization.authorization_decorators import user_is_configuration_authorized - # Local application/library imports from dojo.forms import DeleteGITHUBConfForm, GITHUBForm from dojo.models import GITHUB_Conf @@ -26,7 +24,6 @@ def webhook(request): return HttpResponse("") -@user_is_configuration_authorized("dojo.add_github_conf") def new_github(request): if request.method == "POST": gform = GITHUBForm(request.POST, instance=GITHUB_Conf()) @@ -59,7 +56,6 @@ def new_github(request): {"gform": gform}) -@user_is_configuration_authorized("dojo.view_github_conf") def github(request): confs = GITHUB_Conf.objects.all() add_breadcrumb(title="GitHub List", top_level=not len(request.GET), request=request) @@ -69,7 +65,6 @@ def github(request): }) -@user_is_configuration_authorized("dojo.delete_github_conf") def delete_github(request, tid): github_instance = get_object_or_404(GITHUB_Conf, pk=tid) # eng = test.engagement diff --git a/dojo/group/queries.py b/dojo/group/queries.py index 11a6718bf62..64b041fb67c 100644 --- a/dojo/group/queries.py +++ b/dojo/group/queries.py @@ -1,56 +1,41 @@ -from crum import get_current_user -from django.db.models import Subquery - -from dojo.authorization.authorization import get_roles_for_permission, user_has_configuration_permission -from dojo.authorization.roles_permissions import Permissions -from dojo.models import Dojo_Group, Dojo_Group_Member, Product_Group, Product_Type_Group, Role +try: + from dojo.authorization.query_filters import get_auth_filter +except ImportError: + def get_auth_filter(key): return None + +try: + from dojo.authorization.models import Dojo_Group_Member, Product_Group, Product_Type_Group, Role +except ImportError: + Dojo_Group_Member = None + Product_Group = None + Product_Type_Group = None + Role = None + +from dojo.models import Dojo_Group from dojo.request_cache import cache_for_request # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_groups(permission): - user = get_current_user() - - if user is None: - return Dojo_Group.objects.none() - - if user.is_superuser: - return Dojo_Group.objects.all().order_by("name") - - # Check for the case of the view_group config permission - if user_has_configuration_permission(user, "auth.view_group") or user_has_configuration_permission(user, "auth.add_group"): - return Dojo_Group.objects.all().order_by("name") - - roles = get_roles_for_permission(permission) - - # Get authorized group IDs via subquery - authorized_roles = Dojo_Group_Member.objects.filter( - user=user, role__in=roles, - ).values("group_id") - - # Filter using IN with Subquery - no annotations needed - return Dojo_Group.objects.filter( - pk__in=Subquery(authorized_roles), - ).order_by("name") + impl = get_auth_filter("group.get_authorized_groups") + if impl: + return impl(permission) + return Dojo_Group.objects.all().order_by("name") def get_authorized_group_members(permission): - user = get_current_user() - - if user is None: - return Dojo_Group_Member.objects.none() - - if user.is_superuser: - return Dojo_Group_Member.objects.all().order_by("id").select_related("role") - - groups = get_authorized_groups(permission) - return Dojo_Group_Member.objects.filter(group__in=groups).order_by("id").select_related("role") + impl = get_auth_filter("group.get_authorized_group_members") + if impl: + return impl(permission) + return Dojo_Group_Member.objects.all().order_by("id").select_related("role") def get_authorized_group_members_for_user(user): - groups = get_authorized_groups(Permissions.Group_View) - return Dojo_Group_Member.objects.filter(user=user, group__in=groups).order_by("group__name").select_related("role", "group") + impl = get_auth_filter("group.get_authorized_group_members_for_user") + if impl: + return impl(user) + return Dojo_Group_Member.objects.filter(user=user).order_by("group__name").select_related("role", "group") def get_group_members_for_group(group): diff --git a/dojo/group/utils.py b/dojo/group/utils.py index bf3fd65e9c5..0245f302c92 100644 --- a/dojo/group/utils.py +++ b/dojo/group/utils.py @@ -3,7 +3,8 @@ from django.db.models.signals import post_delete, post_save from django.dispatch import receiver -from dojo.models import Dojo_Group, Dojo_Group_Member, Role +from dojo.authorization.models import Dojo_Group_Member, Role +from dojo.models import Dojo_Group def get_auth_group_name(group, attempt=0): diff --git a/dojo/group/views.py b/dojo/group/views.py index df1e6e815b2..224edf74f46 100644 --- a/dojo/group/views.py +++ b/dojo/group/views.py @@ -18,7 +18,7 @@ user_has_permission, user_has_permission_or_403, ) -from dojo.authorization.authorization_decorators import user_is_authorized, user_is_configuration_authorized +from dojo.authorization.models import Dojo_Group_Member, Global_Role, Product_Group, Product_Type_Group from dojo.authorization.roles_permissions import Permissions from dojo.filters import GroupFilter from dojo.forms import ( @@ -40,7 +40,7 @@ ) from dojo.group.utils import get_auth_group_name from dojo.labels import get_labels -from dojo.models import Dojo_Group, Dojo_Group_Member, Global_Role, Product_Group, Product_Type_Group +from dojo.models import Dojo_Group from dojo.utils import ( add_breadcrumb, get_page_items, @@ -51,7 +51,6 @@ logger = logging.getLogger(__name__) - labels = get_labels() @@ -400,7 +399,6 @@ def post(self, request: HttpRequest): return render(request, self.get_template(), context) -@user_is_authorized(Dojo_Group, Permissions.Group_Manage_Members, "gid") def add_group_member(request, gid): group = get_object_or_404(Dojo_Group, id=gid) groupform = Add_Group_MemberForm(initial={"group": group.id}) @@ -436,7 +434,6 @@ def add_group_member(request, gid): }) -@user_is_authorized(Dojo_Group_Member, Permissions.Group_Manage_Members, "mid") def edit_group_member(request, mid): member = get_object_or_404(Dojo_Group_Member, pk=mid) memberform = Edit_Group_MemberForm(instance=member) @@ -476,7 +473,6 @@ def edit_group_member(request, mid): }) -@user_is_authorized(Dojo_Group_Member, Permissions.Group_Member_Delete, "mid") def delete_group_member(request, mid): member = get_object_or_404(Dojo_Group_Member, pk=mid) memberform = Delete_Group_MemberForm(instance=member) @@ -578,7 +574,6 @@ def add_product_type_group(request, gid): }) -@user_is_configuration_authorized("auth.change_permission") def edit_permissions(request, gid): group = get_object_or_404(Dojo_Group, id=gid) if request.method == "POST": diff --git a/dojo/importers/auto_create_context.py b/dojo/importers/auto_create_context.py index 916bbea056e..03b873a6ddb 100644 --- a/dojo/importers/auto_create_context.py +++ b/dojo/importers/auto_create_context.py @@ -7,13 +7,15 @@ from django.http.request import QueryDict from django.utils import timezone +from dojo.authorization.models import ( + Product_Member, + Product_Type_Member, + Role, +) from dojo.models import ( Engagement, Product, - Product_Member, Product_Type, - Product_Type_Member, - Role, Test, ) from dojo.utils import get_last_object_or_none, get_object_or_none diff --git a/dojo/jira/api/views.py b/dojo/jira/api/views.py index 71fa5ee6393..b23e27b8e27 100644 --- a/dojo/jira/api/views.py +++ b/dojo/jira/api/views.py @@ -2,8 +2,8 @@ from drf_spectacular.utils import extend_schema_view from rest_framework.permissions import IsAuthenticated -from dojo.api_v2 import permissions from dojo.api_v2.views import DojoModelViewSet, PrefetchDojoModelViewSet, schema_with_prefetch +from dojo.authorization import api_permissions as permissions from dojo.authorization.roles_permissions import Permissions from dojo.jira.api.serializers import ( JIRAInstanceSerializer, diff --git a/dojo/jira/queries.py b/dojo/jira/queries.py index 5ce281d2296..418e1fae69d 100644 --- a/dojo/jira/queries.py +++ b/dojo/jira/queries.py @@ -1,115 +1,25 @@ -from crum import get_current_user -from django.db.models import Q, Subquery +try: + from dojo.authorization.query_filters import get_auth_filter +except ImportError: + def get_auth_filter(key): return None -from dojo.authorization.authorization import get_roles_for_permission, user_has_global_permission -from dojo.models import JIRA_Issue, JIRA_Project, Product_Group, Product_Member, Product_Type_Group, Product_Type_Member +from dojo.models import JIRA_Issue, JIRA_Project from dojo.request_cache import cache_for_request # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_jira_projects(permission, user=None): - - if user is None: - user = get_current_user() - - if user is None: - return JIRA_Project.objects.none() - - jira_projects = JIRA_Project.objects.all().order_by("id") - - if user.is_superuser: - return jira_projects - - if user_has_global_permission(user, permission): - return jira_projects - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - # JIRA projects can be attached via engagement or product path - return jira_projects.filter( - # Engagement path - Q(engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(engagement__product_id__in=Subquery(authorized_product_roles)) - | Q(engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(engagement__product_id__in=Subquery(authorized_product_groups)) - # Product path - | Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(product_id__in=Subquery(authorized_product_roles)) - | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(product_id__in=Subquery(authorized_product_groups)), - ) + impl = get_auth_filter("jira_link.get_authorized_jira_projects") + if impl: + return impl(permission, user=user) + return JIRA_Project.objects.all().order_by("id") # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_jira_issues(permission): - user = get_current_user() - - if user is None: - return JIRA_Issue.objects.none() - - jira_issues = JIRA_Issue.objects.all().order_by("id") - - if user.is_superuser: - return jira_issues - - if user_has_global_permission(user, permission): - return jira_issues - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - # JIRA issues can be attached via engagement, finding_group, or finding path - return jira_issues.filter( - # Engagement path - Q(engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(engagement__product_id__in=Subquery(authorized_product_roles)) - | Q(engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(engagement__product_id__in=Subquery(authorized_product_groups)) - # Finding group path - | Q(finding_group__test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(finding_group__test__engagement__product_id__in=Subquery(authorized_product_roles)) - | Q(finding_group__test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(finding_group__test__engagement__product_id__in=Subquery(authorized_product_groups)) - # Finding path - | Q(finding__test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(finding__test__engagement__product_id__in=Subquery(authorized_product_roles)) - | Q(finding__test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(finding__test__engagement__product_id__in=Subquery(authorized_product_groups)), - ) + impl = get_auth_filter("jira_link.get_authorized_jira_issues") + if impl: + return impl(permission) + return JIRA_Issue.objects.all().order_by("id") diff --git a/dojo/location/api/endpoint_compat.py b/dojo/location/api/endpoint_compat.py index 964da4c0d4d..a9fe99c3f4e 100644 --- a/dojo/location/api/endpoint_compat.py +++ b/dojo/location/api/endpoint_compat.py @@ -25,10 +25,10 @@ from rest_framework.viewsets import ReadOnlyModelViewSet from dojo.api_v2 import serializers -from dojo.api_v2.permissions import check_object_permission from dojo.api_v2.prefetch import PrefetchListMixin, PrefetchRetrieveMixin from dojo.api_v2.serializers import TagListSerializerField from dojo.api_v2.views import report_generate +from dojo.authorization.api_permissions import check_object_permission from dojo.authorization.roles_permissions import Permissions from dojo.filters import CharFieldFilterANDExpression, CharFieldInFilter, OrderingFilter from dojo.location.models import LocationFindingReference, LocationProductReference diff --git a/dojo/location/api/permissions.py b/dojo/location/api/permissions.py deleted file mode 100644 index 0dd41b33aa4..00000000000 --- a/dojo/location/api/permissions.py +++ /dev/null @@ -1,46 +0,0 @@ -from rest_framework.permissions import BasePermission - -from dojo.api_v2.permissions import check_object_permission, check_post_permission -from dojo.authorization.roles_permissions import Permissions -from dojo.models import ( - Finding, - Product, -) - - -class LocationFindingReferencePermission(BasePermission): - def has_permission(self, request, view): - return check_post_permission( - request, - Finding, - "finding", - Permissions.Finding_Edit, - ) - - def has_object_permission(self, request, view, obj): - return check_object_permission( - request, - obj.finding, - Permissions.Finding_View, - Permissions.Finding_Edit, - Permissions.Finding_Edit, - ) - - -class LocationProductReferencePermission(BasePermission): - def has_permission(self, request, view): - return check_post_permission( - request, - Product, - "product", - Permissions.Product_Edit, - ) - - def has_object_permission(self, request, view, obj): - return check_object_permission( - request, - obj.product, - Permissions.Product_View, - Permissions.Product_Edit, - Permissions.Product_Edit, - ) diff --git a/dojo/location/api/views.py b/dojo/location/api/views.py index 9eb276bc6b3..a2ac3252d3d 100644 --- a/dojo/location/api/views.py +++ b/dojo/location/api/views.py @@ -3,18 +3,18 @@ from rest_framework.permissions import DjangoModelPermissions, IsAuthenticated from rest_framework.viewsets import ReadOnlyModelViewSet -from dojo.api_v2.permissions import IsSuperUser from dojo.api_v2.views import PrefetchDojoModelViewSet +from dojo.authorization.api_permissions import ( + IsSuperUser, + LocationFindingReferencePermission, + LocationProductReferencePermission, +) from dojo.authorization.roles_permissions import Permissions from dojo.location.api.filters import ( LocationFilter, LocationFindingReferenceFilter, LocationProductReferenceFilter, ) -from dojo.location.api.permissions import ( - LocationFindingReferencePermission, - LocationProductReferencePermission, -) from dojo.location.api.serializers import ( LocationFindingReferenceSerializer, LocationProductReferenceSerializer, diff --git a/dojo/location/queries.py b/dojo/location/queries.py index fae2a5b9ad8..63abeefce8d 100644 --- a/dojo/location/queries.py +++ b/dojo/location/queries.py @@ -1,11 +1,9 @@ import logging -from crum import get_current_user from django.db.models import ( Case, CharField, Count, - Exists, F, IntegerField, OuterRef, @@ -17,15 +15,15 @@ ) from django.db.models.functions import Coalesce -from dojo.authorization.authorization import get_roles_for_permission, user_has_global_permission +try: + from dojo.authorization.query_filters import get_auth_filter +except ImportError: + def get_auth_filter(key): return None + from dojo.location.models import Location, LocationFindingReference, LocationProductReference from dojo.location.status import FindingLocationStatus, ProductLocationStatus from dojo.models import ( Finding, - Product_Group, - Product_Member, - Product_Type_Group, - Product_Type_Member, ) from dojo.query_utils import build_count_subquery @@ -33,132 +31,24 @@ def get_authorized_locations(permission, queryset=None, user=None): - - if user is None: - user = get_current_user() - - if user is None: - return Location.objects.none() - - locations = Location.objects.all().order_by("id") if queryset is None else queryset - - if user.is_superuser: - return locations - - if user_has_global_permission(user, permission): - return locations - - roles = get_roles_for_permission(permission) - authorized_product_type_roles = Product_Type_Member.objects.filter( - product_type=OuterRef("products__product__prod_type_id"), - user=user, - role__in=roles) - authorized_product_roles = Product_Member.objects.filter( - product=OuterRef("products__product_id"), - user=user, - role__in=roles) - authorized_product_type_groups = Product_Type_Group.objects.filter( - product_type=OuterRef("products__product__prod_type_id"), - group__users=user, - role__in=roles) - authorized_product_groups = Product_Group.objects.filter( - product=OuterRef("products__product_id"), - group__users=user, - role__in=roles) - locations = locations.annotate( - product__prod_type__member=Exists(authorized_product_type_roles), - product__member=Exists(authorized_product_roles), - product__prod_type__authorized_group=Exists(authorized_product_type_groups), - product__authorized_group=Exists(authorized_product_groups)) - return locations.filter( - Q(product__prod_type__member=True) | Q(product__member=True) - | Q(product__prod_type__authorized_group=True) | Q(product__authorized_group=True)) + impl = get_auth_filter("location.get_authorized_locations") + if impl: + return impl(permission, queryset=queryset, user=user) + return Location.objects.all().order_by("id") if queryset is None else queryset def get_authorized_location_finding_reference(permission, queryset=None, user=None): - - if user is None: - user = get_current_user() - - if user is None: - return LocationFindingReference.objects.none() - - location_finding_reference = LocationFindingReference.objects.all().order_by("id") if queryset is None else queryset - - if user.is_superuser: - return location_finding_reference - - if user_has_global_permission(user, permission): - return location_finding_reference - - roles = get_roles_for_permission(permission) - authorized_product_type_roles = Product_Type_Member.objects.filter( - product_type=OuterRef("location__products__product__prod_type_id"), - user=user, - role__in=roles) - authorized_product_roles = Product_Member.objects.filter( - product=OuterRef("location__products__product_id"), - user=user, - role__in=roles) - authorized_product_type_groups = Product_Type_Group.objects.filter( - product_type=OuterRef("location__products__product__prod_type_id"), - group__users=user, - role__in=roles) - authorized_product_groups = Product_Group.objects.filter( - product=OuterRef("location__products__product_id"), - group__users=user, - role__in=roles) - location_finding_reference = location_finding_reference.annotate( - location__product__prod_type__member=Exists(authorized_product_type_roles), - location__product__member=Exists(authorized_product_roles), - location__product__prod_type__authorized_group=Exists(authorized_product_type_groups), - location__product__authorized_group=Exists(authorized_product_groups)) - return location_finding_reference.filter( - Q(location__product__prod_type__member=True) | Q(location__product__member=True) - | Q(location__product__prod_type__authorized_group=True) | Q(location__product__authorized_group=True)) + impl = get_auth_filter("location.get_authorized_location_finding_reference") + if impl: + return impl(permission, queryset=queryset, user=user) + return LocationFindingReference.objects.all().order_by("id") if queryset is None else queryset def get_authorized_location_product_reference(permission, queryset=None, user=None): - - if user is None: - user = get_current_user() - - if user is None: - return LocationProductReference.objects.none() - - location_product_reference = LocationProductReference.objects.all().order_by("id") if queryset is None else queryset - - if user.is_superuser: - return location_product_reference - - if user_has_global_permission(user, permission): - return location_product_reference - - roles = get_roles_for_permission(permission) - authorized_product_type_roles = Product_Type_Member.objects.filter( - product_type=OuterRef("product__prod_type_id"), - user=user, - role__in=roles) - authorized_product_roles = Product_Member.objects.filter( - product=OuterRef("product_id"), - user=user, - role__in=roles) - authorized_product_type_groups = Product_Type_Group.objects.filter( - product_type=OuterRef("product__prod_type_id"), - group__users=user, - role__in=roles) - authorized_product_groups = Product_Group.objects.filter( - product=OuterRef("product_id"), - group__users=user, - role__in=roles) - location_product_reference = location_product_reference.annotate( - location__product__prod_type__member=Exists(authorized_product_type_roles), - location__product__member=Exists(authorized_product_roles), - location__product__prod_type__authorized_group=Exists(authorized_product_type_groups), - location__product__authorized_group=Exists(authorized_product_groups)) - return location_product_reference.filter( - Q(location__product__prod_type__member=True) | Q(location__product__member=True) - | Q(location__product__prod_type__authorized_group=True) | Q(location__product__authorized_group=True)) + impl = get_auth_filter("location.get_authorized_location_product_reference") + if impl: + return impl(permission, queryset=queryset, user=user) + return LocationProductReference.objects.all().order_by("id") if queryset is None else queryset def annotate_location_counts_and_status(locations): diff --git a/dojo/management/commands/migrate_staff_users.py b/dojo/management/commands/migrate_staff_users.py index c862c0dee35..de8724b7b00 100644 --- a/dojo/management/commands/migrate_staff_users.py +++ b/dojo/management/commands/migrate_staff_users.py @@ -4,7 +4,8 @@ from django.contrib.auth.models import Permission from django.core.management.base import BaseCommand -from dojo.models import Dojo_Group, Dojo_Group_Member, Dojo_User, Role +from dojo.authorization.models import Dojo_Group_Member, Role +from dojo.models import Dojo_Group, Dojo_User logger = logging.getLogger(__name__) diff --git a/dojo/models.py b/dojo/models.py index e80e22aa099..6dec75e165d 100644 --- a/dojo/models.py +++ b/dojo/models.py @@ -296,17 +296,6 @@ def __str__(self): return self.name -class Role(models.Model): - name = models.CharField(max_length=255, unique=True) - is_owner = models.BooleanField(default=False) - - class Meta: - ordering = ("name",) - - def __str__(self): - return self.name - - class System_Settings(models.Model): enable_deduplication = models.BooleanField( default=False, @@ -614,7 +603,7 @@ class System_Settings(models.Model): help_text=_("New users will be assigned to this group."), on_delete=models.RESTRICT) default_group_role = models.ForeignKey( - Role, + "dojo.Role", null=True, blank=True, help_text=_("New users will be assigned to their default group with this role."), @@ -698,18 +687,6 @@ def get_current_datetime(): return timezone.now() -class Dojo_Group_Member(models.Model): - group = models.ForeignKey(Dojo_Group, on_delete=models.CASCADE) - user = models.ForeignKey(Dojo_User, on_delete=models.CASCADE) - role = models.ForeignKey(Role, on_delete=models.CASCADE, help_text=_("This role determines the permissions of the user to manage the group."), verbose_name=_("Group role")) - - -class Global_Role(models.Model): - user = models.OneToOneField(Dojo_User, null=True, blank=True, on_delete=models.CASCADE) - group = models.OneToOneField(Dojo_Group, null=True, blank=True, on_delete=models.CASCADE) - role = models.ForeignKey(Role, on_delete=models.CASCADE, null=True, blank=True, help_text=_("The global role will be applied to all product types and products."), verbose_name=_("Global role")) - - class Contact(models.Model): name = models.CharField(max_length=100) email = models.EmailField() @@ -1359,30 +1336,6 @@ def violates_sla(self): return findings.count() > 0 -class Product_Member(models.Model): - product = models.ForeignKey(Product, on_delete=models.CASCADE) - user = models.ForeignKey(Dojo_User, on_delete=models.CASCADE) - role = models.ForeignKey(Role, on_delete=models.CASCADE) - - -class Product_Group(models.Model): - product = models.ForeignKey(Product, on_delete=models.CASCADE) - group = models.ForeignKey(Dojo_Group, on_delete=models.CASCADE) - role = models.ForeignKey(Role, on_delete=models.CASCADE) - - -class Product_Type_Member(models.Model): - product_type = models.ForeignKey(Product_Type, on_delete=models.CASCADE) - user = models.ForeignKey(Dojo_User, on_delete=models.CASCADE) - role = models.ForeignKey(Role, on_delete=models.CASCADE) - - -class Product_Type_Group(models.Model): - product_type = models.ForeignKey(Product_Type, on_delete=models.CASCADE) - group = models.ForeignKey(Dojo_Group, on_delete=models.CASCADE) - role = models.ForeignKey(Role, on_delete=models.CASCADE) - - class Tool_Type(models.Model): name = models.CharField(max_length=200) description = models.CharField(max_length=2000, null=True, blank=True) @@ -4791,6 +4744,16 @@ def __str__(self): admin.site.register(SLA_Configuration) admin.site.register(CWE) admin.site.register(Regulation) +from dojo.authorization.models import ( # noqa: E402 + Dojo_Group_Member, + Global_Role, + Product_Group, + Product_Member, + Product_Type_Group, + Product_Type_Member, + Role, +) + admin.site.register(Global_Role) admin.site.register(Role) admin.site.register(Dojo_Group) diff --git a/dojo/note_type/views.py b/dojo/note_type/views.py index c02c92cb82f..65c908c740e 100644 --- a/dojo/note_type/views.py +++ b/dojo/note_type/views.py @@ -5,7 +5,6 @@ from django.shortcuts import get_object_or_404, render from django.urls import reverse -from dojo.authorization.authorization_decorators import user_is_configuration_authorized from dojo.filters import NoteTypesFilter from dojo.forms import DisableOrEnableNoteTypeForm, EditNoteTypeForm, NoteTypeForm from dojo.models import Note_Type @@ -14,7 +13,6 @@ logger = logging.getLogger(__name__) -@user_is_configuration_authorized("dojo.view_note_type") def note_type(request): initial_queryset = Note_Type.objects.all().order_by("name") name_words = initial_queryset.values_list("name", flat=True) @@ -30,7 +28,6 @@ def note_type(request): "name_words": name_words}) -@user_is_configuration_authorized("dojo.change_note_type") def edit_note_type(request, ntid): nt = get_object_or_404(Note_Type, pk=ntid) is_single = nt.is_single @@ -56,7 +53,6 @@ def edit_note_type(request, ntid): "nt": nt}) -@user_is_configuration_authorized("dojo.change_note_type") def disable_note_type(request, ntid): nt = get_object_or_404(Note_Type, pk=ntid) nt_form = DisableOrEnableNoteTypeForm(instance=nt) @@ -81,7 +77,6 @@ def disable_note_type(request, ntid): "nt": nt}) -@user_is_configuration_authorized("dojo.change_note_type") def enable_note_type(request, ntid): nt = get_object_or_404(Note_Type, pk=ntid) nt_form = DisableOrEnableNoteTypeForm(instance=nt) @@ -105,7 +100,6 @@ def enable_note_type(request, ntid): "nt": nt}) -@user_is_configuration_authorized("dojo.add_note_type") def add_note_type(request): form = NoteTypeForm() if request.method == "POST": diff --git a/dojo/object/views.py b/dojo/object/views.py index 9bd443d27dd..40cc57a45b2 100644 --- a/dojo/object/views.py +++ b/dojo/object/views.py @@ -6,8 +6,6 @@ from django.shortcuts import get_object_or_404, render from django.urls import reverse -from dojo.authorization.authorization_decorators import user_is_authorized -from dojo.authorization.roles_permissions import Permissions from dojo.forms import DeleteObjectsSettingsForm, ObjectSettingsForm from dojo.labels import get_labels from dojo.models import Objects_Product, Product @@ -18,7 +16,6 @@ labels = get_labels() -@user_is_authorized(Product, Permissions.Product_Tracking_Files_Add, "pid") def new_object(request, pid): page_name = labels.ASSET_TRACKED_FILES_ADD_LABEL prod = get_object_or_404(Product, id=pid) @@ -46,7 +43,6 @@ def new_object(request, pid): "pid": prod.id}) -@user_is_authorized(Product, Permissions.Product_Tracking_Files_View, "pid") def view_objects(request, pid): product = get_object_or_404(Product, id=pid) object_queryset = Objects_Product.objects.filter(product=pid).order_by("path", "folder", "artifact") @@ -61,7 +57,6 @@ def view_objects(request, pid): }) -@user_is_authorized(Product, Permissions.Product_Tracking_Files_Edit, "pid") def edit_object(request, pid, ttid): object_prod = get_object_or_404(Objects_Product, pk=ttid) product = get_object_or_404(Product, id=pid) @@ -90,7 +85,6 @@ def edit_object(request, pid, ttid): }) -@user_is_authorized(Product, Permissions.Product_Tracking_Files_Delete, "pid") def delete_object(request, pid, ttid): object_prod = get_object_or_404(Objects_Product, pk=ttid) product = get_object_or_404(Product, id=pid) diff --git a/dojo/organization/api/filters.py b/dojo/organization/api/filters.py index 14e282e2ec9..0a08db54ae4 100644 --- a/dojo/organization/api/filters.py +++ b/dojo/organization/api/filters.py @@ -1,12 +1,12 @@ from django_filters import BooleanFilter, NumberFilter from django_filters.rest_framework import FilterSet -from dojo.labels import get_labels -from dojo.models import ( - Product_Type, +from dojo.authorization.models import ( Product_Type_Group, Product_Type_Member, ) +from dojo.labels import get_labels +from dojo.models import Product_Type labels = get_labels() diff --git a/dojo/organization/api/serializers.py b/dojo/organization/api/serializers.py index 73eb68e0294..28dde163a05 100644 --- a/dojo/organization/api/serializers.py +++ b/dojo/organization/api/serializers.py @@ -2,12 +2,12 @@ from rest_framework.exceptions import PermissionDenied, ValidationError from dojo.authorization.authorization import user_has_permission -from dojo.authorization.roles_permissions import Permissions -from dojo.models import ( - Product_Type, +from dojo.authorization.models import ( Product_Type_Group, Product_Type_Member, ) +from dojo.authorization.roles_permissions import Permissions +from dojo.models import Product_Type from dojo.product_type.queries import get_authorized_product_types diff --git a/dojo/organization/api/views.py b/dojo/organization/api/views.py index 0cbbf561eaf..69d39be691b 100644 --- a/dojo/organization/api/views.py +++ b/dojo/organization/api/views.py @@ -5,16 +5,16 @@ from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response -from dojo.api_v2 import permissions from dojo.api_v2.serializers import ReportGenerateOptionSerializer, ReportGenerateSerializer from dojo.api_v2.views import PrefetchDojoModelViewSet, report_generate, schema_with_prefetch -from dojo.authorization.roles_permissions import Permissions -from dojo.models import ( - Product_Type, +from dojo.authorization import api_permissions as permissions +from dojo.authorization.models import ( Product_Type_Group, Product_Type_Member, Role, ) +from dojo.authorization.roles_permissions import Permissions +from dojo.models import Product_Type from dojo.organization.api import serializers from dojo.organization.api.filters import ( OrganizationFilterSet, diff --git a/dojo/pipeline.py b/dojo/pipeline.py index 8aaea4079bb..0bf6b8e4a7b 100644 --- a/dojo/pipeline.py +++ b/dojo/pipeline.py @@ -8,8 +8,9 @@ from social_core.backends.azuread_tenant import AzureADTenantOAuth2 from social_core.backends.google import GoogleOAuth2 +from dojo.authorization.models import Dojo_Group_Member, Product_Member, Role from dojo.authorization.roles_permissions import Permissions, Roles -from dojo.models import Dojo_Group, Dojo_Group_Member, Product, Product_Member, Product_Type, Role +from dojo.models import Dojo_Group, Product, Product_Type from dojo.product.queries import get_authorized_products logger = logging.getLogger(__name__) diff --git a/dojo/product/queries.py b/dojo/product/queries.py index 1b5efffef8c..5a343d69f0a 100644 --- a/dojo/product/queries.py +++ b/dojo/product/queries.py @@ -1,26 +1,26 @@ -from crum import get_current_user -from django.db.models import Q, Subquery +try: + from dojo.authorization.query_filters import get_auth_filter +except ImportError: + def get_auth_filter(key): return None + +try: + from dojo.authorization.models import ( + Global_Role, + Product_Group, + Product_Member, + ) +except ImportError: + Global_Role = None + Product_Group = None + Product_Member = None -from dojo.authorization.authorization import ( - get_roles_for_permission, - role_has_permission, - user_has_global_permission, - user_has_permission, -) -from dojo.authorization.roles_permissions import Permissions -from dojo.group.queries import get_authorized_groups from dojo.models import ( App_Analysis, DojoMeta, Engagement_Presets, - Global_Role, Languages, Product, Product_API_Scan_Configuration, - Product_Group, - Product_Member, - Product_Type_Group, - Product_Type_Member, ) from dojo.request_cache import cache_for_request @@ -28,343 +28,101 @@ # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_products(permission, user=None): - - if user is None: - user = get_current_user() - - if user is None: - return Product.objects.none() - - if user.is_superuser: - return Product.objects.all().order_by("name") - - if user_has_global_permission(user, permission): - return Product.objects.all().order_by("name") - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return Product.objects.filter( - Q(prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(pk__in=Subquery(authorized_product_roles)) - | Q(prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(pk__in=Subquery(authorized_product_groups)), - ).order_by("name") + impl = get_auth_filter("product.get_authorized_products") + if impl: + return impl(permission, user=user) + return Product.objects.all().order_by("name") def get_authorized_members_for_product(product, permission): - user = get_current_user() - - if user.is_superuser or user_has_permission(user, product, permission): - return Product_Member.objects.filter(product=product).order_by("user__first_name", "user__last_name").select_related("role", "user") - return Product_Member.objects.none() + impl = get_auth_filter("product.get_authorized_members_for_product") + if impl: + return impl(product, permission) + return Product_Member.objects.filter(product=product).order_by("user__first_name", "user__last_name").select_related("role", "user") def get_authorized_global_members_for_product(product, permission): - user = get_current_user() - - if user.is_superuser or user_has_permission(user, product, permission): - return Global_Role.objects.filter(group=None, role__isnull=False).order_by("user__first_name", "user__last_name").select_related("role", "user") - return Global_Role.objects.none() + impl = get_auth_filter("product.get_authorized_global_members_for_product") + if impl: + return impl(product, permission) + return Global_Role.objects.filter(group=None, role__isnull=False).order_by("user__first_name", "user__last_name").select_related("role", "user") def get_authorized_groups_for_product(product, permission): - user = get_current_user() - - if user.is_superuser or user_has_permission(user, product, permission): - authorized_groups = get_authorized_groups(Permissions.Group_View) - return Product_Group.objects.filter(product=product, group__in=authorized_groups).order_by("group__name").select_related("role") - return Product_Group.objects.none() + impl = get_auth_filter("product.get_authorized_groups_for_product") + if impl: + return impl(product, permission) + return Product_Group.objects.filter(product=product).order_by("group__name").select_related("role") def get_authorized_global_groups_for_product(product, permission): - user = get_current_user() - - if user.is_superuser or user_has_permission(user, product, permission): - return Global_Role.objects.filter(user=None, role__isnull=False).order_by("group__name").select_related("role") - return Global_Role.objects.none() + impl = get_auth_filter("product.get_authorized_global_groups_for_product") + if impl: + return impl(product, permission) + return Global_Role.objects.filter(user=None, role__isnull=False).order_by("group__name").select_related("role") def get_authorized_product_members(permission): - user = get_current_user() - - if user is None: - return Product_Member.objects.none() - - if user.is_superuser: - return Product_Member.objects.all().order_by("id").select_related("role") - - if user_has_global_permission(user, permission): - return Product_Member.objects.all().order_by("id").select_related("role") - - products = get_authorized_products(permission) - return Product_Member.objects.filter(product__in=products).order_by("id").select_related("role") + impl = get_auth_filter("product.get_authorized_product_members") + if impl: + return impl(permission) + return Product_Member.objects.all().order_by("id").select_related("role") def get_authorized_product_members_for_user(user, permission): - request_user = get_current_user() - - if request_user is None: - return Product_Member.objects.none() - - if request_user.is_superuser: - return Product_Member.objects.filter(user=user).select_related("role", "product") - - if hasattr(request_user, "global_role") and request_user.global_role.role is not None and role_has_permission(request_user.global_role.role.id, permission): - return Product_Member.objects.filter(user=user).select_related("role", "product") - - products = get_authorized_products(permission) - return Product_Member.objects.filter(user=user, product__in=products).select_related("role", "product") + impl = get_auth_filter("product.get_authorized_product_members_for_user") + if impl: + return impl(user, permission) + return Product_Member.objects.filter(user=user).select_related("role", "product") def get_authorized_product_groups(permission): - user = get_current_user() - - if user is None: - return Product_Group.objects.none() - - if user.is_superuser: - return Product_Group.objects.all().order_by("id").select_related("role") - - products = get_authorized_products(permission) - return Product_Group.objects.filter(product__in=products).order_by("id").select_related("role") + impl = get_auth_filter("product.get_authorized_product_groups") + if impl: + return impl(permission) + return Product_Group.objects.all().order_by("id").select_related("role") # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_app_analysis(permission): - user = get_current_user() - - if user is None: - return App_Analysis.objects.none() - - if user.is_superuser: - return App_Analysis.objects.all().order_by("id") - - if user_has_global_permission(user, permission): - return App_Analysis.objects.all().order_by("id") - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return App_Analysis.objects.filter( - Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(product_id__in=Subquery(authorized_product_roles)) - | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(product_id__in=Subquery(authorized_product_groups)), - ).order_by("id") + impl = get_auth_filter("product.get_authorized_app_analysis") + if impl: + return impl(permission) + return App_Analysis.objects.all().order_by("id") # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_dojo_meta(permission): - user = get_current_user() - - if user is None: - return DojoMeta.objects.none() - - if user.is_superuser: - return DojoMeta.objects.all().order_by("id") - - if user_has_global_permission(user, permission): - return DojoMeta.objects.all().order_by("id") - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries for all three paths - # Product path - product_authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - product_authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - product_authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - product_authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - # DojoMeta can be attached to product, endpoint, or finding - return DojoMeta.objects.filter( - # Product path - Q(product__prod_type_id__in=Subquery(product_authorized_product_type_roles)) - | Q(product_id__in=Subquery(product_authorized_product_roles)) - | Q(product__prod_type_id__in=Subquery(product_authorized_product_type_groups)) - | Q(product_id__in=Subquery(product_authorized_product_groups)) - # Endpoint path - | Q(endpoint__product__prod_type_id__in=Subquery(product_authorized_product_type_roles)) - | Q(endpoint__product_id__in=Subquery(product_authorized_product_roles)) - | Q(endpoint__product__prod_type_id__in=Subquery(product_authorized_product_type_groups)) - | Q(endpoint__product_id__in=Subquery(product_authorized_product_groups)) - # Finding path - | Q(finding__test__engagement__product__prod_type_id__in=Subquery(product_authorized_product_type_roles)) - | Q(finding__test__engagement__product_id__in=Subquery(product_authorized_product_roles)) - | Q(finding__test__engagement__product__prod_type_id__in=Subquery(product_authorized_product_type_groups)) - | Q(finding__test__engagement__product_id__in=Subquery(product_authorized_product_groups)), - ).order_by("id") + impl = get_auth_filter("product.get_authorized_dojo_meta") + if impl: + return impl(permission) + return DojoMeta.objects.all().order_by("id") # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_languages(permission): - user = get_current_user() - - if user is None: - return Languages.objects.none() - - if user.is_superuser: - return Languages.objects.all().order_by("id") - - if user_has_global_permission(user, permission): - return Languages.objects.all().order_by("id") - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return Languages.objects.filter( - Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(product_id__in=Subquery(authorized_product_roles)) - | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(product_id__in=Subquery(authorized_product_groups)), - ).order_by("id") + impl = get_auth_filter("product.get_authorized_languages") + if impl: + return impl(permission) + return Languages.objects.all().order_by("id") # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_engagement_presets(permission): - user = get_current_user() - - if user is None: - return Engagement_Presets.objects.none() - - if user.is_superuser: - return Engagement_Presets.objects.all().order_by("id") - - if user_has_global_permission(user, permission): - return Engagement_Presets.objects.all().order_by("id") - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return Engagement_Presets.objects.filter( - Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(product_id__in=Subquery(authorized_product_roles)) - | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(product_id__in=Subquery(authorized_product_groups)), - ).order_by("id") + impl = get_auth_filter("product.get_authorized_engagement_presets") + if impl: + return impl(permission) + return Engagement_Presets.objects.all().order_by("id") # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_product_api_scan_configurations(permission): - user = get_current_user() - - if user is None: - return Product_API_Scan_Configuration.objects.none() - - if user.is_superuser: - return Product_API_Scan_Configuration.objects.all().order_by("id") - - if user_has_global_permission(user, permission): - return Product_API_Scan_Configuration.objects.all().order_by("id") - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return Product_API_Scan_Configuration.objects.filter( - Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(product_id__in=Subquery(authorized_product_roles)) - | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(product_id__in=Subquery(authorized_product_groups)), - ).order_by("id") + impl = get_auth_filter("product.get_authorized_product_api_scan_configurations") + if impl: + return impl(permission) + return Product_API_Scan_Configuration.objects.all().order_by("id") diff --git a/dojo/product/views.py b/dojo/product/views.py index 8f74c74c6ae..2ff447e434e 100644 --- a/dojo/product/views.py +++ b/dojo/product/views.py @@ -27,7 +27,10 @@ import dojo.finding.helper as finding_helper from dojo.authorization.authorization import user_has_permission, user_has_permission_or_403 -from dojo.authorization.authorization_decorators import user_is_authorized +from dojo.authorization.models import ( + Product_Group, + Product_Member, +) from dojo.authorization.roles_permissions import Permissions from dojo.components.sql_group_concat import Sql_GroupConcat from dojo.filters import ( @@ -88,8 +91,6 @@ Notifications, Product, Product_API_Scan_Configuration, - Product_Group, - Product_Member, Product_Type, System_Settings, Test, @@ -247,7 +248,6 @@ def iso_to_gregorian(iso_year, iso_week, iso_day): return start + timedelta(weeks=iso_week - 1, days=iso_day - 1) -@user_is_authorized(Product, Permissions.Product_View, "pid") def view_product(request, pid): prod_query = Product.objects.all().select_related("product_manager", "technical_contact", "team_manager", "sla_configuration") \ .prefetch_related("members") \ @@ -345,7 +345,6 @@ def view_product(request, pid): "sla": sla}) -@user_is_authorized(Product, Permissions.Component_View, "pid") def view_product_components(request, pid): prod = get_object_or_404(Product, id=pid) product_tab = Product_Tab(prod, title=str(labels.ASSET_LABEL), tab="components") @@ -564,7 +563,6 @@ def endpoint_queries(request, prod): return filters -@user_is_authorized(Product, Permissions.Product_View, "pid") def view_product_metrics(request, pid): prod = get_object_or_404(Product, id=pid) engs = Engagement.objects.filter(product=prod, active=True) @@ -797,7 +795,6 @@ def view_product_metrics(request, pid): "user": request.user}) -@user_is_authorized(Product, Permissions.Product_View, "pid") def async_burndown_metrics(request, pid): prod = get_object_or_404(Product, id=pid) open_findings_burndown = get_open_findings_burndown(prod) @@ -813,7 +810,6 @@ def async_burndown_metrics(request, pid): }) -@user_is_authorized(Product, Permissions.Engagement_View, "pid") def view_engagements(request, pid): prod = get_object_or_404(Product, id=pid) default_page_num = 10 @@ -1008,7 +1004,6 @@ def new_product(request, ptid=None): "gform": gform}) -@user_is_authorized(Product, Permissions.Product_Edit, "pid") def edit_product(request, pid): product = Product.objects.get(pk=pid) system_settings = System_Settings.objects.get() @@ -1085,7 +1080,6 @@ def edit_product(request, pid): }) -@user_is_authorized(Product, Permissions.Product_Delete, "pid") def delete_product(request, pid): product = get_object_or_404(Product, pk=pid) form = DeleteProductForm(instance=product) @@ -1134,7 +1128,6 @@ def delete_product(request, pid): "rels": rels}) -@user_is_authorized(Product, Permissions.Engagement_Add, "pid") def new_eng_for_app(request, pid, *, cicd=False): jira_project_form = None jira_epic_form = None @@ -1214,7 +1207,6 @@ def new_eng_for_app(request, pid, *, cicd=False): "jira_project_form": jira_project_form}) -@user_is_authorized(Product, Permissions.Technology_Add, "pid") def new_tech_for_prod(request, pid): if request.method == "POST": form = AppAnalysisForm(request.POST) @@ -1236,7 +1228,6 @@ def new_tech_for_prod(request, pid): "pid": pid}) -@user_is_authorized(App_Analysis, Permissions.Technology_Edit, "tid") def edit_technology(request, tid): technology = get_object_or_404(App_Analysis, id=tid) form = AppAnalysisForm(instance=technology) @@ -1257,7 +1248,6 @@ def edit_technology(request, tid): "technology": technology}) -@user_is_authorized(App_Analysis, Permissions.Technology_Delete, "tid") def delete_technology(request, tid): technology = get_object_or_404(App_Analysis, id=tid) form = DeleteAppAnalysisForm(instance=technology) @@ -1279,13 +1269,11 @@ def delete_technology(request, tid): }) -@user_is_authorized(Product, Permissions.Engagement_Add, "pid") def new_eng_for_app_cicd(request, pid): # we have to use pid=pid here as new_eng_for_app expects kwargs, because that is how django calls the function based on urls.py named groups return new_eng_for_app(request, pid=pid, cicd=True) -@user_is_authorized(Product, Permissions.Product_Edit, "pid") def manage_meta_data(request, pid): product = Product.objects.get(id=pid) meta_data_query = DojoMeta.objects.filter(product=product) @@ -1583,7 +1571,6 @@ def post(self, request: HttpRequest, product_id: int): return render(request, self.get_template(), context) -@user_is_authorized(Product, Permissions.Product_View, "pid") def engagement_presets(request, pid): prod = get_object_or_404(Product, id=pid) presets = Engagement_Presets.objects.filter(product=prod).all() @@ -1596,7 +1583,6 @@ def engagement_presets(request, pid): "prod": prod}) -@user_is_authorized(Product, Permissions.Product_Edit, "pid") def edit_engagement_presets(request, pid, eid): prod = get_object_or_404(Product, id=pid) preset = get_object_or_404(Engagement_Presets.objects.filter(product=prod), id=eid) @@ -1622,7 +1608,6 @@ def edit_engagement_presets(request, pid, eid): "prod": prod}) -@user_is_authorized(Product, Permissions.Product_Edit, "pid") def add_engagement_presets(request, pid): prod = get_object_or_404(Product, id=pid) if request.method == "POST": @@ -1645,7 +1630,6 @@ def add_engagement_presets(request, pid): return render(request, "dojo/new_params.html", {"tform": tform, "pid": pid, "product_tab": product_tab}) -@user_is_authorized(Product, Permissions.Product_Edit, "pid") def delete_engagement_presets(request, pid, eid): prod = get_object_or_404(Product, id=pid) preset = get_object_or_404(Engagement_Presets.objects.filter(product=prod), id=eid) @@ -1678,7 +1662,6 @@ def delete_engagement_presets(request, pid, eid): }) -@user_is_authorized(Product, Permissions.Product_View, "pid") def edit_notifications(request, pid): prod = get_object_or_404(Product, id=pid) if request.method == "POST": @@ -1701,7 +1684,6 @@ def edit_notifications(request, pid): return HttpResponseRedirect(reverse("view_product", args=(pid,))) -@user_is_authorized(Product, Permissions.Product_Manage_Members, "pid") def add_product_member(request, pid): product = get_object_or_404(Product, pk=pid) memberform = Add_Product_MemberForm(initial={"product": product.id}) @@ -1739,7 +1721,6 @@ def add_product_member(request, pid): }) -@user_is_authorized(Product_Member, Permissions.Product_Manage_Members, "memberid") def edit_product_member(request, memberid): member = get_object_or_404(Product_Member, pk=memberid) memberform = Edit_Product_MemberForm(instance=member) @@ -1771,7 +1752,6 @@ def edit_product_member(request, memberid): }) -@user_is_authorized(Product_Member, Permissions.Product_Member_Delete, "memberid") def delete_product_member(request, memberid): member = get_object_or_404(Product_Member, pk=memberid) memberform = Delete_Product_MemberForm(instance=member) @@ -1799,7 +1779,6 @@ def delete_product_member(request, memberid): }) -@user_is_authorized(Product, Permissions.Product_API_Scan_Configuration_Add, "pid") def add_api_scan_configuration(request, pid): product = get_object_or_404(Product, id=pid) if request.method == "POST": @@ -1843,7 +1822,6 @@ def add_api_scan_configuration(request, pid): }) -@user_is_authorized(Product, Permissions.Product_View, "pid") def view_api_scan_configurations(request, pid): product_api_scan_configurations = Product_API_Scan_Configuration.objects.filter(product=pid) @@ -1857,7 +1835,6 @@ def view_api_scan_configurations(request, pid): }) -@user_is_authorized(Product_API_Scan_Configuration, Permissions.Product_API_Scan_Configuration_Edit, "pascid") def edit_api_scan_configuration(request, pid, pascid): product_api_scan_configuration = get_object_or_404(Product_API_Scan_Configuration, id=pascid) @@ -1903,7 +1880,6 @@ def edit_api_scan_configuration(request, pid, pascid): }) -@user_is_authorized(Product_API_Scan_Configuration, Permissions.Product_API_Scan_Configuration_Delete, "pascid") def delete_api_scan_configuration(request, pid, pascid): product_api_scan_configuration = get_object_or_404(Product_API_Scan_Configuration, id=pascid) @@ -1930,7 +1906,6 @@ def delete_api_scan_configuration(request, pid, pascid): }) -@user_is_authorized(Product_Group, Permissions.Product_Group_Edit, "groupid") def edit_product_group(request, groupid): logger.error(groupid) group = get_object_or_404(Product_Group, pk=groupid) @@ -1965,7 +1940,6 @@ def edit_product_group(request, groupid): }) -@user_is_authorized(Product_Group, Permissions.Product_Group_Delete, "groupid") def delete_product_group(request, groupid): group = get_object_or_404(Product_Group, pk=groupid) groupform = Delete_Product_GroupForm(instance=group) @@ -1994,7 +1968,6 @@ def delete_product_group(request, groupid): }) -@user_is_authorized(Product, Permissions.Product_Group_Add, "pid") def add_product_group(request, pid): product = get_object_or_404(Product, pk=pid) group_form = Add_Product_GroupForm(initial={"product": product.id}) diff --git a/dojo/product_type/queries.py b/dojo/product_type/queries.py index a0ae41e2590..6de8adf6577 100644 --- a/dojo/product_type/queries.py +++ b/dojo/product_type/queries.py @@ -1,123 +1,72 @@ -from crum import get_current_user -from django.db.models import Q, Subquery - -from dojo.authorization.authorization import ( - get_roles_for_permission, - role_has_permission, - user_has_global_permission, - user_has_permission, -) -from dojo.authorization.roles_permissions import Permissions -from dojo.group.queries import get_authorized_groups -from dojo.models import Global_Role, Product_Type, Product_Type_Group, Product_Type_Member +try: + from dojo.authorization.query_filters import get_auth_filter +except ImportError: + def get_auth_filter(key): return None + +try: + from dojo.authorization.models import Global_Role, Product_Type_Group, Product_Type_Member +except ImportError: + Global_Role = None + Product_Type_Group = None + Product_Type_Member = None + +from dojo.models import Product_Type from dojo.request_cache import cache_for_request # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_product_types(permission): - user = get_current_user() - - if user is None: - return Product_Type.objects.none() - - if user.is_superuser: - return Product_Type.objects.all().order_by("name") - - if user_has_global_permission(user, permission): - return Product_Type.objects.all().order_by("name") - - roles = get_roles_for_permission(permission) - - # Get authorized product_type IDs via subqueries - authorized_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - # Filter using IN with Subquery - no annotations needed - return Product_Type.objects.filter( - Q(pk__in=Subquery(authorized_roles)) - | Q(pk__in=Subquery(authorized_groups)), - ).order_by("name") + impl = get_auth_filter("product_type.get_authorized_product_types") + if impl: + return impl(permission) + return Product_Type.objects.all().order_by("name") def get_authorized_members_for_product_type(product_type, permission): - user = get_current_user() - - if user.is_superuser or user_has_permission(user, product_type, permission): - return Product_Type_Member.objects.filter(product_type=product_type).order_by("user__first_name", "user__last_name").select_related("role", "product_type", "user") - return Product_Type_Member.objects.none() + impl = get_auth_filter("product_type.get_authorized_members_for_product_type") + if impl: + return impl(product_type, permission) + return Product_Type_Member.objects.filter(product_type=product_type).order_by("user__first_name", "user__last_name").select_related("role", "product_type", "user") def get_authorized_global_members_for_product_type(product_type, permission): - user = get_current_user() - - if user.is_superuser or user_has_permission(user, product_type, permission): - return Global_Role.objects.filter(group=None, role__isnull=False).order_by("user__first_name", "user__last_name").select_related("role", "user") - return Global_Role.objects.none() + impl = get_auth_filter("product_type.get_authorized_global_members_for_product_type") + if impl: + return impl(product_type, permission) + return Global_Role.objects.filter(group=None, role__isnull=False).order_by("user__first_name", "user__last_name").select_related("role", "user") def get_authorized_groups_for_product_type(product_type, permission): - user = get_current_user() - - if user.is_superuser or user_has_permission(user, product_type, permission): - authorized_groups = get_authorized_groups(Permissions.Group_View) - return Product_Type_Group.objects.filter(product_type=product_type, group__in=authorized_groups).order_by("group__name").select_related("role", "group") - return Product_Type_Group.objects.none() + impl = get_auth_filter("product_type.get_authorized_groups_for_product_type") + if impl: + return impl(product_type, permission) + return Product_Type_Group.objects.filter(product_type=product_type).order_by("group__name").select_related("role", "group") def get_authorized_global_groups_for_product_type(product_type, permission): - user = get_current_user() - - if user.is_superuser or user_has_permission(user, product_type, permission): - return Global_Role.objects.filter(user=None, role__isnull=False).order_by("group__name").select_related("role", "group") - return Global_Role.objects.none() + impl = get_auth_filter("product_type.get_authorized_global_groups_for_product_type") + if impl: + return impl(product_type, permission) + return Global_Role.objects.filter(user=None, role__isnull=False).order_by("group__name").select_related("role", "group") def get_authorized_product_type_members(permission): - user = get_current_user() - - if user is None: - return Product_Type_Member.objects.none() - - if user.is_superuser: - return Product_Type_Member.objects.all().order_by("id").select_related("role") - - if user_has_global_permission(user, permission): - return Product_Type_Member.objects.all().order_by("id").select_related("role") - - product_types = get_authorized_product_types(permission) - return Product_Type_Member.objects.filter(product_type__in=product_types).order_by("id").select_related("role") + impl = get_auth_filter("product_type.get_authorized_product_type_members") + if impl: + return impl(permission) + return Product_Type_Member.objects.all().order_by("id").select_related("role") def get_authorized_product_type_members_for_user(user, permission): - request_user = get_current_user() - - if request_user is None: - return Product_Type_Member.objects.none() - - if request_user.is_superuser: - return Product_Type_Member.objects.filter(user=user).select_related("role", "product_type") - - if hasattr(request_user, "global_role") and request_user.global_role.role is not None and role_has_permission(request_user.global_role.role.id, permission): - return Product_Type_Member.objects.filter(user=user).select_related("role", "product_type") - - product_types = get_authorized_product_types(permission) - return Product_Type_Member.objects.filter(user=user, product_type__in=product_types).select_related("role", "product_type") + impl = get_auth_filter("product_type.get_authorized_product_type_members_for_user") + if impl: + return impl(user, permission) + return Product_Type_Member.objects.filter(user=user).select_related("role", "product_type") def get_authorized_product_type_groups(permission): - user = get_current_user() - - if user is None: - return Product_Type_Group.objects.none() - - if user.is_superuser: - return Product_Type_Group.objects.all().order_by("id").select_related("role") - - product_types = get_authorized_product_types(permission) - return Product_Type_Group.objects.filter(product_type__in=product_types).order_by("id").select_related("role") + impl = get_auth_filter("product_type.get_authorized_product_type_groups") + if impl: + return impl(permission) + return Product_Type_Group.objects.all().order_by("id").select_related("role") diff --git a/dojo/product_type/views.py b/dojo/product_type/views.py index 51c3985ec40..e0c8790d906 100644 --- a/dojo/product_type/views.py +++ b/dojo/product_type/views.py @@ -13,7 +13,7 @@ from django.utils.translation import gettext as _ from dojo.authorization.authorization import user_has_permission -from dojo.authorization.authorization_decorators import user_has_global_permission, user_is_authorized +from dojo.authorization.models import Product_Type_Group, Product_Type_Member, Role from dojo.authorization.roles_permissions import Permissions from dojo.filters import ProductFilter, ProductFilterWithoutObjectLookups, ProductTypeFilter from dojo.forms import ( @@ -27,7 +27,7 @@ Product_TypeForm, ) from dojo.labels import get_labels -from dojo.models import Finding, Product, Product_Type, Product_Type_Group, Product_Type_Member, Role +from dojo.models import Finding, Product, Product_Type from dojo.product.queries import get_authorized_products from dojo.product_type.queries import ( get_authorized_global_groups_for_product_type, @@ -98,7 +98,6 @@ def prefetch_for_product_type(prod_types): ) -@user_has_global_permission(Permissions.Product_Type_Add) def add_product_type(request): page_name = str(labels.ORG_CREATE_LABEL) form = Product_TypeForm() @@ -124,7 +123,6 @@ def add_product_type(request): }) -@user_is_authorized(Product_Type, Permissions.Product_Type_View, "ptid") def view_product_type(request, ptid): page_name = str(labels.ORG_READ_LABEL) pt = get_object_or_404(Product_Type, pk=ptid) @@ -151,7 +149,6 @@ def view_product_type(request, ptid): }) -@user_is_authorized(Product_Type, Permissions.Product_Type_Delete, "ptid") def delete_product_type(request, ptid): product_type = get_object_or_404(Product_Type, pk=ptid) form = Delete_Product_TypeForm(instance=product_type) @@ -188,7 +185,6 @@ def delete_product_type(request, ptid): }) -@user_is_authorized(Product_Type, Permissions.Product_Type_Edit, "ptid") def edit_product_type(request, ptid): page_name = str(labels.ORG_UPDATE_LABEL) pt = get_object_or_404(Product_Type, pk=ptid) @@ -215,7 +211,6 @@ def edit_product_type(request, ptid): "members": members}) -@user_is_authorized(Product_Type, Permissions.Product_Type_Manage_Members, "ptid") def add_product_type_member(request, ptid): page_name = str(labels.ORG_USERS_ADD_LABEL) pt = get_object_or_404(Product_Type, pk=ptid) @@ -251,7 +246,6 @@ def add_product_type_member(request, ptid): }) -@user_is_authorized(Product_Type_Member, Permissions.Product_Type_Manage_Members, "memberid") def edit_product_type_member(request, memberid): page_name = str(labels.ORG_USERS_UPDATE_LABEL) member = get_object_or_404(Product_Type_Member, pk=memberid) @@ -291,7 +285,6 @@ def edit_product_type_member(request, memberid): }) -@user_is_authorized(Product_Type_Member, Permissions.Product_Type_Member_Delete, "memberid") def delete_product_type_member(request, memberid): page_name = str(labels.ORG_USERS_DELETE_LABEL) member = get_object_or_404(Product_Type_Member, pk=memberid) @@ -327,7 +320,6 @@ def delete_product_type_member(request, memberid): }) -@user_is_authorized(Product_Type, Permissions.Product_Type_Group_Add, "ptid") def add_product_type_group(request, ptid): page_name = str(labels.ORG_GROUPS_ADD_LABEL) pt = get_object_or_404(Product_Type, pk=ptid) @@ -365,7 +357,6 @@ def add_product_type_group(request, ptid): }) -@user_is_authorized(Product_Type_Group, Permissions.Product_Type_Group_Edit, "groupid") def edit_product_type_group(request, groupid): page_name = str(labels.ORG_GROUPS_UPDATE_LABEL) group = get_object_or_404(Product_Type_Group, pk=groupid) @@ -397,7 +388,6 @@ def edit_product_type_group(request, groupid): }) -@user_is_authorized(Product_Type_Group, Permissions.Product_Type_Group_Delete, "groupid") def delete_product_type_group(request, groupid): page_name = str(labels.ORG_GROUPS_DELETE_LABEL) group = get_object_or_404(Product_Type_Group, pk=groupid) diff --git a/dojo/regulations/views.py b/dojo/regulations/views.py index e9a5f1a9f55..9bbb3296190 100644 --- a/dojo/regulations/views.py +++ b/dojo/regulations/views.py @@ -8,7 +8,6 @@ from django.urls import reverse from dojo.authorization.authorization import user_has_configuration_permission_or_403 -from dojo.authorization.authorization_decorators import user_is_configuration_authorized from dojo.forms import RegulationForm from dojo.models import Regulation from dojo.utils import add_breadcrumb @@ -16,7 +15,6 @@ logger = logging.getLogger(__name__) -@user_is_configuration_authorized("dojo.add_regulation") def new_regulation(request): if request.method == "POST": tform = RegulationForm(request.POST, instance=Regulation()) @@ -34,7 +32,6 @@ def new_regulation(request): {"form": tform}) -@user_is_configuration_authorized("dojo.change_regulation") def edit_regulations(request, ttid): regulation = Regulation.objects.get(pk=ttid) if request.method == "POST" and request.POST.get("delete"): diff --git a/dojo/reports/views.py b/dojo/reports/views.py index 3c820564d42..bdabcd4e804 100644 --- a/dojo/reports/views.py +++ b/dojo/reports/views.py @@ -15,7 +15,6 @@ from openpyxl.styles import Font from dojo.authorization.authorization import user_has_permission_or_403 -from dojo.authorization.authorization_decorators import user_is_authorized from dojo.authorization.roles_permissions import Permissions from dojo.filters import ( EndpointFilter, @@ -56,10 +55,8 @@ logger = logging.getLogger(__name__) - labels = get_labels() - EXCEL_CHAR_LIMIT = 32767 @@ -251,13 +248,11 @@ def report_cover_page(request): "report_info": report_info}) -@user_is_authorized(Product_Type, Permissions.Product_Type_View, "ptid") def product_type_report(request, ptid): product_type = get_object_or_404(Product_Type, id=ptid) return generate_report(request, product_type) -@user_is_authorized(Product, Permissions.Product_View, "pid") def product_report(request, pid): product = get_object_or_404(Product, id=pid) return generate_report(request, product) @@ -268,19 +263,16 @@ def product_findings_report(request): return generate_report(request, findings) -@user_is_authorized(Engagement, Permissions.Engagement_View, "eid") def engagement_report(request, eid): engagement = get_object_or_404(Engagement, id=eid) return generate_report(request, engagement) -@user_is_authorized(Test, Permissions.Test_View, "tid") def test_report(request, tid): test = get_object_or_404(Test, id=tid) return generate_report(request, test) -@user_is_authorized(Product, Permissions.Product_View, "pid") def product_endpoint_report(request, pid): product = get_object_or_404(Product.objects.all().prefetch_related("engagement_set__test_set__test_type", "engagement_set__test_set__environment"), id=pid) if settings.V3_FEATURE_LOCATIONS: diff --git a/dojo/risk_acceptance/api.py b/dojo/risk_acceptance/api.py index 4db4e09c76d..77b4fd6a25d 100644 --- a/dojo/risk_acceptance/api.py +++ b/dojo/risk_acceptance/api.py @@ -10,8 +10,8 @@ from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response -from dojo.api_v2.permissions import UserHasRiskAcceptanceRelatedObjectPermission from dojo.api_v2.serializers import RiskAcceptanceSerializer +from dojo.authorization.api_permissions import UserHasRiskAcceptanceRelatedObjectPermission from dojo.authorization.roles_permissions import Permissions from dojo.engagement.queries import get_authorized_engagements from dojo.models import Engagement, Risk_Acceptance, User, Vulnerability_Id diff --git a/dojo/risk_acceptance/queries.py b/dojo/risk_acceptance/queries.py index a608a4d174b..495ecc0f985 100644 --- a/dojo/risk_acceptance/queries.py +++ b/dojo/risk_acceptance/queries.py @@ -1,48 +1,16 @@ -from crum import get_current_user -from django.db.models import Q, Subquery +try: + from dojo.authorization.query_filters import get_auth_filter +except ImportError: + def get_auth_filter(key): return None -from dojo.authorization.authorization import get_roles_for_permission, user_has_global_permission -from dojo.models import Product_Group, Product_Member, Product_Type_Group, Product_Type_Member, Risk_Acceptance +from dojo.models import Risk_Acceptance from dojo.request_cache import cache_for_request # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_risk_acceptances(permission): - user = get_current_user() - - if user is None: - return Risk_Acceptance.objects.none() - - if user.is_superuser: - return Risk_Acceptance.objects.all().order_by("id") - - if user_has_global_permission(user, permission): - return Risk_Acceptance.objects.all().order_by("id") - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return Risk_Acceptance.objects.filter( - Q(engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(engagement__product_id__in=Subquery(authorized_product_roles)) - | Q(engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(engagement__product_id__in=Subquery(authorized_product_groups)), - ).order_by("id") + impl = get_auth_filter("risk_acceptance.get_authorized_risk_acceptances") + if impl: + return impl(permission) + return Risk_Acceptance.objects.all().order_by("id") diff --git a/dojo/settings/settings.dist.py b/dojo/settings/settings.dist.py index 2c828aa9582..45647f0d603 100644 --- a/dojo/settings/settings.dist.py +++ b/dojo/settings/settings.dist.py @@ -1019,6 +1019,7 @@ def generate_url(scheme, double_slashes, user, password, host, port, path, param "dojo.middleware.AdditionalHeaderMiddleware", "dojo.middleware.CustomSocialAuthExceptionMiddleware", "crum.CurrentRequestUserMiddleware", + "dojo.authorization.middleware.AuthorizationMiddleware", "dojo.middleware.AsyncSearchContextMiddleware", "dojo.request_cache.middleware.RequestCacheMiddleware", "dojo.middleware.LongRunningRequestAlertMiddleware", diff --git a/dojo/sla_config/views.py b/dojo/sla_config/views.py index c07e8dadc2a..a86eb61fff0 100644 --- a/dojo/sla_config/views.py +++ b/dojo/sla_config/views.py @@ -6,7 +6,6 @@ from django.urls import reverse from dojo.authorization.authorization import user_has_configuration_permission_or_403 -from dojo.authorization.authorization_decorators import user_is_configuration_authorized from dojo.forms import SLAConfigForm from dojo.models import Product, SLA_Configuration, System_Settings from dojo.utils import add_breadcrumb @@ -14,7 +13,6 @@ logger = logging.getLogger(__name__) -@user_is_configuration_authorized("dojo.add_sla_configuration") def new_sla_config(request): if request.method == "POST": tform = SLAConfigForm(request.POST, instance=SLA_Configuration()) @@ -35,7 +33,6 @@ def new_sla_config(request): {"form": tform}) -@user_is_configuration_authorized("dojo.change_sla_configuration") def edit_sla_config(request, slaid): sla_config = SLA_Configuration.objects.get(pk=slaid) @@ -86,7 +83,6 @@ def edit_sla_config(request, slaid): }) -@user_is_configuration_authorized("dojo.view_sla_configuration") def sla_config(request): settings = System_Settings.objects.all() diff --git a/dojo/survey/views.py b/dojo/survey/views.py index 60c8cd58a15..e2bf236bb2f 100644 --- a/dojo/survey/views.py +++ b/dojo/survey/views.py @@ -18,7 +18,6 @@ user_has_permission, user_has_permission_or_403, ) -from dojo.authorization.authorization_decorators import user_is_authorized, user_is_configuration_authorized from dojo.authorization.roles_permissions import Permissions from dojo.filters import QuestionFilter, QuestionnaireFilter from dojo.forms import ( @@ -54,7 +53,6 @@ from dojo.utils import add_breadcrumb, get_page_items, get_setting -@user_is_authorized(Engagement, Permissions.Engagement_Edit, "eid") def delete_engagement_survey(request, eid, sid): engagement = get_object_or_404(Engagement, id=eid) survey = get_object_or_404(Answered_Survey.objects.filter(engagement=engagement), id=sid) @@ -160,7 +158,6 @@ def answer_questionnaire(request, eid, sid): }) -@user_is_authorized(Engagement, Permissions.Engagement_Edit, "eid") def assign_questionnaire(request, eid, sid): engagement = get_object_or_404(Engagement, id=eid) survey = get_object_or_404(Answered_Survey.objects.filter(engagement=engagement), id=sid) @@ -181,7 +178,6 @@ def assign_questionnaire(request, eid, sid): }) -@user_is_authorized(Engagement, Permissions.Engagement_View, "eid") def view_questionnaire(request, eid, sid): engagement = get_object_or_404(Engagement, id=eid) survey = get_object_or_404(Answered_Survey.objects.filter(engagement=engagement), id=sid) @@ -219,7 +215,6 @@ def get_answered_questions(survey=None, *, read_only=False): return questions -@user_is_authorized(Engagement, Permissions.Engagement_Edit, "eid") def add_questionnaire(request, eid): user = request.user engagement = get_object_or_404(Engagement, id=eid) @@ -257,7 +252,6 @@ def add_questionnaire(request, eid): }) -@user_is_configuration_authorized("dojo.change_engagement_survey") def edit_questionnaire(request, sid): survey = get_object_or_404(Engagement_Survey, id=sid) old_name = survey.name @@ -309,7 +303,6 @@ def edit_questionnaire(request, sid): }) -@user_is_configuration_authorized("dojo.delete_engagement_survey") def delete_questionnaire(request, sid): survey = get_object_or_404(Engagement_Survey, id=sid) form = Delete_Eng_Survey_Form(instance=survey) @@ -340,7 +333,6 @@ def delete_questionnaire(request, sid): }) -@user_is_configuration_authorized("dojo.add_engagement_survey") def create_questionnaire(request): form = CreateQuestionnaireForm() survey = None @@ -419,7 +411,6 @@ def edit_questionnaire_questions(request, sid): }) -@user_is_configuration_authorized("dojo.view_engagement_survey") def questionnaire(request): surveys = Engagement_Survey.objects.all() surveys = QuestionnaireFilter(request.GET, queryset=surveys) @@ -438,7 +429,6 @@ def questionnaire(request): }) -@user_is_configuration_authorized("dojo.view_question") def questions(request): questions = Question.polymorphic.all() questions = QuestionFilter(request.GET, queryset=questions) @@ -451,7 +441,6 @@ def questions(request): }) -@user_is_configuration_authorized("dojo.add_question") def create_question(request): error = False form = CreateQuestionForm() @@ -519,7 +508,6 @@ def create_question(request): }) -@user_is_configuration_authorized("dojo.change_question") def edit_question(request, qid): try: question = Question.polymorphic.get(id=qid) @@ -583,7 +571,6 @@ def edit_question(request, qid): }) -@user_is_configuration_authorized("dojo.change_question") def add_choices(request): form = AddChoicesForm() if request.method == "POST": @@ -610,7 +597,6 @@ def add_choices(request): # Empty questionnaire functions -@user_is_configuration_authorized("dojo.add_engagement_survey") def add_empty_questionnaire(request): user = request.user surveys = Engagement_Survey.objects.all() @@ -646,7 +632,6 @@ def add_empty_questionnaire(request): }) -@user_is_configuration_authorized("dojo.view_engagement_survey") def view_empty_survey(request, esid): survey = get_object_or_404(Answered_Survey, id=esid) engagement = None @@ -664,7 +649,6 @@ def view_empty_survey(request, esid): }) -@user_is_configuration_authorized("dojo.delete_engagement_survey") def delete_empty_questionnaire(request, esid): engagement = None survey = get_object_or_404(Answered_Survey, id=esid) @@ -704,7 +688,6 @@ def delete_empty_questionnaire(request, esid): }) -@user_is_configuration_authorized("dojo.delete_engagement_survey") def delete_general_questionnaire(request, esid): engagement = None questions = None diff --git a/dojo/templatetags/authorization_tags.py b/dojo/templatetags/authorization_tags.py index befe2cf27ae..b94b1c62113 100644 --- a/dojo/templatetags/authorization_tags.py +++ b/dojo/templatetags/authorization_tags.py @@ -1,54 +1,19 @@ -import crum from django import template -from dojo.authorization.authorization import user_has_configuration_permission as configuration_permission -from dojo.authorization.authorization import user_has_global_permission, user_has_permission -from dojo.authorization.roles_permissions import Permissions -from dojo.request_cache import cache_for_request +from dojo.authorization.template_filters import ( + group_has_configuration_permission, + has_configuration_permission, + has_global_permission, + has_object_permission, + user_can_clear_peer_review, + user_has_configuration_permission_without_group, +) register = template.Library() - -@register.filter -def has_object_permission(obj, permission): - return user_has_permission(crum.get_current_user(), obj, Permissions[permission]) - - -@register.filter -def has_global_permission(permission): - return user_has_global_permission(crum.get_current_user(), Permissions[permission]) - - -@register.filter -def has_configuration_permission(permission, request): - user = crum.get_current_user() if request is None else crum.get_current_user() or request.user - return configuration_permission(user, permission) - - -@cache_for_request -def get_user_permissions(user): - return user.user_permissions.all() - - -@register.filter -def user_has_configuration_permission_without_group(user, codename): - permissions = get_user_permissions(user) - return any(permission.codename == codename for permission in permissions) - - -@cache_for_request -def get_group_permissions(group): - return group.permissions.all() - - -@register.filter -def group_has_configuration_permission(group, codename): - return any(permission.codename == codename for permission in get_group_permissions(group)) - - -@register.simple_tag -def user_can_clear_peer_review(finding, user): - finding_under_review = finding.under_review - user_requesting_review = user == finding.review_requested_by - user_is_reviewer = user in finding.reviewers.all() - return finding_under_review and (user_requesting_review or user_is_reviewer) +register.filter("has_object_permission", has_object_permission) +register.filter("has_global_permission", has_global_permission) +register.filter("has_configuration_permission", has_configuration_permission) +register.filter("user_has_configuration_permission_without_group", user_has_configuration_permission_without_group) +register.filter("group_has_configuration_permission", group_has_configuration_permission) +register.simple_tag(user_can_clear_peer_review, name="user_can_clear_peer_review") diff --git a/dojo/test/queries.py b/dojo/test/queries.py index 89089efbf1d..0376cb02dd4 100644 --- a/dojo/test/queries.py +++ b/dojo/test/queries.py @@ -1,94 +1,25 @@ -from crum import get_current_user -from django.db.models import Q, Subquery +try: + from dojo.authorization.query_filters import get_auth_filter +except ImportError: + def get_auth_filter(key): return None -from dojo.authorization.authorization import get_roles_for_permission, user_has_global_permission -from dojo.models import Product_Group, Product_Member, Product_Type_Group, Product_Type_Member, Test, Test_Import +from dojo.models import Test, Test_Import from dojo.request_cache import cache_for_request # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_tests(permission, product=None): - user = get_current_user() - - if user is None: - return Test.objects.none() - - tests = Test.objects.all().order_by("id") - if product: - tests = tests.filter(engagement__product=product) - - if user.is_superuser: - return tests - - if user_has_global_permission(user, permission): - return Test.objects.all().order_by("id") - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return tests.filter( - Q(engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(engagement__product_id__in=Subquery(authorized_product_roles)) - | Q(engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(engagement__product_id__in=Subquery(authorized_product_groups)), - ) + impl = get_auth_filter("test.get_authorized_tests") + if impl: + return impl(permission, product=product) + return Test.objects.all().order_by("id") # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_test_imports(permission): - user = get_current_user() - - if user is None: - return Test_Import.objects.none() - - if user.is_superuser: - return Test_Import.objects.all().order_by("id") - - if user_has_global_permission(user, permission): - return Test_Import.objects.all().order_by("id") - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return Test_Import.objects.filter( - Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(test__engagement__product_id__in=Subquery(authorized_product_roles)) - | Q(test__engagement__product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(test__engagement__product_id__in=Subquery(authorized_product_groups)), - ).order_by("id") + impl = get_auth_filter("test.get_authorized_test_imports") + if impl: + return impl(permission) + return Test_Import.objects.all().order_by("id") diff --git a/dojo/test/views.py b/dojo/test/views.py index 936a43b54aa..4132d323bd7 100644 --- a/dojo/test/views.py +++ b/dojo/test/views.py @@ -24,7 +24,6 @@ import dojo.finding.helper as finding_helper from dojo.authorization.authorization import user_has_permission_or_403 -from dojo.authorization.authorization_decorators import user_is_authorized from dojo.authorization.roles_permissions import Permissions from dojo.celery_dispatch import dojo_dispatch_task from dojo.engagement.queries import get_authorized_engagements @@ -252,7 +251,6 @@ def post(self, request: HttpRequest, test_id: int): # Render the form return render(request, self.get_template(), context) - # def prefetch_for_test_imports(test_imports): # prefetched_test_imports = test_imports # if isinstance(test_imports, QuerySet): # old code can arrive here with prods being a list because the query was already executed @@ -265,7 +263,6 @@ def post(self, request: HttpRequest, test_id: int): # return prefetch_for_test_imports -@user_is_authorized(Test, Permissions.Test_Edit, "tid") def edit_test(request, tid): test = get_object_or_404(Test, pk=tid) form = TestForm(instance=test) @@ -292,7 +289,6 @@ def edit_test(request, tid): }) -@user_is_authorized(Test, Permissions.Test_Delete, "tid") def delete_test(request, tid): test = get_object_or_404(Test, pk=tid) eng = test.engagement @@ -333,7 +329,6 @@ def delete_test(request, tid): }) -@user_is_authorized(Test, Permissions.Test_Edit, "tid") def copy_test(request, tid): test = get_object_or_404(Test, id=tid) product = test.engagement.product @@ -407,7 +402,6 @@ def test_calendar(request): "users": get_authorized_users(Permissions.Test_View)}) -@user_is_authorized(Test, Permissions.Test_View, "tid") def test_ics(request, tid): test = get_object_or_404(Test, id=tid) start_date = datetime.combine(test.target_start, datetime.min.time()) @@ -661,10 +655,10 @@ def post(self, request: HttpRequest, test_id: int): return render(request, self.get_template(), context) -@user_is_authorized(Test, Permissions.Finding_Add, "tid") def add_finding_from_template(request, tid, fid): jform = None test = get_object_or_404(Test, id=tid) + user_has_permission_or_403(request.user, test, Permissions.Finding_Add) template = get_object_or_404(Finding_Template, id=fid) findings = Finding_Template.objects.all() push_all_jira_issues = jira_services.is_push_all_issues(template) @@ -826,7 +820,6 @@ def add_finding_from_template(request, tid, fid): }) -@user_is_authorized(Test, Permissions.Test_View, "tid") def search(request, tid): test = get_object_or_404(Test, id=tid) templates = Finding_Template.objects.all() diff --git a/dojo/test_type/views.py b/dojo/test_type/views.py index d7a2b9739bb..5a25e9ed00a 100644 --- a/dojo/test_type/views.py +++ b/dojo/test_type/views.py @@ -7,7 +7,6 @@ from django.shortcuts import get_object_or_404, render from django.urls import reverse -from dojo.authorization.authorization_decorators import user_is_configuration_authorized from dojo.filters import TestTypeFilter from dojo.forms import Test_TypeForm from dojo.models import Test_Type @@ -38,7 +37,6 @@ def test_type(request): "name_words": name_words}) -@user_is_configuration_authorized("dojo.add_test_type") def add_test_type(request): form = Test_TypeForm() if request.method == "POST": @@ -59,7 +57,6 @@ def add_test_type(request): }) -@user_is_configuration_authorized("dojo.change_test_type") def edit_test_type(request, ptid): tt = get_object_or_404(Test_Type, pk=ptid) form = Test_TypeForm(instance=tt) diff --git a/dojo/tool_config/views.py b/dojo/tool_config/views.py index 6ffbe94067d..cb32d3203cf 100644 --- a/dojo/tool_config/views.py +++ b/dojo/tool_config/views.py @@ -6,7 +6,6 @@ from django.shortcuts import render from django.urls import reverse -from dojo.authorization.authorization_decorators import user_is_configuration_authorized from dojo.forms import ToolConfigForm from dojo.models import Tool_Configuration from dojo.tool_config.factory import create_API @@ -15,7 +14,6 @@ logger = logging.getLogger(__name__) -@user_is_configuration_authorized("dojo.add_tool_configuration") def new_tool_config(request): if request.method == "POST": tform = ToolConfigForm(request.POST) @@ -50,7 +48,6 @@ def new_tool_config(request): {"tform": tform}) -@user_is_configuration_authorized("dojo.change_tool_configuration") def edit_tool_config(request, ttid): tool_config = Tool_Configuration.objects.get(pk=ttid) if request.method == "POST": @@ -92,7 +89,6 @@ def edit_tool_config(request, ttid): }) -@user_is_configuration_authorized("dojo.view_tool_configuration") def tool_config(request): confs = Tool_Configuration.objects.all().order_by("name") add_breadcrumb(title="Tool Configuration List", top_level=not len(request.GET), request=request) diff --git a/dojo/tool_product/queries.py b/dojo/tool_product/queries.py index d8979fa5066..6ae66429fdc 100644 --- a/dojo/tool_product/queries.py +++ b/dojo/tool_product/queries.py @@ -1,48 +1,16 @@ -from crum import get_current_user -from django.db.models import Q, Subquery +try: + from dojo.authorization.query_filters import get_auth_filter +except ImportError: + def get_auth_filter(key): return None -from dojo.authorization.authorization import get_roles_for_permission, user_has_global_permission -from dojo.models import Product_Group, Product_Member, Product_Type_Group, Product_Type_Member, Tool_Product_Settings +from dojo.models import Tool_Product_Settings from dojo.request_cache import cache_for_request # Cached: all parameters are hashable, no dynamic queryset filtering @cache_for_request def get_authorized_tool_product_settings(permission): - user = get_current_user() - - if user is None: - return Tool_Product_Settings.objects.none() - - if user.is_superuser: - return Tool_Product_Settings.objects.all().order_by("id") - - if user_has_global_permission(user, permission): - return Tool_Product_Settings.objects.all().order_by("id") - - roles = get_roles_for_permission(permission) - - # Get authorized product/product_type IDs via subqueries - authorized_product_type_roles = Product_Type_Member.objects.filter( - user=user, role__in=roles, - ).values("product_type_id") - - authorized_product_roles = Product_Member.objects.filter( - user=user, role__in=roles, - ).values("product_id") - - authorized_product_type_groups = Product_Type_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_type_id") - - authorized_product_groups = Product_Group.objects.filter( - group__users=user, role__in=roles, - ).values("product_id") - - # Filter using IN with Subquery - no annotations needed - return Tool_Product_Settings.objects.filter( - Q(product__prod_type_id__in=Subquery(authorized_product_type_roles)) - | Q(product_id__in=Subquery(authorized_product_roles)) - | Q(product__prod_type_id__in=Subquery(authorized_product_type_groups)) - | Q(product_id__in=Subquery(authorized_product_groups)), - ).order_by("id") + impl = get_auth_filter("tool_product.get_authorized_tool_product_settings") + if impl: + return impl(permission) + return Tool_Product_Settings.objects.all().order_by("id") diff --git a/dojo/tool_product/views.py b/dojo/tool_product/views.py index 98f56d2f5ce..de142b1bcf8 100644 --- a/dojo/tool_product/views.py +++ b/dojo/tool_product/views.py @@ -8,8 +8,6 @@ from django.urls import reverse from django.utils.translation import gettext as _ -from dojo.authorization.authorization_decorators import user_is_authorized -from dojo.authorization.roles_permissions import Permissions from dojo.forms import DeleteToolProductSettingsForm, ToolProductSettingsForm from dojo.models import Product, Tool_Product_Settings from dojo.utils import Product_Tab @@ -17,7 +15,6 @@ logger = logging.getLogger(__name__) -@user_is_authorized(Product, Permissions.Product_Edit, "pid") def new_tool_product(request, pid): prod = get_object_or_404(Product, id=pid) if request.method == "POST": @@ -45,7 +42,6 @@ def new_tool_product(request, pid): }) -@user_is_authorized(Product, Permissions.Product_Edit, "pid") def all_tool_product(request, pid): prod = get_object_or_404(Product, id=pid) tools = Tool_Product_Settings.objects.filter(product=prod).order_by("name") @@ -57,7 +53,6 @@ def all_tool_product(request, pid): }) -@user_is_authorized(Product, Permissions.Product_Edit, "pid") def edit_tool_product(request, pid, ttid): product = get_object_or_404(Product, id=pid) tool_product = get_object_or_404(Tool_Product_Settings, pk=ttid) @@ -84,7 +79,6 @@ def edit_tool_product(request, pid, ttid): }) -@user_is_authorized(Product, Permissions.Product_Edit, "pid") def delete_tool_product(request, pid, ttid): tool_product = get_object_or_404(Tool_Product_Settings, pk=ttid) product = get_object_or_404(Product, id=pid) diff --git a/dojo/tool_type/views.py b/dojo/tool_type/views.py index 88867012097..3f9e8218136 100644 --- a/dojo/tool_type/views.py +++ b/dojo/tool_type/views.py @@ -7,7 +7,6 @@ from django.urls import reverse from django.utils.translation import gettext as _ -from dojo.authorization.authorization_decorators import user_is_configuration_authorized from dojo.forms import ToolTypeForm from dojo.models import Tool_Type from dojo.utils import add_breadcrumb @@ -15,7 +14,6 @@ logger = logging.getLogger(__name__) -@user_is_configuration_authorized("dojo.add_tool_type") def new_tool_type(request): if request.method == "POST": tform = ToolTypeForm(request.POST, instance=Tool_Type()) @@ -35,7 +33,6 @@ def new_tool_type(request): return render(request, "dojo/new_tool_type.html", {"tform": tform}) -@user_is_configuration_authorized("dojo.change_tool_type") def edit_tool_type(request, ttid): tool_type = Tool_Type.objects.get(pk=ttid) if request.method == "POST": @@ -55,7 +52,6 @@ def edit_tool_type(request, ttid): return render(request, "dojo/edit_tool_type.html", {"tform": tform}) -@user_is_configuration_authorized("dojo.view_tool_type") def tool_type(request): confs = Tool_Type.objects.all().order_by("name") add_breadcrumb(title=_("Tool Type List"), top_level=not len(request.GET), request=request) diff --git a/dojo/url/api/views.py b/dojo/url/api/views.py index caebd867bec..14a8f5b5fa5 100644 --- a/dojo/url/api/views.py +++ b/dojo/url/api/views.py @@ -3,8 +3,8 @@ from django_filters.rest_framework import DjangoFilterBackend from rest_framework.permissions import DjangoModelPermissions -from dojo.api_v2.permissions import IsSuperUser from dojo.api_v2.views import PrefetchDojoModelViewSet +from dojo.authorization.api_permissions import IsSuperUser from dojo.location.models import LocationFindingReference from dojo.location.status import FindingLocationStatus from dojo.query_utils import build_count_subquery diff --git a/dojo/url/ui/views.py b/dojo/url/ui/views.py index 2fe72fc94a8..c4baa0f55e9 100644 --- a/dojo/url/ui/views.py +++ b/dojo/url/ui/views.py @@ -14,7 +14,6 @@ from django.utils import timezone from dojo.authorization.authorization import user_has_permission_or_403 -from dojo.authorization.authorization_decorators import user_is_authorized from dojo.authorization.roles_permissions import Permissions from dojo.endpoint.utils import endpoint_meta_import from dojo.forms import ( @@ -45,12 +44,10 @@ logger = logging.getLogger(__name__) -@user_is_authorized(Location, Permissions.Location_View, "location_id") def view_endpoint(request: HttpRequest, location_id: int): return process_endpoint_view(request, location_id, host_view=False) -@user_is_authorized(Location, Permissions.Location_View, "location_id") def view_endpoint_host(request: HttpRequest, location_id: int): return process_endpoint_view(request, location_id, host_view=True) @@ -290,7 +287,6 @@ def process_endpoints_view(request, *, host_view=False, vulnerable=False): ) -@user_is_authorized(Location, Permissions.Location_Edit, "location_id") def edit_endpoint(request, location_id): # Retrieve the Location object by ID and add breadcrumb for editing location = get_object_or_404(Location, id=location_id) @@ -327,7 +323,6 @@ def edit_endpoint(request, location_id): ) -@user_is_authorized(Product, Permissions.Location_Add, "product_id") def add_endpoint_to_product(request, product_id): # Retrieve the Product object by ID and initialize the URLForm for adding a new endpoint product = get_object_or_404(Product, id=product_id) @@ -349,7 +344,6 @@ def add_endpoint_to_product(request, product_id): return render(request, "dojo/url/create.html", {"product_tab": product_tab, "form": form}) -@user_is_authorized(Product, Permissions.Location_Add, "finding_id") def add_endpoint_to_finding(request, finding_id): # Retrieve the Finding object by ID and get its associated Product finding = get_object_or_404(Finding, id=finding_id) @@ -371,7 +365,6 @@ def add_endpoint_to_finding(request, finding_id): return render(request, "dojo/url/create.html", {"product_tab": product_tab, "form": form}) -@user_is_authorized(Location, Permissions.Location_Delete, "location_id") def delete_endpoint(request, location_id): # Retrieve the Location object by primary key and initialize the delete form location = get_object_or_404(Location, pk=location_id) @@ -405,7 +398,6 @@ def delete_endpoint(request, location_id): ) -@user_is_authorized(Location, Permissions.Location_Edit, "location_id") def manage_meta_data(request, location_id): # Retrieve the Location object by ID and filter its associated metadata location = Location.objects.get(id=location_id) @@ -430,7 +422,6 @@ def manage_meta_data(request, location_id): ) -@user_is_authorized(Product, Permissions.Location_Edit, "product_id") def import_endpoint_meta(request, product_id): # Retrieve the Product object by ID and initialize the import form product = get_object_or_404(Product, id=product_id) @@ -562,7 +553,6 @@ def endpoint_bulk_update_all(request, product_id=None): return HttpResponseRedirect(reverse("endpoint", args=())) -@user_is_authorized(Finding, Permissions.Finding_Edit, "finding_id") def finding_location_bulk_update(request, finding_id): if request.method == "POST": # Get the list of endpoint IDs to update and the statuses to enable @@ -634,13 +624,11 @@ def migrate_endpoints_view(request): }) -@user_is_authorized(Location, Permissions.Location_View, "location_id") def endpoint_report(request, location_id): location = get_object_or_404(Location, id=location_id) return generate_report(request, location, host_view=False) -@user_is_authorized(Location, Permissions.Location_View, "location_id") def endpoint_host_report(request, location_id): location = get_object_or_404(Location, id=location_id) return generate_report(request, location, host_view=True) diff --git a/dojo/user/queries.py b/dojo/user/queries.py index 85f04b281e4..136bfcd9c67 100644 --- a/dojo/user/queries.py +++ b/dojo/user/queries.py @@ -1,146 +1,32 @@ -from crum import get_current_user -from django.db.models import Q, Subquery +try: + from dojo.authorization.query_filters import get_auth_filter +except ImportError: + def get_auth_filter(key): return None -from dojo.authorization.authorization import get_roles_for_permission, user_has_global_permission from dojo.models import ( - Dojo_Group_Member, Dojo_User, - Global_Role, - Product_Group, - Product_Member, - Product_Type_Group, - Product_Type_Member, ) -from dojo.product.queries import get_authorized_products -from dojo.product_type.queries import get_authorized_product_types from dojo.request_cache import cache_for_request def get_authorized_users_for_product_type(users, product_type, permission): - roles = get_roles_for_permission(permission) - - # Get user IDs via subqueries instead of materializing into Python lists - product_type_member_users = Product_Type_Member.objects.filter( - product_type=product_type, role__in=roles, - ).values("user_id") - - # Get group IDs that have access to this product type - product_type_group_ids = Product_Type_Group.objects.filter( - product_type=product_type, role__in=roles, - ).values("group_id") - - global_role_group_ids = Global_Role.objects.filter( - role__in=roles, group__isnull=False, - ).values("group_id") - - # Get users from those groups - group_member_users = Dojo_Group_Member.objects.filter( - Q(group_id__in=Subquery(product_type_group_ids)) - | Q(group_id__in=Subquery(global_role_group_ids)), - ).values("user_id") - - return users.filter( - Q(id__in=Subquery(product_type_member_users)) - | Q(id__in=Subquery(group_member_users)) - | Q(global_role__role__in=roles) - | Q(is_superuser=True), - ) + impl = get_auth_filter("user.get_authorized_users_for_product_type") + if impl: + return impl(users, product_type, permission) + return users def get_authorized_users_for_product_and_product_type(users, product, permission): - if users is None: - users = Dojo_User.objects.filter(is_active=True) - - roles = get_roles_for_permission(permission) - - # Get user IDs via subqueries instead of materializing into Python lists - product_member_users = Product_Member.objects.filter( - product=product, role__in=roles, - ).values("user_id") - - product_type_member_users = Product_Type_Member.objects.filter( - product_type=product.prod_type, role__in=roles, - ).values("user_id") - - # Get group IDs that have access to this product or product type - product_group_ids = Product_Group.objects.filter( - product=product, role__in=roles, - ).values("group_id") - - product_type_group_ids = Product_Type_Group.objects.filter( - product_type=product.prod_type, role__in=roles, - ).values("group_id") - - global_role_group_ids = Global_Role.objects.filter( - role__in=roles, group__isnull=False, - ).values("group_id") - - # Get users from those groups - group_member_users = Dojo_Group_Member.objects.filter( - Q(group_id__in=Subquery(product_group_ids)) - | Q(group_id__in=Subquery(product_type_group_ids)) - | Q(group_id__in=Subquery(global_role_group_ids)), - ).values("user_id") - - return users.filter( - Q(id__in=Subquery(product_member_users)) - | Q(id__in=Subquery(product_type_member_users)) - | Q(id__in=Subquery(group_member_users)) - | Q(global_role__role__in=roles) - | Q(is_superuser=True), - ) + impl = get_auth_filter("user.get_authorized_users_for_product_and_product_type") + if impl: + return impl(users, product, permission) + return users # Cached because it is a complex SQL query and it is called 3 times for the engagement lists in products @cache_for_request def get_authorized_users(permission, user=None): - if user is None: - user = get_current_user() - - if user is None: - return Dojo_User.objects.none() - - if user.is_anonymous: - return Dojo_User.objects.none() - - users = Dojo_User.objects.all().order_by("first_name", "last_name", "username") - - if user.is_superuser or user_has_global_permission(user, permission): - return users - - authorized_products = get_authorized_products(permission).values("id") - authorized_product_types = get_authorized_product_types(permission).values("id") - - roles = get_roles_for_permission(permission) - - # Get user IDs via subqueries instead of materializing into Python lists - product_member_users = Product_Member.objects.filter( - product_id__in=Subquery(authorized_products), role__in=roles, - ).values("user_id") - - product_type_member_users = Product_Type_Member.objects.filter( - product_type_id__in=Subquery(authorized_product_types), role__in=roles, - ).values("user_id") - - # Get group IDs that have access to authorized products/product types - product_group_ids = Product_Group.objects.filter( - product_id__in=Subquery(authorized_products), role__in=roles, - ).values("group_id") - - product_type_group_ids = Product_Type_Group.objects.filter( - product_type_id__in=Subquery(authorized_product_types), role__in=roles, - ).values("group_id") - - # Get users from those groups - group_member_users = Dojo_Group_Member.objects.filter( - Q(group_id__in=Subquery(product_group_ids)) - | Q(group_id__in=Subquery(product_type_group_ids)), - ).values("user_id") - - return users.filter( - Q(id__in=Subquery(product_member_users)) - | Q(id__in=Subquery(product_type_member_users)) - | Q(id__in=Subquery(group_member_users)) - | Q(global_role__role__in=roles) - | Q(is_superuser=True), - ) + impl = get_auth_filter("user.get_authorized_users") + if impl: + return impl(permission, user=user) + return Dojo_User.objects.all().order_by("first_name", "last_name") diff --git a/dojo/user/views.py b/dojo/user/views.py index dd732b5c91e..1d103658136 100644 --- a/dojo/user/views.py +++ b/dojo/user/views.py @@ -30,7 +30,7 @@ from rest_framework.exceptions import ValidationError as RFValidationError from dojo.authorization.authorization import user_is_superuser_or_global_owner -from dojo.authorization.authorization_decorators import user_is_configuration_authorized +from dojo.authorization.models import Dojo_Group_Member, Product_Member, Product_Type_Member from dojo.authorization.roles_permissions import Permissions from dojo.decorators import dojo_ratelimit from dojo.filters import UserFilter @@ -50,7 +50,7 @@ ) from dojo.group.queries import get_authorized_group_members_for_user from dojo.labels import get_labels -from dojo.models import Alerts, Dojo_Group_Member, Dojo_User, Product_Member, Product_Type_Member, UserContactInfo +from dojo.models import Alerts, Dojo_User, UserContactInfo from dojo.product.queries import get_authorized_product_members_for_user from dojo.product_type.queries import get_authorized_product_type_members_for_user from dojo.user.authentication import reset_token_for_user @@ -58,7 +58,6 @@ logger = logging.getLogger(__name__) - labels = get_labels() @@ -82,9 +81,9 @@ def form_valid(self, form): extra_tags="alert-success") return response - # # Django Rest Framework API v2 + def api_v2_key(request): # This check should not be necessary because url should not be in 'urlpatterns' but we never know if not settings.API_TOKENS_ENABLED: @@ -312,7 +311,6 @@ def change_password(request): return render(request, "dojo/change_pwd.html", {"form": form}) -@user_is_configuration_authorized("auth.view_user") def user(request): page_name = _("All Users") users = Dojo_User.objects.all() \ @@ -328,7 +326,6 @@ def user(request): }) -@user_is_configuration_authorized("auth.add_user") def add_user(request): page_name = _("Add User") form = AddDojoUserForm() @@ -385,7 +382,6 @@ def add_user(request): "to_add": True}) -@user_is_configuration_authorized("auth.view_user") def view_user(request, uid): user = get_object_or_404(Dojo_User, id=uid) product_members = get_authorized_product_members_for_user(user, Permissions.Product_View) @@ -402,7 +398,6 @@ def view_user(request, uid): "configuration_permission_form": configuration_permission_form}) -@user_is_configuration_authorized("auth.change_user") def edit_user(request, uid): page_name = _("Edit User") user = get_object_or_404(Dojo_User, id=uid) @@ -490,7 +485,6 @@ def edit_user(request, uid): "to_edit": user}) -@user_is_configuration_authorized("auth.delete_user") def delete_user(request, uid): user = get_object_or_404(Dojo_User, id=uid) form = DeleteUserForm(instance=user) @@ -635,7 +629,6 @@ def add_group_member(request, uid): }) -@user_is_configuration_authorized("auth.change_permission") def edit_permissions(request, uid): user = get_object_or_404(Dojo_User, id=uid) if request.method == "POST": diff --git a/dojo/utils.py b/dojo/utils.py index 605fb11b55c..02b902e5909 100644 --- a/dojo/utils.py +++ b/dojo/utils.py @@ -45,6 +45,7 @@ from django.utils.translation import gettext as _ from kombu import Connection +from dojo.authorization.models import Dojo_Group_Member from dojo.authorization.roles_permissions import Permissions from dojo.celery import app from dojo.finding.queries import get_authorized_findings @@ -60,7 +61,6 @@ from dojo.models import ( NOTIFICATION_CHOICES, Benchmark_Type, - Dojo_Group_Member, Dojo_User, Endpoint, Engagement, diff --git a/unittests/authorization/test_authorization.py b/unittests/authorization/test_authorization.py index 60fff15eca6..7b49f1d6103 100644 --- a/unittests/authorization/test_authorization.py +++ b/unittests/authorization/test_authorization.py @@ -15,24 +15,26 @@ user_has_permission, user_has_permission_or_403, ) +from dojo.authorization.models import ( + Dojo_Group_Member, + Global_Role, + Product_Group, + Product_Member, + Product_Type_Group, + Product_Type_Member, + Role, +) from dojo.authorization.roles_permissions import Permissions, Roles from dojo.models import ( App_Analysis, Dojo_Group, - Dojo_Group_Member, Dojo_User, Endpoint, Engagement, Finding, - Global_Role, Languages, Product, - Product_Group, - Product_Member, Product_Type, - Product_Type_Group, - Product_Type_Member, - Role, Stub_Finding, Test, ) diff --git a/unittests/authorization/test_authorization_tags.py b/unittests/authorization/test_authorization_tags.py index 2e3de9ba31b..3a4881e87a4 100644 --- a/unittests/authorization/test_authorization_tags.py +++ b/unittests/authorization/test_authorization_tags.py @@ -28,7 +28,7 @@ def setUp(self): self.permission_c = Permission() self.permission_c.codename = "c" - @patch("dojo.templatetags.authorization_tags.user_has_permission") + @patch("dojo.authorization.template_filters.user_has_permission") def test_has_object_permission_no_permission(self, mock_has_permission): mock_has_permission.return_value = False @@ -37,7 +37,7 @@ def test_has_object_permission_no_permission(self, mock_has_permission): self.assertFalse(result) mock_has_permission.assert_called_with(None, self.product_type, Permissions.Product_Type_View) - @patch("dojo.templatetags.authorization_tags.user_has_permission") + @patch("dojo.authorization.template_filters.user_has_permission") @patch("crum.get_current_user") def test_has_object_permission_has_permission(self, mock_current_user, mock_has_permission): mock_has_permission.return_value = True @@ -54,7 +54,7 @@ def test_has_object_permission_wrong_permission(self): with self.assertRaises(KeyError): has_object_permission(self.product_type, "Test") - @patch("dojo.templatetags.authorization_tags.configuration_permission") + @patch("dojo.authorization.template_filters.configuration_permission") @patch("crum.get_current_user") def test_has_configuration_permission(self, mock_current_user, mock_configuration_permission): mock_configuration_permission.return_value = True diff --git a/unittests/test_apiv2_user.py b/unittests/test_apiv2_user.py index fc232144759..a7da900462c 100644 --- a/unittests/test_apiv2_user.py +++ b/unittests/test_apiv2_user.py @@ -3,7 +3,8 @@ from rest_framework.authtoken.models import Token from rest_framework.test import APIClient, APITestCase -from dojo.models import Global_Role, Role, User, UserContactInfo +from dojo.authorization.models import Global_Role, Role +from dojo.models import User, UserContactInfo from unittests.dojo_test_case import versioned_fixtures diff --git a/unittests/test_apply_finding_template.py b/unittests/test_apply_finding_template.py index 75af3311db7..f2e359f2521 100644 --- a/unittests/test_apply_finding_template.py +++ b/unittests/test_apply_finding_template.py @@ -9,6 +9,10 @@ from django.test.client import RequestFactory from django.utils import timezone +from dojo.authorization.models import ( + Product_Member, + Role, +) from dojo.finding import views from dojo.finding.helper import save_endpoints_template, save_vulnerability_ids_template from dojo.models import ( @@ -17,9 +21,7 @@ Finding_Template, Notes, Product, - Product_Member, Product_Type, - Role, System_Settings, Test, Test_Type, diff --git a/unittests/test_authorization_queries.py b/unittests/test_authorization_queries.py index 10ea562c512..6546daf66b2 100644 --- a/unittests/test_authorization_queries.py +++ b/unittests/test_authorization_queries.py @@ -10,6 +10,15 @@ from django.conf import settings from django.utils import timezone +from dojo.authorization.models import ( + Dojo_Group_Member, + Global_Role, + Product_Group, + Product_Member, + Product_Type_Group, + Product_Type_Member, + Role, +) from dojo.authorization.roles_permissions import Permissions from dojo.endpoint.queries import get_authorized_endpoint_status, get_authorized_endpoints from dojo.engagement.queries import get_authorized_engagements @@ -29,21 +38,14 @@ ) from dojo.models import ( Dojo_Group, - Dojo_Group_Member, Dojo_User, Endpoint, Endpoint_Status, Engagement, Finding, Finding_Group, - Global_Role, Product, - Product_Group, - Product_Member, Product_Type, - Product_Type_Group, - Product_Type_Member, - Role, Stub_Finding, Test, Test_Type, @@ -360,7 +362,7 @@ def test_queryset_parameter_filters_correctly(self): def test_none_user_returns_empty(self): """None user should return empty queryset""" - with patch("dojo.finding.queries.get_current_user", return_value=None): + with patch("dojo.authorization.query_registrations.get_current_user", return_value=None): findings = get_authorized_findings(Permissions.Finding_View) self.assertEqual(findings.count(), 0) @@ -369,7 +371,7 @@ class TestGetAuthorizedStubFindings(AuthorizationQueriesTestBase): """Tests for get_authorized_stub_findings() - uses get_current_user()""" - @patch("dojo.finding.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_superuser_gets_all_stub_findings(self, mock_get_current_user): """Superuser should get all stub findings""" mock_get_current_user.return_value = self.superuser @@ -377,7 +379,7 @@ def test_superuser_gets_all_stub_findings(self, mock_get_current_user): self.assertIn(self.stub_finding_1, stub_findings) self.assertIn(self.stub_finding_2, stub_findings) - @patch("dojo.finding.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_user_no_permissions_gets_empty(self, mock_get_current_user): """User with no permissions should not get test stub findings""" mock_get_current_user.return_value = self.user_no_perms @@ -385,7 +387,7 @@ def test_user_no_permissions_gets_empty(self, mock_get_current_user): self.assertNotIn(self.stub_finding_1, stub_findings) self.assertNotIn(self.stub_finding_2, stub_findings) - @patch("dojo.finding.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_user_product_member_gets_product_stub_findings(self, mock_get_current_user): """User with product membership should get only that product's stub findings""" mock_get_current_user.return_value = self.user_product_member @@ -468,7 +470,7 @@ class TestGetAuthorizedProductTypes(AuthorizationQueriesTestBase): """Tests for get_authorized_product_types() - uses get_current_user()""" - @patch("dojo.product_type.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_superuser_gets_all_product_types(self, mock_get_current_user): """Superuser should get all product types""" mock_get_current_user.return_value = self.superuser @@ -476,7 +478,7 @@ def test_superuser_gets_all_product_types(self, mock_get_current_user): self.assertIn(self.product_type_1, product_types) self.assertIn(self.product_type_2, product_types) - @patch("dojo.product_type.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_user_no_permissions_gets_empty(self, mock_get_current_user): """User with no permissions should not get test product types""" mock_get_current_user.return_value = self.user_no_perms @@ -484,7 +486,7 @@ def test_user_no_permissions_gets_empty(self, mock_get_current_user): self.assertNotIn(self.product_type_1, product_types) self.assertNotIn(self.product_type_2, product_types) - @patch("dojo.product_type.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_user_global_reader_gets_all(self, mock_get_current_user): """User with global reader role should get all product types""" mock_get_current_user.return_value = self.user_global_reader @@ -492,7 +494,7 @@ def test_user_global_reader_gets_all(self, mock_get_current_user): self.assertIn(self.product_type_1, product_types) self.assertIn(self.product_type_2, product_types) - @patch("dojo.product_type.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_user_product_type_member_gets_own_types(self, mock_get_current_user): """User with product type membership should get only that type""" mock_get_current_user.return_value = self.user_product_type_member @@ -500,7 +502,7 @@ def test_user_product_type_member_gets_own_types(self, mock_get_current_user): self.assertIn(self.product_type_1, product_types) self.assertNotIn(self.product_type_2, product_types) - @patch("dojo.product_type.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_user_group_product_type_member_gets_group_types(self, mock_get_current_user): """User in group with product type access should get that type""" mock_get_current_user.return_value = self.user_group_product_type_member @@ -513,7 +515,7 @@ class TestGetAuthorizedEngagements(AuthorizationQueriesTestBase): """Tests for get_authorized_engagements() - uses get_current_user()""" - @patch("dojo.engagement.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_superuser_gets_all_engagements(self, mock_get_current_user): """Superuser should get all engagements""" mock_get_current_user.return_value = self.superuser @@ -521,7 +523,7 @@ def test_superuser_gets_all_engagements(self, mock_get_current_user): self.assertIn(self.engagement_1, engagements) self.assertIn(self.engagement_2, engagements) - @patch("dojo.engagement.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_user_no_permissions_gets_empty(self, mock_get_current_user): """User with no permissions should not get test engagements""" mock_get_current_user.return_value = self.user_no_perms @@ -529,7 +531,7 @@ def test_user_no_permissions_gets_empty(self, mock_get_current_user): self.assertNotIn(self.engagement_1, engagements) self.assertNotIn(self.engagement_2, engagements) - @patch("dojo.engagement.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_user_global_reader_gets_all(self, mock_get_current_user): """User with global reader role should get all engagements""" mock_get_current_user.return_value = self.user_global_reader @@ -537,7 +539,7 @@ def test_user_global_reader_gets_all(self, mock_get_current_user): self.assertIn(self.engagement_1, engagements) self.assertIn(self.engagement_2, engagements) - @patch("dojo.engagement.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_user_product_member_gets_product_engagements(self, mock_get_current_user): """User with product membership should get only that product's engagements""" mock_get_current_user.return_value = self.user_product_member @@ -545,7 +547,7 @@ def test_user_product_member_gets_product_engagements(self, mock_get_current_use self.assertIn(self.engagement_1, engagements) self.assertNotIn(self.engagement_2, engagements) - @patch("dojo.engagement.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_user_product_type_member_gets_product_type_engagements(self, mock_get_current_user): """User with product type membership should get engagements in that type""" mock_get_current_user.return_value = self.user_product_type_member @@ -558,7 +560,7 @@ class TestGetAuthorizedTests(AuthorizationQueriesTestBase): """Tests for get_authorized_tests() - uses get_current_user()""" - @patch("dojo.test.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_superuser_gets_all_tests(self, mock_get_current_user): """Superuser should get all tests""" mock_get_current_user.return_value = self.superuser @@ -566,7 +568,7 @@ def test_superuser_gets_all_tests(self, mock_get_current_user): self.assertIn(self.test_1, tests) self.assertIn(self.test_2, tests) - @patch("dojo.test.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_user_no_permissions_gets_empty(self, mock_get_current_user): """User with no permissions should not get test tests""" mock_get_current_user.return_value = self.user_no_perms @@ -574,7 +576,7 @@ def test_user_no_permissions_gets_empty(self, mock_get_current_user): self.assertNotIn(self.test_1, tests) self.assertNotIn(self.test_2, tests) - @patch("dojo.test.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_user_product_member_gets_product_tests(self, mock_get_current_user): """User with product membership should get only that product's tests""" mock_get_current_user.return_value = self.user_product_member @@ -709,7 +711,7 @@ class TestGetAuthorizedGroups(AuthorizationQueriesTestBase): """Tests for get_authorized_groups() - uses get_current_user()""" - @patch("dojo.group.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_superuser_gets_all_groups(self, mock_get_current_user): """Superuser should get all groups""" mock_get_current_user.return_value = self.superuser @@ -717,7 +719,7 @@ def test_superuser_gets_all_groups(self, mock_get_current_user): self.assertIn(self.group_product, groups) self.assertIn(self.group_product_type, groups) - @patch("dojo.group.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_user_group_member_gets_own_groups(self, mock_get_current_user): """User who is a group member should get that group""" mock_get_current_user.return_value = self.user_group_product_member diff --git a/unittests/test_bulk_edit_validation.py b/unittests/test_bulk_edit_validation.py index 63c84568b02..644f9742aad 100644 --- a/unittests/test_bulk_edit_validation.py +++ b/unittests/test_bulk_edit_validation.py @@ -5,14 +5,16 @@ from django.urls import reverse from django.utils import timezone +from dojo.authorization.models import ( + Product_Type_Member, + Role, +) from dojo.authorization.roles_permissions import Roles from dojo.models import ( Engagement, Finding, Product, Product_Type, - Product_Type_Member, - Role, Test, Test_Type, ) diff --git a/unittests/test_bulk_risk_acceptance_api.py b/unittests/test_bulk_risk_acceptance_api.py index cc5ace94517..6480a3a3075 100644 --- a/unittests/test_bulk_risk_acceptance_api.py +++ b/unittests/test_bulk_risk_acceptance_api.py @@ -4,6 +4,10 @@ from rest_framework.reverse import reverse from rest_framework.test import APIClient, APITestCase +from dojo.authorization.models import ( + Product_Type_Member, + Role, +) from dojo.authorization.roles_permissions import Roles from dojo.models import ( Engagement, @@ -11,8 +15,6 @@ Product, Product_Member, Product_Type, - Product_Type_Member, - Role, Test, Test_Type, User, diff --git a/unittests/test_permissions_audit.py b/unittests/test_permissions_audit.py index 9d4ca0a746e..3f532fe246a 100644 --- a/unittests/test_permissions_audit.py +++ b/unittests/test_permissions_audit.py @@ -23,6 +23,10 @@ from rest_framework.authtoken.models import Token from rest_framework.test import APIClient +from dojo.authorization.models import ( + Product_Member, + Role, +) from dojo.models import ( Answered_Survey, Benchmark_Category, @@ -41,10 +45,8 @@ Objects_Product, Objects_Review, Product, - Product_Member, Product_Type, Risk_Acceptance, - Role, Test, Test_Type, Tool_Configuration, diff --git a/unittests/test_remote_user.py b/unittests/test_remote_user.py index a1d3706c6a7..54f9af47803 100644 --- a/unittests/test_remote_user.py +++ b/unittests/test_remote_user.py @@ -1,7 +1,8 @@ from django.test import Client, override_settings from netaddr import IPSet -from dojo.models import Dojo_Group, Dojo_Group_Member, User +from dojo.authorization.models import Dojo_Group_Member +from dojo.models import Dojo_Group, User from dojo.remote_user import RemoteUserScheme from .dojo_test_case import DojoTestCase diff --git a/unittests/test_rest_framework.py b/unittests/test_rest_framework.py index 2fcb52feed7..1a72da425cb 100644 --- a/unittests/test_rest_framework.py +++ b/unittests/test_rest_framework.py @@ -92,6 +92,15 @@ AssetMemberViewSet, AssetViewSet, ) +from dojo.authorization.models import ( + Dojo_Group_Member, + Global_Role, + Product_Group, + Product_Member, + Product_Type_Group, + Product_Type_Member, + Role, +) from dojo.authorization.roles_permissions import Permissions from dojo.location.api.endpoint_compat import V3EndpointCompatibleViewSet, V3EndpointStatusCompatibleViewSet from dojo.location.api.views import LocationFindingReferenceViewSet, LocationProductReferenceViewSet, LocationViewSet @@ -107,7 +116,6 @@ Cred_User, Development_Environment, Dojo_Group, - Dojo_Group_Member, DojoMeta, Endpoint, Endpoint_Status, @@ -117,7 +125,6 @@ Finding, Finding_Template, General_Survey, - Global_Role, JIRA_Instance, JIRA_Issue, JIRA_Project, @@ -129,13 +136,8 @@ Notifications, Product, Product_API_Scan_Configuration, - Product_Group, - Product_Member, Product_Type, - Product_Type_Group, - Product_Type_Member, Risk_Acceptance, - Role, Sonarqube_Issue, Sonarqube_Issue_Transition, Stub_Finding, @@ -603,7 +605,7 @@ def test_create(self): self.check_schema_response("post", "201", response) @skipIfNotSubclass(CreateModelMixin) - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_create_object_not_authorized(self, mock): if self.test_type != TestType.OBJECT_PERMISSIONS: self.skipTest("Authorization is not object based") @@ -665,7 +667,7 @@ def test_update(self): self.check_schema_response("put", "200", response, detail=True) @skipIfNotSubclass(UpdateModelMixin) - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_update_object_not_authorized(self, mock): if self.test_type != TestType.OBJECT_PERMISSIONS: self.skipTest("Authorization is not object based") @@ -756,7 +758,7 @@ def test_delete_preview(self): self.assertEqual(self.deleted_objects, len(response.data["results"]), response.content) @skipIfNotSubclass(DestroyModelMixin) - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_delete_object_not_authorized(self, mock): if self.test_type != TestType.OBJECT_PERMISSIONS: self.skipTest("Authorization is not object based") @@ -817,7 +819,7 @@ def test_update(self): self.check_schema_response("put", "200", response, detail=True) @skipIfNotSubclass(UpdateModelMixin) - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_update_object_not_authorized(self, mock): if self.test_type != TestType.OBJECT_PERMISSIONS: self.skipTest("Authorization is not object based") @@ -1368,7 +1370,7 @@ def test_create(self): self.assertEqual(403, response.status_code, response.content[:1000]) self.assertEqual(self.endpoint_model.objects.count(), length) - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_create_object_not_authorized(self, mock): length = self.endpoint_model.objects.count() response = self.client.post(self.url, self.payload) @@ -1386,7 +1388,7 @@ def test_delete(self): self.assertEqual(403, response.status_code, response.content[:1000]) self.assertEqual(self.endpoint_model.objects.count(), length) - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_delete_object_not_authorized(self, mock): length = self.endpoint_model.objects.count() current_objects = self.client.get(self.url, format="json").data @@ -1405,7 +1407,7 @@ def test_update(self): response = self.client.put(relative_url, self.payload) self.assertEqual(403, response.status_code, response.content[:1000]) - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_update_object_not_authorized(self, mock): current_objects = self.client.get(self.url, format="json").data relative_url = self.url + "{}/".format(current_objects["results"][0]["id"]) @@ -2831,7 +2833,7 @@ def __del__(self: object): @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_create_not_authorized_product_name_engagement_name(self, mock, importer_mock, reimporter_mock): mock.return_value = False importer_mock.return_value = IMPORTER_MOCK_RETURN_VALUE @@ -2861,7 +2863,7 @@ def test_create_not_authorized_product_name_engagement_name(self, mock, importer @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_create_not_authorized_product_name_engagement_name_auto_create_engagement(self, mock, importer_mock, reimporter_mock): mock.return_value = False importer_mock.return_value = IMPORTER_MOCK_RETURN_VALUE @@ -2892,7 +2894,7 @@ def test_create_not_authorized_product_name_engagement_name_auto_create_engageme @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_create_not_authorized_product_name_engagement_name_auto_create_product(self, mock, importer_mock, reimporter_mock): mock.return_value = False importer_mock.return_value = IMPORTER_MOCK_RETURN_VALUE @@ -2924,7 +2926,7 @@ def test_create_not_authorized_product_name_engagement_name_auto_create_product( @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_global_permission") + @patch("dojo.authorization.api_permissions.user_has_global_permission") def test_create_not_authorized_product_name_engagement_name_auto_create_product_type(self, mock, importer_mock, reimporter_mock): mock.return_value = False importer_mock.return_value = IMPORTER_MOCK_RETURN_VALUE @@ -2955,7 +2957,7 @@ def test_create_not_authorized_product_name_engagement_name_auto_create_product_ @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_create_authorized_product_name_engagement_name_auto_create_engagement(self, mock, importer_mock, reimporter_mock): """Test creating a new engagement should also check for import scan permission in the product""" mock.return_value = True @@ -2992,7 +2994,7 @@ def test_create_authorized_product_name_engagement_name_auto_create_engagement(s @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_create_authorized_product_name_engagement_name_auto_create_product(self, mock, importer_mock, reimporter_mock): mock.return_value = True importer_mock.return_value = IMPORTER_MOCK_RETURN_VALUE @@ -3023,7 +3025,7 @@ def test_create_authorized_product_name_engagement_name_auto_create_product(self @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_global_permission") + @patch("dojo.authorization.api_permissions.user_has_global_permission") def test_create_authorized_product_name_engagement_name_auto_create_product_type(self, mock, importer_mock, reimporter_mock): mock.return_value = True importer_mock.return_value = IMPORTER_MOCK_RETURN_VALUE @@ -3092,7 +3094,7 @@ def test_reimport_zap_xml(self, importer_mock, reimporter_mock): @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_create_not_authorized_product_name_engagement_name(self, mock, importer_mock, reimporter_mock): mock.return_value = False importer_mock.return_value = IMPORTER_MOCK_RETURN_VALUE @@ -3122,7 +3124,7 @@ def test_create_not_authorized_product_name_engagement_name(self, mock, importer @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_create_authorized_product_name_engagement_name_scan_type_title_auto_create(self, mock, importer_mock, reimporter_mock): mock.return_value = True importer_mock.return_value = IMPORTER_MOCK_RETURN_VALUE @@ -3152,7 +3154,7 @@ def test_create_authorized_product_name_engagement_name_scan_type_title_auto_cre @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_create_authorized_product_name_engagement_name_auto_create_engagement(self, mock, importer_mock, reimporter_mock): """Test creating a new engagement should also check for import scan permission in the product""" mock.return_value = True @@ -3189,7 +3191,7 @@ def test_create_authorized_product_name_engagement_name_auto_create_engagement(s @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_create_authorized_product_name_engagement_name_auto_create_product(self, mock, importer_mock, reimporter_mock): mock.return_value = True importer_mock.return_value = IMPORTER_MOCK_RETURN_VALUE @@ -3221,7 +3223,7 @@ def test_create_authorized_product_name_engagement_name_auto_create_product(self @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_global_permission") + @patch("dojo.authorization.api_permissions.user_has_global_permission") def test_create_authorized_product_name_engagement_name_auto_create_product_type(self, mock, importer_mock, reimporter_mock): mock.return_value = True importer_mock.return_value = IMPORTER_MOCK_RETURN_VALUE @@ -3252,7 +3254,7 @@ def test_create_authorized_product_name_engagement_name_auto_create_product_type @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_create_not_authorized_test_id(self, mock, importer_mock, reimporter_mock): mock.return_value = False importer_mock.return_value = IMPORTER_MOCK_RETURN_VALUE @@ -3280,7 +3282,7 @@ def test_create_not_authorized_test_id(self, mock, importer_mock, reimporter_moc @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_create_not_authorized_product_name_engagement_name_auto_create_engagement(self, mock, importer_mock, reimporter_mock): mock.return_value = False importer_mock.return_value = IMPORTER_MOCK_RETURN_VALUE @@ -3311,7 +3313,7 @@ def test_create_not_authorized_product_name_engagement_name_auto_create_engageme @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_create_not_authorized_product_name_engagement_name_auto_create_product(self, mock, importer_mock, reimporter_mock): mock.return_value = False importer_mock.return_value = IMPORTER_MOCK_RETURN_VALUE @@ -3343,7 +3345,7 @@ def test_create_not_authorized_product_name_engagement_name_auto_create_product( @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_global_permission") + @patch("dojo.authorization.api_permissions.user_has_global_permission") def test_create_not_authorized_product_name_engagement_name_auto_create_product_type(self, mock, importer_mock, reimporter_mock): mock.return_value = False importer_mock.return_value = IMPORTER_MOCK_RETURN_VALUE @@ -3374,7 +3376,7 @@ def test_create_not_authorized_product_name_engagement_name_auto_create_product_ @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_create_not_authorized_product_name_engagement_name_scan_type(self, mock, importer_mock, reimporter_mock): mock.return_value = False importer_mock.return_value = IMPORTER_MOCK_RETURN_VALUE @@ -3402,7 +3404,7 @@ def test_create_not_authorized_product_name_engagement_name_scan_type(self, mock @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_create_not_authorized_product_name_engagement_name_scan_type_title(self, mock, importer_mock, reimporter_mock): mock.return_value = False importer_mock.return_value = IMPORTER_MOCK_RETURN_VALUE @@ -3433,7 +3435,7 @@ def test_create_not_authorized_product_name_engagement_name_scan_type_title(self @patch("dojo.importers.default_reimporter.DefaultReImporter.process_scan") @patch("dojo.importers.default_importer.DefaultImporter.process_scan") - @patch("dojo.api_v2.permissions.user_has_permission") + @patch("dojo.authorization.api_permissions.user_has_permission") def test_reimport_engagement_param_ignored_permission_checked_on_name_resolved_target(self, mock, importer_mock, reimporter_mock): """ Engagement is not a declared field on ReImportScanSerializer — verify diff --git a/unittests/test_risk_acceptance_api.py b/unittests/test_risk_acceptance_api.py index 45756bb6e54..4720226f815 100644 --- a/unittests/test_risk_acceptance_api.py +++ b/unittests/test_risk_acceptance_api.py @@ -4,15 +4,17 @@ from rest_framework.reverse import reverse from rest_framework.test import APIClient, APITestCase +from dojo.authorization.models import ( + Product_Type_Member, + Role, +) from dojo.authorization.roles_permissions import Roles from dojo.models import ( Engagement, Finding, Product, Product_Type, - Product_Type_Member, Risk_Acceptance, - Role, Test, Test_Type, User, diff --git a/unittests/test_user_queries.py b/unittests/test_user_queries.py index 33daa786eb2..766d3278daa 100644 --- a/unittests/test_user_queries.py +++ b/unittests/test_user_queries.py @@ -1,19 +1,21 @@ from unittest.mock import patch -from dojo.authorization.roles_permissions import Permissions -from dojo.models import ( - Dojo_Group, +from dojo.authorization.models import ( Dojo_Group_Member, - Dojo_User, Global_Role, - Product, Product_Group, Product_Member, - Product_Type, Product_Type_Group, Product_Type_Member, Role, ) +from dojo.authorization.roles_permissions import Permissions +from dojo.models import ( + Dojo_Group, + Dojo_User, + Product, + Product_Type, +) from dojo.user.queries import ( get_authorized_users, get_authorized_users_for_product_and_product_type, @@ -71,31 +73,29 @@ def tearDown(self): self.product_type_user.delete() self.invisible_user.delete() - @patch("dojo.user.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_user_none(self, mock_current_user): mock_current_user.return_value = None self.assertQuerySetEqual(Dojo_User.objects.none(), get_authorized_users(Permissions.Product_View)) - @patch("dojo.user.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_user_admin(self, mock_current_user): mock_current_user.return_value = self.admin_user users = Dojo_User.objects.all().order_by("first_name", "last_name", "username") self.assertQuerySetEqual(users, get_authorized_users(Permissions.Product_View)) - @patch("dojo.user.queries.get_current_user") + @patch("dojo.authorization.query_registrations.get_current_user") def test_user_global_permission(self, mock_current_user): mock_current_user.return_value = self.global_permission_user users = Dojo_User.objects.all().order_by("first_name", "last_name", "username") self.assertQuerySetEqual(users, get_authorized_users(Permissions.Product_View)) - @patch("dojo.user.queries.get_current_user") - @patch("dojo.product.queries.get_current_user") - def test_user_regular(self, mock_current_user_1, mock_current_user_2): - mock_current_user_1.return_value = self.regular_user - mock_current_user_2.return_value = self.regular_user + @patch("dojo.authorization.query_registrations.get_current_user") + def test_user_regular(self, mock_current_user): + mock_current_user.return_value = self.regular_user users = Dojo_User.objects.exclude(username="invisible_user").order_by("first_name", "last_name", "username") self.assertQuerySetEqual(users, get_authorized_users(Permissions.Product_View)) diff --git a/unittests/test_utils.py b/unittests/test_utils.py index 98aa3d0589a..d3317d53e61 100644 --- a/unittests/test_utils.py +++ b/unittests/test_utils.py @@ -4,6 +4,10 @@ from django.conf import settings +from dojo.authorization.models import ( + Dojo_Group_Member, + Role, +) from dojo.authorization.roles_permissions import Roles from dojo.location.models import Location from dojo.models import ( @@ -12,14 +16,12 @@ IMPORT_REACTIVATED_FINDING, IMPORT_UNTOUCHED_FINDING, Dojo_Group, - Dojo_Group_Member, Dojo_User, Endpoint, Engagement, Notifications, Product, Product_Type, - Role, System_Settings, Test, Test_Import,