"""This module contains all views concerning the PKI -> Truststore section."""
from __future__ import annotations
from typing import TYPE_CHECKING
from django.contrib import messages
from django.core.exceptions import ValidationError
from django.db.models import ProtectedError
from django.http import Http404, HttpRequest, HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from django.views.generic.base import RedirectView
from django.views.generic.detail import DetailView
from django.views.generic.edit import FormView
from django.views.generic.list import ListView
from trustpoint_core.archiver import ArchiveFormat, Archiver
from trustpoint_core.serializer import CertificateFormat
from pki.forms import TruststoreAddForm
from pki.models import DomainModel
from pki.models.truststore import TruststoreModel
from trustpoint.settings import UIConfig
from trustpoint.views.base import (
BulkDeleteView,
PrimaryKeyListFromPrimaryKeyString,
SortableTableMixin,
)
if TYPE_CHECKING:
from typing import Any, ClassVar
from django.forms import Form
[docs]
class TruststoresRedirectView(RedirectView):
"""View that redirects to the index of the PKI Truststores application: Truststores."""
[docs]
pattern_name = 'pki:truststores'
[docs]
class TruststoresContextMixin:
"""Mixin which adds some extra context for the PKI Views."""
[docs]
class TruststoreTableView(TruststoresContextMixin, SortableTableMixin, ListView[TruststoreModel]):
"""Truststore Table View."""
[docs]
model = TruststoreModel
[docs]
template_name = 'pki/truststores/truststores.html'
[docs]
context_object_name = 'truststores'
[docs]
paginate_by = UIConfig.paginate_by
[docs]
default_sort_param = 'unique_name'
[docs]
class TruststoreCreateView(TruststoresContextMixin, FormView[TruststoreAddForm]):
"""View for creating a new Truststore."""
[docs]
model = TruststoreModel
[docs]
template_name = 'pki/truststores/add/file_import.html'
[docs]
ignore_url = reverse_lazy('pki:truststores')
[docs]
def get_success_url(self) -> str:
"""You could still use a success URL here if needed."""
return reverse_lazy('pki:truststores')
[docs]
def get_context_data(self, **kwargs: dict[str, Any]) -> dict[str, Any]:
"""Include domain in context only if pk is present."""
context = super().get_context_data(**kwargs)
pk = self.kwargs.get('pk')
if pk:
context['domain'] = get_object_or_404(DomainModel, id=pk)
return context
[docs]
class TruststoreDetailView(TruststoresContextMixin, DetailView[TruststoreModel]):
"""The truststore detail view."""
[docs]
model = TruststoreModel
[docs]
success_url = reverse_lazy('pki:truststores')
[docs]
ignore_url = reverse_lazy('pki:truststores')
[docs]
template_name = 'pki/truststores/details.html'
[docs]
context_object_name = 'truststore'
[docs]
class TruststoreDownloadView(TruststoresContextMixin, DetailView[TruststoreModel]):
"""View for downloading a single truststore."""
[docs]
model = TruststoreModel
[docs]
success_url = reverse_lazy('pki:truststores')
[docs]
ignore_url = reverse_lazy('pki:truststores')
[docs]
template_name = 'pki/truststores/download.html'
[docs]
context_object_name = 'truststore'
[docs]
def get(
self,
request: HttpRequest,
pk: str | None = None,
file_format: str | None = None,
*args: tuple[Any],
**kwargs: dict[str, Any],
) -> HttpResponse:
"""HTTP GET Method.
If only the certificate primary key are passed in the url, the download summary will be displayed.
If value for file_format is also provided, a file download will be performed.
Compare the re_path regex in the pki.urls package.
Args:
request: The HttpRequest object.
pk: A string containing the certificate primary key.
file_format: The format of the certificate to download.
*args: Positional arguments.
**kwargs: Keyword arguments.
Returns:
HttpResponse: The HTTP response with either the download summary or a file download.
Raises:
Http404
"""
if not pk:
raise Http404
if file_format is None:
return super().get(request, *args, **kwargs)
try:
file_format_enum = CertificateFormat(value=self.kwargs.get('file_format'))
except Exception as exception:
raise Http404 from exception
certificate_serializer = TruststoreModel.objects.get(pk=pk).get_certificate_collection_serializer()
file_bytes = certificate_serializer.as_format(file_format_enum)
response = HttpResponse(file_bytes, content_type=file_format_enum.mime_type)
response['Content-Disposition'] = f'attachment; filename="truststore{file_format_enum.file_extension}"'
return response
[docs]
class TruststoreMultipleDownloadView(
TruststoresContextMixin, PrimaryKeyListFromPrimaryKeyString, ListView[TruststoreModel]
):
"""View for downloading multiple truststores at once as archived files."""
[docs]
model = TruststoreModel
[docs]
success_url = reverse_lazy('pki:truststores')
[docs]
ignore_url = reverse_lazy('pki:truststores')
[docs]
template_name = 'pki/truststores/download_multiple.html'
[docs]
context_object_name = 'truststores'
[docs]
def get_context_data(self, **kwargs: dict[str, Any]) -> dict[str, Any]:
"""Adding the part of the url to the context, that contains the truststores primary keys.
This is used for the {% url }% tags in the template to download files.
Args:
**kwargs: Keyword arguments passed to super().get_context_data().
Returns:
dict: The context data.
"""
context = super().get_context_data(**kwargs)
context['pks_path'] = self.kwargs.get('pks')
return context
[docs]
def get(
self,
request: HttpRequest,
pks: str | None = None,
file_format: None | str = None,
archive_format: None | str = None,
*args: tuple[Any],
**kwargs: dict[str, Any],
) -> HttpResponse:
"""HTTP GET Method.
If only certificate primary keys are passed in the url, the download summary will be displayed.
If value for file_format and archive_format are also provided, a file download will be performed.
Compare the re_path regex in the pki.urls package.
Args:
request: The HttpRequest object.
pks: A string containing the certificate primary keys, e.g. 1/2/3/4/5
file_format: The format of the archived certificate files.
archive_format: The archive format that will be provided as download.
*args: Positional arguments.
**kwargs: Keyword arguments.
Returns:
HttpResponse: The HTTP response with either the download summary or a file download.
Raises:
Http404
"""
if not pks:
raise Http404
pks_list = self.get_pks_as_list(pks=pks)
self.queryset = self.model.objects.filter(pk__in=pks_list)
if len(pks_list) != len(self.queryset):
raise Http404
if not file_format and not archive_format:
return super().get(request, *args, **kwargs)
try:
file_format_enum = CertificateFormat(value=file_format)
except Exception as exception:
raise Http404 from exception
try:
archive_format_enum = ArchiveFormat(archive_format)
except Exception as exception:
raise Http404 from exception
certificate_collection_serializers = [
TruststoreModel.objects.get(pk=pk).get_certificate_collection_serializer() for pk in pks_list
]
data_to_archive = {
f'trust-store-{i}': trust_store.as_format(file_format_enum)
for i, trust_store in enumerate(certificate_collection_serializers)
}
file_bytes = Archiver.archive(data_to_archive, archive_format_enum)
response = HttpResponse(file_bytes, content_type=archive_format_enum.mime_type)
response['Content-Disposition'] = f'attachment; filename="truststores{archive_format_enum.file_extension}"'
return response
[docs]
class TruststoreBulkDeleteConfirmView(TruststoresContextMixin, BulkDeleteView):
"""View for confirming the deletion of multiple truststores."""
[docs]
model = TruststoreModel
[docs]
success_url = reverse_lazy('pki:truststores')
[docs]
ignore_url = reverse_lazy('pki:truststores')
[docs]
template_name = 'pki/truststores/confirm_delete.html'
[docs]
context_object_name = 'truststores'