Source code for pki.views.ca
"""CA views for the PKI application."""
import logging
from typing import Any
from django.contrib import messages
from django.core.exceptions import ValidationError
from django.db.models import ProtectedError, QuerySet
from django.forms import Form
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from django.views.generic import ListView
from pki.models import CaModel
from trustpoint.views.base import BulkDeleteView, ContextDataMixin
[docs]
logger = logging.getLogger(__name__)
[docs]
class CaTableView(ContextDataMixin, ListView[CaModel]):
"""Table view for all CAs with hierarchy information."""
[docs]
template_name = 'pki/cas/cas.html'
[docs]
context_object_name = 'cas'
# Context attributes for sidebar navigation
[docs]
context_page_category = 'pki'
[docs]
context_page_name = 'cas'
[docs]
def get_queryset(self) -> QuerySet[CaModel]:
"""Return all CA models with parent relationships and domains prefetched, ordered by hierarchy."""
return (super().get_queryset()
.select_related('parent_ca')
.prefetch_related('domains'))
[docs]
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
"""Add hierarchy information to each CA and apply hierarchical ordering."""
context = super().get_context_data(**kwargs)
# Handle both paginated and non-paginated cases
if context.get('page_obj'):
# Paginated case
ca_list = list(context['page_obj'].object_list)
else:
# Non-paginated case
ca_list = list(context.get(self.context_object_name, []))
if ca_list:
ca_list = self._hierarchical_sort(ca_list)
# Add display properties to each CA
for ca in ca_list:
ca.display_indentation = f'{ca.get_hierarchy_depth() * 20}px' # type: ignore[attr-defined]
if context.get('page_obj'):
context['page_obj'].object_list = ca_list
else:
context[self.context_object_name] = ca_list
return context
def _hierarchical_sort(self, cas: list[CaModel]) -> list[CaModel]:
"""Sort CAs hierarchically: roots first, then their children recursively.
Args:
cas: List of CA models to sort.
Returns:
Hierarchically sorted list of CAs.
"""
children_map: dict[int | None, list[CaModel]] = {}
for ca in cas:
parent_id = ca.parent_ca.id if ca.parent_ca else None
if parent_id not in children_map:
children_map[parent_id] = []
children_map[parent_id].append(ca)
for children in children_map.values():
children.sort(key=lambda x: x.unique_name)
result: list[CaModel] = []
def add_ca_and_children(parent_id: int | None) -> None:
"""Recursively add CA and its children to result."""
if parent_id in children_map:
for ca in children_map[parent_id]:
result.append(ca)
add_ca_and_children(ca.id)
add_ca_and_children(None)
return result
[docs]
class CaBulkDeleteConfirmView(BulkDeleteView):
"""View to confirm the deletion of multiple CAs."""
[docs]
success_url = reverse_lazy('pki:cas')
[docs]
ignore_url = reverse_lazy('pki:cas')
[docs]
template_name = 'pki/cas/confirm_delete.html'
[docs]
context_object_name = 'cas'
[docs]
def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
"""Handle GET requests."""
queryset = self.get_queryset()
if not queryset.exists():
messages.error(request, _('No CAs selected for deletion.'))
return HttpResponseRedirect(self.success_url)
return super().get(request, *args, **kwargs)
def _delete_cas_hierarchically(self, cas: list[CaModel]) -> None:
"""Delete CAs in hierarchical order (children before parents).
Args:
cas: List of CA models to delete
"""
ca_ids_to_delete = {ca.id for ca in cas}
children_map: dict[int | None, list[CaModel]] = {}
for ca in cas:
parent_id = ca.parent_ca.id if ca.parent_ca else None
if parent_id not in children_map:
children_map[parent_id] = []
children_map[parent_id].append(ca)
deleted_ids: set[int] = set()
def delete_ca_and_children(ca: CaModel) -> None:
"""Recursively delete a CA and all its children."""
if ca.id in deleted_ids:
return
if ca.id in children_map:
for child in children_map[ca.id]:
delete_ca_and_children(child)
ca.delete()
deleted_ids.add(ca.id)
for ca in cas:
parent_id = ca.parent_ca.id if ca.parent_ca else None
if parent_id is None or parent_id not in ca_ids_to_delete:
delete_ca_and_children(ca)