Skip to content

Commit 5b3dc72

Browse files
dogboatvalentijnscholtenMaffoochclaude
authored
Split CI/CD-related infrastructure into its own model (#15002)
* wip cicd split * migration updates * old ui * display under ci/cd eng details * cicdinfra type field is read only on form to prevent changes in usage on engagements * migration update after merge * add cicd link to navbar * css for disabled controls (new old ui) * eng view updates * classic copies of templates * make (name, infra type) unique * linter fix * cicd tests * linter fixes * test fixes * refactor model into module in line with how it should be done * refactor cicd api to correct layout * refactor (old) ui into proper package * add cicd_infrastructure to modules state table * remove config authorized decorator usage * rework api perms to allow read by authed user, edit by superuser * rework edit-related view perms * comment * disallow editing of infra type on instances in api, check in model save as well * reorder listings to scm/build/orch for logical usage * fix migration * comments * migration updates after merge * Renumber cicd_infrastructure migration to 0285 after dev merge * fix: make CICDInfrastructure a grantable configuration permission CICDInfrastructure was never added to get_configuration_permissions_fields(), so its view/add/change/delete codenames never reached a user's configuration permissions. Non-superusers could not be granted access and the new CI/CD Infrastructure UI/menu was effectively superuser-only, unlike the Tool_Configuration it replaces. Register it (with a "CI/CD Infrastructure" display name) so it is grantable like every other configuration model. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Valentijn Scholten <valentijnscholten@gmail.com> Co-authored-by: Cody Maffucci <46459665+Maffooch@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d604c30 commit 5b3dc72

36 files changed

Lines changed: 1036 additions & 132 deletions

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ Modules in various stages of reorganization:
5454
|--------|-----------|-------------|-----|------|--------|
5555
| **url** | In module | N/A | Done | Done | **Complete** |
5656
| **location** | In module | N/A | N/A | Done | **Complete** |
57+
| **cicd_infrastructure** | In module | N/A | Done | Done | **Complete** |
5758
| **product_type** | In module | N/A | Done | Done | **Complete** (#14970) |
5859
| **test** | In module | N/A | Done | Done | **Complete** (#14971) |
5960
| **engagement** | In module | In module | Done | Done | **Complete** (#14972) |

dojo/authorization/api_permissions.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from dojo.importers.auto_create_context import AutoCreateContextManager
2121
from dojo.location.models import Location
2222
from dojo.models import (
23+
CICDInfrastructure,
2324
Development_Environment,
2425
Endpoint,
2526
Engagement,
@@ -1114,6 +1115,18 @@ class UserHasRegulationPermission(BaseDjangoModelPermission):
11141115
}
11151116

11161117

1118+
class UserHasCICDInfrastructurePermission(BaseDjangoModelPermission):
1119+
django_model = CICDInfrastructure
1120+
# Reads are open to any authenticated user (engagement views surface CICD
1121+
# references and need to render them). Writes require elevated privileges.
1122+
request_method_permission_map = {
1123+
"POST": "add",
1124+
"PUT": "change",
1125+
"PATCH": "change",
1126+
"DELETE": "delete",
1127+
}
1128+
1129+
11171130
def raise_no_auto_create_import_validation_error(
11181131
test_title,
11191132
scan_type,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import dojo.cicd_infrastructure.admin # noqa: F401

dojo/cicd_infrastructure/admin.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from django.contrib import admin
2+
3+
from dojo.cicd_infrastructure.models import CICDInfrastructure
4+
5+
6+
@admin.register(CICDInfrastructure)
7+
class CICDInfrastructureAdmin(admin.ModelAdmin):
8+
9+
"""Admin support for the CICDInfrastructure model."""

dojo/cicd_infrastructure/api/__init__.py

Whitespace-only changes.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from rest_framework import serializers
2+
3+
from dojo.models import CICDInfrastructure
4+
5+
6+
class CICDInfrastructureSerializer(serializers.ModelSerializer):
7+
class Meta:
8+
model = CICDInfrastructure
9+
fields = "__all__"
10+
11+
def get_fields(self):
12+
fields = super().get_fields()
13+
if self.instance is not None:
14+
# Disallow editing of infra type on an instance; see the matching comment on CICDInfrastructure#save()
15+
fields["infrastructure_type"].read_only = True
16+
return fields
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from dojo.cicd_infrastructure.api.views import CICDInfrastructureViewSet
2+
3+
4+
def add_cicd_infrastructure_urls(router):
5+
router.register(r"cicd_infrastructure", CICDInfrastructureViewSet, basename="cicd_infrastructure")
6+
return router
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from django_filters.rest_framework import DjangoFilterBackend
2+
from rest_framework.permissions import IsAuthenticated
3+
4+
from dojo.api_v2.views import DojoModelViewSet
5+
from dojo.authorization import api_permissions as permissions
6+
from dojo.cicd_infrastructure.api.serializers import CICDInfrastructureSerializer
7+
from dojo.models import CICDInfrastructure
8+
9+
10+
# Authorization: read open to authenticated users; write requires configuration permission.
11+
class CICDInfrastructureViewSet(
12+
DojoModelViewSet,
13+
):
14+
serializer_class = CICDInfrastructureSerializer
15+
queryset = CICDInfrastructure.objects.none()
16+
filter_backends = (DjangoFilterBackend,)
17+
filterset_fields = ["id", "name", "infrastructure_type"]
18+
permission_classes = (IsAuthenticated, permissions.UserHasCICDInfrastructurePermission)
19+
20+
def get_queryset(self):
21+
return CICDInfrastructure.objects.all().order_by("id")

dojo/cicd_infrastructure/models.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from django.core.exceptions import ValidationError
2+
from django.db import models
3+
from django.utils.translation import gettext as _
4+
5+
6+
class CICDInfrastructure(models.Model):
7+
INFRASTRUCTURE_TYPE_CHOICES = (
8+
("scm_server", "SCM Server"),
9+
("build_server", "Build Server"),
10+
("orchestration", "Orchestration Engine"),
11+
)
12+
13+
name = models.CharField(max_length=200)
14+
description = models.CharField(max_length=2000, blank=True, default="")
15+
url = models.URLField(max_length=2000, blank=True, default="", help_text=_("Public URL of the tool (e.g., https://jenkins.company.com)"))
16+
infrastructure_type = models.CharField(max_length=30, choices=INFRASTRUCTURE_TYPE_CHOICES)
17+
18+
class Meta:
19+
ordering = ["name"]
20+
unique_together = [("name", "infrastructure_type")]
21+
22+
def __str__(self):
23+
return self.name
24+
25+
def save(self, *args, **kwargs):
26+
if self.pk:
27+
# Disallow editing of the infra type on an instance; engagement CICD FKs are scoped by infrastructure_type
28+
# via limit_choices_to (build_server/scm_server/orchestration), so changing the type would create a
29+
# semantic conflict between an engagement and this object.
30+
current_type = type(self).objects.filter(pk=self.pk).values_list("infrastructure_type", flat=True).first()
31+
if current_type is not None and current_type != self.infrastructure_type:
32+
raise ValidationError(
33+
{"infrastructure_type": _("infrastructure_type cannot be changed once set.")},
34+
)
35+
super().save(*args, **kwargs)

dojo/cicd_infrastructure/ui/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)