Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions docs/content/asset_modelling/PRO__asset_attribute_options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
title: "Editable Platform, Lifecycle and Origin Lists"
description: "Customize the Platform, Lifecycle and Origin dropdown options on your assets"
audience: pro
weight: 8
---

The **Platform**, **Lifecycle** and **Origin** fields on an asset used to be fixed lists that only
DefectDojo could change. They are now editable lookup tables, so an administrator can rename the
built-in options and add their own to match how the organization actually describes its assets. This
works the same way the **Environments** list has always worked.

## Managing the options

Open **Settings > Configuration** and pick **Platforms**, **Lifecycles** or **Origins**. Each page
lists the current options with:

- **Label** — the name shown in dropdowns and on the asset (for example, "API" or "Production"). This
is the part you edit.
- **Value** — a fixed machine key stored on the asset and used by the API, imports and automation
rules. It is set when an option is created and never changes afterward, so relabeling an option
never breaks an integration.
- **Icon** and **Display order** — optional. The icon is a Font Awesome name; display order controls
where the option appears in the dropdown.
- **Assets Using** — how many assets currently reference the option.

Use **New Platform** (or Lifecycle/Origin) to add an option, click an option's label to edit it, and
delete an option from its edit screen. An option that is still in use by an asset cannot be deleted;
reassign or clear those assets first.

## How your changes appear

New and renamed options show up immediately in the **Platform / Lifecycle / Origin** dropdowns on the
asset add and edit forms, and their labels are what appears on the asset detail page, in the asset
list, in reports and on dashboard tiles.

## What the API sees

Nothing about the API contract changes. These fields are still sent and returned as the option's
**value** string (for example `"web service"` or `"production"`), so existing integrations, imports
and exports keep working. When you add an option, its value must exist before an asset or an import
can use it; an unknown value is rejected, exactly as before.

## A note on Business Criticality

**Business Criticality** is intentionally **not** editable. Its values feed asset and finding
prioritization, so its list stays fixed. If you need a bespoke attribute that should not affect
prioritization, use [Custom Fields](../pro__custom_fields/) instead.
17 changes: 11 additions & 6 deletions dojo/asset/api/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
Product,
Product_API_Scan_Configuration,
)
from dojo.product_attributes.choices import (
lifecycle_value_choices,
origin_value_choices,
platform_value_choices,
)

labels = get_labels()

Expand All @@ -38,9 +43,9 @@ class ApiAssetFilter(DojoFilter):
name_exact = CharFilter(field_name="name", lookup_expr="iexact")
description = CharFilter(lookup_expr="icontains")
business_criticality = MultipleChoiceFilter(choices=Product.BUSINESS_CRITICALITY_CHOICES)
platform = MultipleChoiceFilter(choices=Product.PLATFORM_CHOICES)
lifecycle = MultipleChoiceFilter(choices=Product.LIFECYCLE_CHOICES)
origin = MultipleChoiceFilter(choices=Product.ORIGIN_CHOICES)
platform = MultipleChoiceFilter(field_name="platform__value", choices=platform_value_choices)
lifecycle = MultipleChoiceFilter(field_name="lifecycle__value", choices=lifecycle_value_choices)
origin = MultipleChoiceFilter(field_name="origin__value", choices=origin_value_choices)
# NumberInFilter
id = NumberInFilter(field_name="id", lookup_expr="in")
asset_manager = NumberInFilter(field_name="product_manager", lookup_expr="in")
Expand Down Expand Up @@ -81,9 +86,9 @@ class ApiAssetFilter(DojoFilter):
("created", "created"),
("prod_numeric_grade", "asset_numeric_grade"),
("business_criticality", "business_criticality"),
("platform", "platform"),
("lifecycle", "lifecycle"),
("origin", "origin"),
("platform__name", "platform"),
("lifecycle__name", "lifecycle"),
("origin__name", "origin"),
("revenue", "revenue"),
("external_audience", "external_audience"),
("internet_accessible", "internet_accessible"),
Expand Down
9 changes: 6 additions & 3 deletions dojo/asset/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
Dojo_User,
Product,
Product_API_Scan_Configuration,
Product_Lifecycle,
Product_Origin,
Product_Platform,
)
from dojo.organization.api.serializers import RelatedOrganizationField
from dojo.product.queries import get_authorized_products
Expand Down Expand Up @@ -44,9 +47,9 @@ class AssetSerializer(AuthorizedUsersMemberGuardMixin, serializers.ModelSerializ
required=False, allow_null=True,
)
business_criticality = serializers.ChoiceField(choices=Product.BUSINESS_CRITICALITY_CHOICES, allow_blank=True, allow_null=True, required=False)
platform = serializers.ChoiceField(choices=Product.PLATFORM_CHOICES, allow_blank=True, allow_null=True, required=False)
lifecycle = serializers.ChoiceField(choices=Product.LIFECYCLE_CHOICES, allow_blank=True, allow_null=True, required=False)
origin = serializers.ChoiceField(choices=Product.ORIGIN_CHOICES, allow_blank=True, allow_null=True, required=False)
platform = serializers.SlugRelatedField(slug_field="value", queryset=Product_Platform.objects.all(), allow_null=True, required=False)
lifecycle = serializers.SlugRelatedField(slug_field="value", queryset=Product_Lifecycle.objects.all(), allow_null=True, required=False)
origin = serializers.SlugRelatedField(slug_field="value", queryset=Product_Origin.objects.all(), allow_null=True, required=False)

class Meta:
model = Product
Expand Down
36 changes: 36 additions & 0 deletions dojo/authorization/api_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
Test,
)

# Imported from the leaf module (not dojo.models) to avoid a circular import during
# dojo.models loading, matching how Location is imported above.
from dojo.product_attributes.models import Product_Lifecycle, Product_Origin, Product_Platform


def check_post_permission(
request: Request,
Expand Down Expand Up @@ -1115,6 +1119,38 @@ class UserHasDevelopmentEnvironmentPermission(BaseDjangoModelPermission):
}


class UserHasProductPlatformPermission(BaseDjangoModelPermission):
django_model = Product_Platform
# Reads are open to any authenticated user (the asset form and asset views need to
# render the option labels). Writes require the configuration permission.
request_method_permission_map = {
"POST": "add",
"PUT": "change",
"PATCH": "change",
"DELETE": "delete",
}


class UserHasProductLifecyclePermission(BaseDjangoModelPermission):
django_model = Product_Lifecycle
request_method_permission_map = {
"POST": "add",
"PUT": "change",
"PATCH": "change",
"DELETE": "delete",
}


class UserHasProductOriginPermission(BaseDjangoModelPermission):
django_model = Product_Origin
request_method_permission_map = {
"POST": "add",
"PUT": "change",
"PATCH": "change",
"DELETE": "delete",
}


class UserHasRegulationPermission(BaseDjangoModelPermission):
django_model = Regulation
# https://github.com/DefectDojo/django-DefectDojo/blob/963d4a35bfd8f5138330f0d70595a755fa4999b0/dojo/user/utils.py#L104
Expand Down
Loading
Loading