Coverage for product_risk_suite / product / admin.py: 56%
80 statements
« prev ^ index » next coverage.py v7.14.0, created at 2026-05-13 23:42 +0000
« prev ^ index » next coverage.py v7.14.0, created at 2026-05-13 23:42 +0000
1from django.contrib import admin
2from django.forms import SelectMultiple
3from django.db import models
5from .models import Product, ProductRiskAnalysis, ProductRiskEntry
6from threat_model.shared_models import ThreatModelConnectionName
7from guardian.admin import GuardedModelAdmin
9class ProductAdmin(GuardedModelAdmin):
10 list_display = ["title", "description"]
11 prepopulated_fields = {"slug": ("title",)}
13class ProductRiskAnalysisAdmin(admin.ModelAdmin):
14 filter_horizontal = ['risk_entries']
16 list_display = ["name", "n_entries"]
17 prepopulated_fields = {"slug": ("name",)}
19 @admin.display(description='N Entries')
20 def n_entries(self, obj):
21 return len(obj.risk_entries.all())
23class ProductRiskEntryAdmin(admin.ModelAdmin):
24 ordering = ['risk__custom_id']
25 def get_form(self, request, obj=None, **kwargs):
26 form = super().get_form(request, obj, **kwargs)
27 if obj is None:
28 form.base_fields['svg_id'].help_text = "Here all available IDs are listed, if you do not know which matches, assign this entry to a product analysis and that to a product, then this list is reduced."
29 else:
30 form.base_fields['svg_id'].help_text = "Select one or more threat model connections this risk applies to." + "<br>" + form.base_fields['svg_id'].help_text
31 return form
33 def formfield_for_manytomany(self, db_field, request, **kwargs):
34 if db_field.name == "svg_id":
35 # Get the current ProductRiskEntry instance (if editing)
36 obj = request.resolver_match.kwargs.get('object_id')
37 if obj:
38 try:
39 instance = ProductRiskEntry.objects.get(pk=obj)
40 ras = ProductRiskAnalysis.objects.get(risk_entries=instance)
41 products = Product.objects.get(analyzes=ras)
42 connection_names = ThreatModelConnectionName.objects.filter(
43 threatmodel__product=products
44 ).distinct().order_by("name")
45 kwargs['queryset'] = connection_names
46 except (ProductRiskEntry.DoesNotExist, AttributeError):
47 pass
48 except (ProductRiskAnalysis.DoesNotExist, AttributeError):
49 pass
50 except (Product.DoesNotExist, AttributeError):
51 pass
52 else:
53 # for a new instance ALL available tm link names are listed
54 pass
55 return super().formfield_for_manytomany(db_field, request, **kwargs)
57 filter_horizontal = ['evidences','svg_id']
59 list_display = [
60 "risk_Id",
61 "risk_title",
62 "risk_asset",
63 "risk_origin",
64 "risk_stride",
65 "tm_linked",
66 "risk_initial",
67 "risk_accepted",
68 "risk_mitigated",
69 "risk_after_mitigation",
70 ]
72 @admin.display(description='Risk Id')
73 def risk_Id(self, obj):
74 return obj.risk.custom_id
76 @admin.display(description='risk title')
77 def risk_title(self, obj):
78 return obj.risk.title
80 @admin.display(description='Asset')
81 def risk_asset(self, obj):
82 return obj.risk.asset
84 @admin.display(description='Origin')
85 def risk_origin(self, obj):
86 return obj.risk.origin
88 @admin.display(description='Stride')
89 def risk_stride(self, obj):
90 return obj.risk.stride_str
92 @admin.display(description='Risk Accepted')
93 def risk_accepted(self, obj):
94 return obj.risk_accepted
96 @admin.display(description='TM Linked')
97 def tm_linked(self, obj):
98 return len(obj.svg_id.all()) > 0
99 tm_linked.boolean = True
101 @admin.display(description='Risk Initial')
102 def risk_initial(self, obj):
103 return obj.risk_rating_initial.risk
105 @admin.display(description='Mitigated')
106 def risk_mitigated(self, obj):
107 return obj.risk_mitigation != None
109 @admin.display(description='Risk after Mitigation')
110 def risk_after_mitigation(self, obj):
111 if obj.risk_rating_after_mitigation:
112 return obj.risk_rating_after_mitigation.risk
113 return None
115admin.site.register(Product, ProductAdmin)
116admin.site.register(ProductRiskAnalysis, ProductRiskAnalysisAdmin)
117admin.site.register(ProductRiskEntry, ProductRiskEntryAdmin)