Source code for help_pages.devices_help_views

"""This module contains all views concerning the help pages used within the devices app."""

from __future__ import annotations

import enum
from dataclasses import dataclass
from typing import TYPE_CHECKING

from devices.models import (
    DeviceModel,
    IssuedCredentialModel,
    NoOnboardingConfigModel,
    NoOnboardingPkiProtocol,
    OnboardingConfigModel,
    OnboardingProtocol,
)
from devices.views import (
    ActiveTrustpointTlsServerCredentialModelMissingErrorMsg,
    DeviceWithoutDomainErrorMsg,
    PublicKeyInfoMissingErrorMsg,
)
from django.http import Http404, HttpRequest, HttpResponse, HttpResponseRedirect
from django.urls import reverse
from django.utils.html import format_html
from django.utils.translation import gettext as _non_lazy
from django.utils.translation import gettext_lazy as _
from django.views.generic.detail import DetailView
from pki.models.truststore import ActiveTrustpointTlsServerCredentialModel
from management.models import TlsSettings

from help_pages.commands import (
    CmpClientCertificateCommandBuilder,
    CmpSharedSecretCommandBuilder,
    EstClientCertificateCommandBuilder,
    EstUsernamePasswordCommandBuilder,
    KeyGenCommandBuilder,
)
from help_pages.help_section import HelpPage, HelpRow, HelpSection, ValueRenderType
from trustpoint.page_context import (
    DEVICES_PAGE_CATEGORY,
    DEVICES_PAGE_DEVICES_SUBCATEGORY,
    DEVICES_PAGE_OPC_UA_SUBCATEGORY,
    PageContextMixin,
)

if TYPE_CHECKING:
    from collections.abc import Callable
    from typing import Any, Self

    from django.utils.safestring import SafeString
    from pki.models.domain import DomainModel


@dataclass(frozen=True)
[docs] class ApplicationCertificateProfileData: """The application certificate profile data class that holds both value an label."""
[docs] name: str
[docs] label: str
[docs] class ApplicationCertificateProfile(enum.Enum): """Allowed application credential profiles."""
[docs] TLS_CLIENT = ApplicationCertificateProfileData('tls-client', 'TLS-Client Certificate')
[docs] TLS_SERVER = ApplicationCertificateProfileData('tls-server', 'TLS-Server Certificate')
[docs] OPC_UA_CLIENT = ApplicationCertificateProfileData('opc-ua-client', 'OPC-UA-Client Certificate')
[docs] OPC_UA_SERVER = ApplicationCertificateProfileData('opc-ua-server', 'OPC-UA-Server Certificate')
@property
[docs] def name(self) -> str: """Return the name of the profile. Returns: The name of the profile. """ return self.value.name
@property
[docs] def label(self) -> str: """Return the label of the profile. Returns: The label of the profile. """ return self.value.label
@classmethod
[docs] def from_name(cls, name: str) -> Self: """Gets the ApplicationCertificateProfile matching the name. Returns: The matching ApplicationCertificateProfile. Raises: ValueError: If no matching ApplicationCertifiateProfile is found for the name provided. """ for member in cls: if member.value.name == name: return member err_msg = f'No ApplicationCertificateProfile with name={name} found.' raise ValueError(err_msg)
@classmethod
[docs] def from_label(cls, label: str) -> Self: """Gets the ApplicationCertificateProfile matching the label. Returns: The matching ApplicationCertificateProfile. Raises: ValueError: If no matching ApplicationCertifiateProfile is found for the label provided. """ for member in cls: if member.value.name == label: return member err_msg = f'No ApplicationCertificateProfile with name={label} found.' raise ValueError(err_msg)
# ----------------------------------- Certificate Lifecycle Management - Help Pages -----------------------------------
[docs] class GetRedirectMixin: """Provides a get method that redirects to the ULR returned by get_redirect_url."""
[docs] get_redirect_url: Callable[..., str]
[docs] def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: """Pro. Args: request: The django HttpRequest object. *args: Positional arguments are passed to self.get_redirect.url() **kwargs: Keyword arguments are passed to self.get_redirect.url() Returns: The corresponding redirect. """ __ = request return HttpResponseRedirect(self.get_redirect_url(*args, **kwargs))
[docs] class AbstractNoOnboardingCmpSharedSecretHelpView(PageContextMixin, DetailView[DeviceModel]): """Abstract help view for the case of no onboarding using CMP shared-secret."""
[docs] template_name = 'help/help_page.html'
[docs] pki_protocol = NoOnboardingPkiProtocol.CMP_SHARED_SECRET
[docs] http_method_names = ('get',)
[docs] model = DeviceModel
[docs] context_object_name = 'device'
[docs] page_category = DEVICES_PAGE_CATEGORY
[docs] page_name: str
[docs] no_onboarding_config: NoOnboardingConfigModel
[docs] certificate_profile: str
[docs] host: str
[docs] def get_context_data(self, **kwargs: Any) -> dict[str, Any]: """Adds information about the required OpenSSL commands to the context. Args: **kwargs: Keyword arguments passed to super().get_context_data. Returns: The context to render the page. """ context = super().get_context_data(**kwargs) if not self.object.no_onboarding_config: err_msg = _('Onboarding is configured for this device.') raise Http404(err_msg) self.no_onboarding_config = self.object.no_onboarding_config if not self.object.domain: err_msg = _('No domain is configured for this device.') raise Http404(err_msg) self.certificate_profile = self.kwargs.get('certificate_template') if not self.certificate_profile: err_msg = _('Failed to get certificate profile') self.host = ( f'{TlsSettings.get_first_ipv4_address()}:{self.request.META.get("SERVER_PORT", "443")}/' f'.well-known/cmp/certification/{ self.object.domain.unique_name }') help_page = HelpPage(heading=_non_lazy('Help - CMP Shared-Secret (HMAC)'), sections=[ self._get_summary_section(), self._get_key_generation_section(), self._get_certificate_profile_select_section(), self._get_cmp_tls_client_profile_cmd_section(), self._get_cmp_tls_server_profile_cmd_section(hidden=True), self._get_cmp_opc_ua_client_profile_cmd_section(hidden=True), self._get_cmp_opc_ua_server_profile_cmd_section(hidden=True) ]) context['help_page'] = help_page context['ValueRenderType_CODE'] = ValueRenderType.CODE.value context['ValueRenderType_PLAIN'] = ValueRenderType.PLAIN.value context['clm_url'] = f'{self.page_category}:{self.page_name}_certificate_lifecycle_management' return context
[docs] def _get_certificate_profile_select_section(self) -> HelpSection: cert_profile_select_row = HelpRow( key=_non_lazy('Certificate Profile'), value=self._get_cert_profile_select_input(), value_render_type=ValueRenderType.PLAIN ) return HelpSection( heading=_non_lazy('Certificate Profile Selection'), rows=[cert_profile_select_row] )
[docs] def _get_cmp_tls_client_profile_cmd_section(self, *, hidden: bool = False) -> HelpSection: cmp_command = CmpSharedSecretCommandBuilder.get_tls_client_profile_command( host=self.host + '/tls-client', pk=self.object.pk, shared_secret=self._get_shared_secret(), cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) openssl_cmd_tls_client_profile_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=cmp_command, value_render_type=ValueRenderType.CODE, ) return HelpSection( heading=_non_lazy('Certificate Request for TLS Client Certificates'), rows=[openssl_cmd_tls_client_profile_row], hidden=hidden, css_id=ApplicationCertificateProfile.TLS_CLIENT.name )
[docs] def _get_cmp_tls_server_profile_cmd_section(self, *, hidden: bool = False) -> HelpSection: cmp_command = CmpSharedSecretCommandBuilder.get_tls_server_profile_command( host=self.host + '/tls-server', pk=self.object.pk, shared_secret=self._get_shared_secret(), cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) openssl_cmd_tls_server_profile_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=cmp_command, value_render_type=ValueRenderType.CODE, hidden=False ) return HelpSection( heading=_non_lazy('Certificate Request for TLS Server Certificates'), rows=[openssl_cmd_tls_server_profile_row], hidden=hidden, css_id=ApplicationCertificateProfile.TLS_SERVER.name )
[docs] def _get_cmp_opc_ua_client_profile_cmd_section(self, *, hidden: bool = False) -> HelpSection: cmp_command = CmpSharedSecretCommandBuilder.get_opc_ua_client_profile_command( host=self.host + '/opc-ua-client', pk=self.object.pk, shared_secret=self._get_shared_secret(), cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) openssl_cmd_opc_ua_client_profile_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=cmp_command, value_render_type=ValueRenderType.CODE, hidden=False ) return HelpSection( heading=_non_lazy('Certificate Request for OPC-UA Client Certificates'), rows=[openssl_cmd_opc_ua_client_profile_row], hidden=hidden, css_id=ApplicationCertificateProfile.OPC_UA_CLIENT.name )
[docs] def _get_cmp_opc_ua_server_profile_cmd_section(self, *, hidden: bool = False) -> HelpSection: cmp_command = CmpSharedSecretCommandBuilder.get_opc_ua_server_profile_command( host=self.host + '/opc-ua-server', pk=self.object.pk, shared_secret=self._get_shared_secret(), cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) openssl_cmd_opc_ua_server_profile_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=cmp_command, value_render_type=ValueRenderType.CODE, hidden=False ) return HelpSection( heading=_non_lazy('Certificate Request for OPC-UA Server Certificates'), rows=[openssl_cmd_opc_ua_server_profile_row], hidden=hidden, css_id=ApplicationCertificateProfile.OPC_UA_SERVER.name )
[docs] def _get_summary_section(self) -> HelpSection: if self.object.domain is None: raise ValueError certificate_request_url = ( f'https://{self.host}<certificate_profile>' ) url_row = HelpRow( key=format_html(_non_lazy('Certificate Request URL')), value=certificate_request_url, value_render_type=ValueRenderType.CODE ) kid_row = HelpRow( key=_non_lazy('Key Identifier (KID)'), value=str(self.object.pk), value_render_type=ValueRenderType.CODE ) public_key_type_row = HelpRow( key=_non_lazy('Required Public Key Type'), value=str(self.object.public_key_info), value_render_type=ValueRenderType.CODE ) shared_secret_row = HelpRow( key=('Shared-Secret'), value=self._get_shared_secret(), value_render_type=ValueRenderType.CODE, ) return HelpSection( heading=_non_lazy('Summary'), rows=[ url_row, kid_row, public_key_type_row, shared_secret_row] )
[docs] def _get_cert_profile_select_input(self) -> SafeString: select_field = '<select id="cert-profile-select" class="form-select" aria-label="Certificate Profile Select">' for index, profile in enumerate(ApplicationCertificateProfile): if index == 0: select_field += format_html('<option value="{}" selected>{}</option>', profile.name, profile.label) else: select_field += format_html('<option value="{}">{}</option>', profile.name, profile.label) select_field += '</select>' return format_html(select_field)
[docs] def _get_key_generation_section(self) -> HelpSection: key_generation_row = HelpRow( key=_non_lazy('Generate Key-Pair'), value=self._get_key_gen_command(), value_render_type=ValueRenderType.CODE ) return HelpSection( heading=format_html(_non_lazy('Key Generation')), rows=[key_generation_row] )
[docs] def _get_key_gen_command(self) -> str: device: DeviceModel = self.object if device.domain is None: raise Http404(DeviceWithoutDomainErrorMsg) if not device.public_key_info: raise Http404(PublicKeyInfoMissingErrorMsg) try: return KeyGenCommandBuilder.get_key_gen_command( public_key_info=device.public_key_info, cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) except Exception as exception: raise Http404(exception) from exception
[docs] def _get_shared_secret(self) -> str: return self.no_onboarding_config.cmp_shared_secret
[docs] class DeviceNoOnboardingCmpSharedSecretHelpView(AbstractNoOnboardingCmpSharedSecretHelpView): """Help view for the case of no onboarding using CMP shared-secret for generic devices."""
[docs] page_name = DEVICES_PAGE_DEVICES_SUBCATEGORY
[docs] class OpcUaGdsNoOnboardingCmpSharedSecretHelpView(AbstractNoOnboardingCmpSharedSecretHelpView): """Help view for the case of no onboarding using CMP shared-secret for generic devices."""
[docs] page_name = DEVICES_PAGE_OPC_UA_SUBCATEGORY
[docs] class AbstractNoOnboardingEstUsernamePasswordHelpView(PageContextMixin, DetailView[DeviceModel]): """Abstract help view for the case of no onboarding using CMP shared-secret."""
[docs] template_name = 'help/help_page.html'
[docs] pki_protocol = NoOnboardingPkiProtocol.CMP_SHARED_SECRET
[docs] http_method_names = ('get',)
[docs] model = DeviceModel
[docs] context_object_name = 'device'
[docs] page_category = DEVICES_PAGE_CATEGORY
[docs] page_name: str
[docs] no_onboarding_config: NoOnboardingConfigModel
[docs] domain: DomainModel
[docs] certificate_profile: str
[docs] host: str
[docs] def get_context_data(self, **kwargs: Any) -> dict[str, Any]: """Adds information about the required OpenSSL commands to the context. Args: **kwargs: Keyword arguments passed to super().get_context_data. Returns: The context to render the page. """ context = super().get_context_data(**kwargs) if not self.object.no_onboarding_config: err_msg = _('Onboarding is configured for this device.') raise Http404(err_msg) self.no_onboarding_config = self.object.no_onboarding_config if not self.object.domain: err_msg = _('No domain is configured for this device.') raise Http404(err_msg) self.domain = self.object.domain self.certificate_profile = self.kwargs.get('certificate_template') if not self.certificate_profile: err_msg = _('Failed to get certificate profile') self.host = ( f'https://{TlsSettings.get_first_ipv4_address()}:{self.request.META.get("SERVER_PORT", "443")}' ) help_page = HelpPage(heading=_non_lazy('Help - EST Username & Password'), sections=[ self._get_summary_section(), self._get_download_tls_trust_store_section(), self._get_key_generation_section(), self._get_certificate_profile_select_section(), self._get_cmp_tls_client_profile_cmd_section(), self._get_cmp_tls_server_profile_cmd_section(hidden=True), self._get_cmp_opc_ua_client_profile_cmd_section(hidden=True), self._get_cmp_opc_ua_server_profile_cmd_section(hidden=True), self._get_conversion_der_to_pem_section() ]) context['help_page'] = help_page context['ValueRenderType_CODE'] = ValueRenderType.CODE.value context['ValueRenderType_PLAIN'] = ValueRenderType.PLAIN.value context['clm_url'] = f'{self.page_category}:{self.page_name}_certificate_lifecycle_management' return context
[docs] def _get_conversion_der_to_pem_section(self) -> HelpSection: conversion_cmd = EstUsernamePasswordCommandBuilder.get_conversion_der_pem_command( cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) conversion_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=conversion_cmd, value_render_type=ValueRenderType.CODE ) return HelpSection( heading='Convert the certificate from DER format to PEM format (Optional)', rows=[conversion_row] )
[docs] def _get_certificate_profile_select_section(self) -> HelpSection: cert_profile_select_row = HelpRow( key=_non_lazy('Certificate Profile'), value=self._get_cert_profile_select_input(), value_render_type=ValueRenderType.PLAIN ) return HelpSection( heading=_non_lazy('Certificate Profile Selection'), rows=[cert_profile_select_row] )
[docs] def _get_enroll_row(self, certificate_profile: str) -> HelpRow: enroll_cmd = EstUsernamePasswordCommandBuilder.get_curl_enroll_command( est_username=self.object.common_name, est_password=self.no_onboarding_config.est_password, host=f'{ self.host }/.well-known/est/{ self.domain.unique_name }/{ certificate_profile }/simpleenroll/', cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) return HelpRow( key=_non_lazy('Enroll certificate with curl'), value=enroll_cmd, value_render_type=ValueRenderType.CODE )
[docs] def _get_cmp_tls_client_profile_cmd_section(self, *, hidden: bool = False) -> HelpSection: openssl_req_cmd = EstUsernamePasswordCommandBuilder.get_tls_client_profile_command( cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) openssl_req_cmd_tls_client_profile_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=openssl_req_cmd, value_render_type=ValueRenderType.CODE, ) return HelpSection( heading=_non_lazy('Generate PKCS#10 CSR for TLS Client Certificates'), rows=[ openssl_req_cmd_tls_client_profile_row, self._get_enroll_row(certificate_profile='tls-client') ], hidden=hidden, css_id=ApplicationCertificateProfile.TLS_CLIENT.name )
[docs] def _get_cmp_tls_server_profile_cmd_section(self, *, hidden: bool = False) -> HelpSection: openssl_req_cmd = EstUsernamePasswordCommandBuilder.get_tls_server_profile_command( cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) openssl_req_cmd_tls_server_profile_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=openssl_req_cmd, value_render_type=ValueRenderType.CODE, hidden=False ) return HelpSection( heading=_non_lazy('Generate PKCS#10 CSR for TLS Server Certificates'), rows=[ openssl_req_cmd_tls_server_profile_row, self._get_enroll_row(certificate_profile='tls-server') ], hidden=hidden, css_id=ApplicationCertificateProfile.TLS_SERVER.name )
[docs] def _get_cmp_opc_ua_client_profile_cmd_section(self, *, hidden: bool = False) -> HelpSection: openssl_req_cmd = EstUsernamePasswordCommandBuilder.get_opc_ua_client_profile_command( cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) openssl_req_cmd_opc_ua_client_profile_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=openssl_req_cmd, value_render_type=ValueRenderType.CODE, hidden=False ) return HelpSection( heading=_non_lazy('Generate PKCS#10 CSR for OPC-UA Client Certificates'), rows=[ openssl_req_cmd_opc_ua_client_profile_row, self._get_enroll_row(certificate_profile='opc-ua-client') ], hidden=hidden, css_id=ApplicationCertificateProfile.OPC_UA_CLIENT.name )
[docs] def _get_cmp_opc_ua_server_profile_cmd_section(self, *, hidden: bool = False) -> HelpSection: openssl_req_cmd = EstUsernamePasswordCommandBuilder.get_opc_ua_server_profile_command( cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) openssl_req_cmd_opc_ua_server_profile_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=openssl_req_cmd, value_render_type=ValueRenderType.CODE, hidden=False ) return HelpSection( heading=_non_lazy('Generate PKCS#10 CSR for OPC-UA Server Certificates'), rows=[ openssl_req_cmd_opc_ua_server_profile_row, self._get_enroll_row(certificate_profile='opc-ua-server') ], hidden=hidden, css_id=ApplicationCertificateProfile.OPC_UA_SERVER.name )
[docs] def _get_summary_section(self) -> HelpSection: if self.object.domain is None: raise ValueError certificate_request_url = ( f'https://{ self.host }/.well-known/est/' f'{ self.object.domain.unique_name }/<certificate_profile>/simpleenroll/' ) url_row = HelpRow( key=format_html(_non_lazy('Certificate Request URL')), value=certificate_request_url, value_render_type=ValueRenderType.CODE ) public_key_type_row = HelpRow( key=_non_lazy('Required Public Key Type'), value=str(self.object.public_key_info), value_render_type=ValueRenderType.CODE ) est_username_row = HelpRow( key=('EST-Username'), value=self.object.common_name, value_render_type=ValueRenderType.CODE, ) if not self.object.no_onboarding_config: err_msg = 'Device configured for onboarding.' raise ValueError(err_msg) est_password = HelpRow( key=('EST-Password'), value=self.object.no_onboarding_config.est_password, value_render_type=ValueRenderType.CODE, ) return HelpSection( heading=_non_lazy('Summary'), rows=[ url_row, public_key_type_row, est_username_row, est_password ] )
[docs] def _get_cert_profile_select_input(self) -> SafeString: select_field = '<select id="cert-profile-select" class="form-select" aria-label="Certificate Profile Select">' for index, profile in enumerate(ApplicationCertificateProfile): if index == 0: select_field += format_html('<option value="{}" selected>{}</option>', profile.name, profile.label) else: select_field += format_html('<option value="{}">{}</option>', profile.name, profile.label) select_field += '</select>' return format_html(select_field)
[docs] def _get_key_generation_section(self) -> HelpSection: key_generation_row = HelpRow( key=_non_lazy('Generate Key-Pair'), value=self._get_key_gen_command(), value_render_type=ValueRenderType.CODE ) return HelpSection( heading=format_html(_non_lazy('Key Generation')), rows=[key_generation_row] )
[docs] def _get_key_gen_command(self) -> str: device: DeviceModel = self.object if device.domain is None: raise Http404(DeviceWithoutDomainErrorMsg) if not device.public_key_info: raise Http404(PublicKeyInfoMissingErrorMsg) try: return KeyGenCommandBuilder.get_key_gen_command( public_key_info=device.public_key_info, cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) except Exception as exception: raise Http404(exception) from exception
[docs] def _get_shared_secret(self) -> str: return self.no_onboarding_config.cmp_shared_secret
[docs] def _get_download_tls_trust_store_section(self) -> HelpSection: tls_cert_pk = self._get_tls_server_root_ca_pk() if tls_cert_pk is None: err_msg = _non_lazy('Failed to get the Trustpoint TLS Root Certificate.') raise ValueError(err_msg) download_tls_truststore_row = HelpRow( key=_non_lazy('Download TLS Trust-Store'), value=format_html( '<a href="{}" class="btn btn-primary w-100">{}</a>', reverse( 'pki:certificate-file-download-file-name', kwargs={ 'file_format': 'pem', 'pk': tls_cert_pk, 'file_name': 'trustpoint-tls-trust-store.pem' } ), _non_lazy('Download TLS Trust-Store') ), value_render_type=ValueRenderType.PLAIN ) return HelpSection( heading=_non_lazy('Download TLS Trust-Store'), rows=[download_tls_truststore_row] )
[docs] def _get_tls_server_root_ca_pk(self) -> None | int: tls_cert = ActiveTrustpointTlsServerCredentialModel.objects.first() if not tls_cert or not tls_cert.credential: raise Http404(ActiveTrustpointTlsServerCredentialModelMissingErrorMsg) root_ca_model = tls_cert.credential.get_last_in_chain() if not root_ca_model: return None return root_ca_model.pk
[docs] class DeviceNoOnboardingEstUsernamePasswordHelpView(AbstractNoOnboardingEstUsernamePasswordHelpView): """Help view for the case of no onboarding using CMP shared-secret for generic devices."""
[docs] page_name = DEVICES_PAGE_DEVICES_SUBCATEGORY
[docs] class OpcUaGdsNoOnboardingEstUsernamePasswordHelpView(AbstractNoOnboardingEstUsernamePasswordHelpView): """Help view for the case of no onboarding using CMP shared-secret for generic devices."""
[docs] page_name = DEVICES_PAGE_OPC_UA_SUBCATEGORY
[docs] class AbstractOnboardingDomainCredentialCmpSharedSecretHelpView(PageContextMixin, DetailView[DeviceModel]): """Abstract help view for the case of no onboarding using CMP shared-secret."""
[docs] template_name = 'help/help_page.html'
[docs] http_method_names = ('get',)
[docs] model = DeviceModel
[docs] context_object_name = 'device'
[docs] onboarding_protocol = OnboardingProtocol.CMP_SHARED_SECRET
[docs] page_category = DEVICES_PAGE_CATEGORY
[docs] page_name: str
[docs] onboarding_config: OnboardingConfigModel
[docs] domain: DomainModel
[docs] host: str
[docs] def get_context_data(self, **kwargs: Any) -> dict[str, Any]: """Adds information about the required OpenSSL commands to the context. Args: **kwargs: Keyword arguments passed to super().get_context_data. Returns: The context to render the page. """ context = super().get_context_data(**kwargs) if not self.object.onboarding_config: err_msg = _('Onboarding is not configured for this device.') raise Http404(err_msg) self.onboarding_config = self.object.onboarding_config if not self.object.domain: err_msg = _('No domain is configured for this device.') raise Http404(err_msg) self.domain = self.object.domain self.host = ( f'https://{TlsSettings.get_first_ipv4_address()}:{self.request.META.get("SERVER_PORT", "443")}' ) help_page = HelpPage(heading=_non_lazy('Help - CMP Shared-Secret (HMAC)'), sections=[ self._get_summary_section(), self._get_key_generation_section(), self._get_cmp_domain_credential_cmd_section(), ]) context['help_page'] = help_page context['ValueRenderType_CODE'] = ValueRenderType.CODE.value context['ValueRenderType_PLAIN'] = ValueRenderType.PLAIN.value context['clm_url'] = f'{self.page_category}:{self.page_name}_certificate_lifecycle_management' return context
[docs] def _get_certificate_profile_select_section(self) -> HelpSection: cert_profile_select_row = HelpRow( key=_non_lazy('Certificate Profile'), value=self._get_cert_profile_select_input(), value_render_type=ValueRenderType.PLAIN ) return HelpSection( heading=_non_lazy('Certificate Profile Selection'), rows=[cert_profile_select_row] )
[docs] def _get_cmp_domain_credential_cmd_section(self, *, hidden: bool = False) -> HelpSection: cmp_command = CmpSharedSecretCommandBuilder.get_domain_credential_profile_command( host=self.host, pk=self.object.pk, shared_secret=self._get_shared_secret(), domain_name=self.domain.unique_name ) openssl_cmd_tls_client_profile_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=cmp_command, value_render_type=ValueRenderType.CODE, ) return HelpSection( heading=_non_lazy('Certificate Request for TLS Client Certificates'), rows=[openssl_cmd_tls_client_profile_row], hidden=hidden, css_id=ApplicationCertificateProfile.TLS_CLIENT.name )
[docs] def _get_summary_section(self) -> HelpSection: if self.object.domain is None: raise ValueError certificate_request_url = ( f'https://{self.host}/domaincredential' ) url_row = HelpRow( key=format_html(_non_lazy('Certificate Request URL')), value=certificate_request_url, value_render_type=ValueRenderType.CODE ) kid_row = HelpRow( key=_non_lazy('Key Identifier (KID)'), value=str(self.object.pk), value_render_type=ValueRenderType.CODE ) public_key_type_row = HelpRow( key=_non_lazy('Required Public Key Type'), value=str(self.object.public_key_info), value_render_type=ValueRenderType.CODE ) shared_secret_row = HelpRow( key=('Shared-Secret'), value=self._get_shared_secret(), value_render_type=ValueRenderType.CODE, ) return HelpSection( heading=_non_lazy('Summary'), rows=[ url_row, kid_row, public_key_type_row, shared_secret_row] )
[docs] def _get_cert_profile_select_input(self) -> SafeString: select_field = '<select id="cert-profile-select" class="form-select" aria-label="Certificate Profile Select">' for index, profile in enumerate(ApplicationCertificateProfile): if index == 0: select_field += format_html('<option value="{}" selected>{}</option>', profile.name, profile.label) else: select_field += format_html('<option value="{}">{}</option>', profile.name, profile.label) select_field += '</select>' return format_html(select_field)
[docs] def _get_key_generation_section(self) -> HelpSection: key_generation_row = HelpRow( key=_non_lazy('Generate Key-Pair'), value=self._get_key_gen_command(), value_render_type=ValueRenderType.CODE ) return HelpSection( heading=format_html(_non_lazy('Key Generation')), rows=[key_generation_row] )
[docs] def _get_key_gen_command(self) -> str: device: DeviceModel = self.object if device.domain is None: raise Http404(DeviceWithoutDomainErrorMsg) if not device.public_key_info: raise Http404(PublicKeyInfoMissingErrorMsg) try: return KeyGenCommandBuilder.get_key_gen_command( public_key_info=device.public_key_info, cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)), key_name='domain-credential-key.pem' ) except Exception as exception: raise Http404(exception) from exception
[docs] def _get_shared_secret(self) -> str: return self.onboarding_config.cmp_shared_secret
[docs] class DeviceOnboardingDomainCredentialCmpSharedSecretHelpView( AbstractOnboardingDomainCredentialCmpSharedSecretHelpView): """abc."""
[docs] page_name = DEVICES_PAGE_DEVICES_SUBCATEGORY
[docs] class OpcUaGdsOnboardingDomainCredentialCmpSharedSecretHelpView( AbstractOnboardingDomainCredentialCmpSharedSecretHelpView): """abc."""
[docs] page_name = DEVICES_PAGE_OPC_UA_SUBCATEGORY
[docs] class AbstractOnboardingDomainCredentialEstUsernamePasswordHelpView(PageContextMixin, DetailView[DeviceModel]): """Abstract help view for the case of no onboarding using CMP shared-secret."""
[docs] template_name = 'help/help_page.html'
[docs] pki_protocol = NoOnboardingPkiProtocol.CMP_SHARED_SECRET
[docs] http_method_names = ('get',)
[docs] model = DeviceModel
[docs] context_object_name = 'device'
[docs] page_category = DEVICES_PAGE_CATEGORY
[docs] page_name: str
[docs] onboarding_config: OnboardingConfigModel
[docs] domain: DomainModel
[docs] certificate_profile: str
[docs] host: str
[docs] def get_context_data(self, **kwargs: Any) -> dict[str, Any]: """Adds information about the required OpenSSL commands to the context. Args: **kwargs: Keyword arguments passed to super().get_context_data. Returns: The context to render the page. """ context = super().get_context_data(**kwargs) if not self.object.onboarding_config: err_msg = _('Onboarding is not configured for this device.') raise Http404(err_msg) self.onboarding_config = self.object.onboarding_config if not self.object.domain: err_msg = _('No domain is configured for this device.') raise Http404(err_msg) self.domain = self.object.domain self.certificate_profile = 'domaincredential' if not self.certificate_profile: err_msg = _('Failed to get certificate profile') self.host = ( f'https://{TlsSettings.get_first_ipv4_address()}:{self.request.META.get("SERVER_PORT", "443")}' ) help_page = HelpPage(heading=_non_lazy('Help - EST Username & Password'), sections=[ self._get_summary_section(), self._get_download_tls_trust_store_section(), self._get_key_generation_section(), self._get_est_domain_cred_profile_cmd_section(), self._get_conversion_der_to_pem_section() ]) context['help_page'] = help_page context['ValueRenderType_CODE'] = ValueRenderType.CODE.value context['ValueRenderType_PLAIN'] = ValueRenderType.PLAIN.value context['clm_url'] = f'{self.page_category}:{self.page_name}_certificate_lifecycle_management' return context
[docs] def _get_conversion_der_to_pem_section(self) -> HelpSection: conversion_cmd = EstUsernamePasswordCommandBuilder.get_domain_credential_conversion_der_pem_command() conversion_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=conversion_cmd, value_render_type=ValueRenderType.CODE ) return HelpSection( heading='Convert the certificate from DER format to PEM format (Optional)', rows=[conversion_row] )
[docs] def _get_enroll_row(self, certificate_profile: str) -> HelpRow: enroll_cmd = EstUsernamePasswordCommandBuilder.get_curl_enroll_domain_credential_command( est_username=self.object.common_name, est_password=self.onboarding_config.est_password, host=f'{ self.host }/.well-known/est/{ self.domain.unique_name }/{ certificate_profile }/simpleenroll/', ) return HelpRow( key=_non_lazy('Enroll certificate with curl'), value=enroll_cmd, value_render_type=ValueRenderType.CODE )
[docs] def _get_est_domain_cred_profile_cmd_section(self, *, hidden: bool = False) -> HelpSection: openssl_req_cmd = EstUsernamePasswordCommandBuilder.get_domain_credential_profile_command() openssl_req_cmd_tls_client_profile_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=openssl_req_cmd, value_render_type=ValueRenderType.CODE, ) return HelpSection( heading=_non_lazy('Generate PKCS#10 CSR for the domain credential'), rows=[ openssl_req_cmd_tls_client_profile_row, self._get_enroll_row(certificate_profile=self.certificate_profile) ], hidden=hidden, css_id=ApplicationCertificateProfile.TLS_CLIENT.name )
[docs] def _get_summary_section(self) -> HelpSection: if self.object.domain is None: raise ValueError certificate_request_url = ( f'https://{ self.host }/.well-known/est/' f'{ self.object.domain.unique_name }/<certificate_profile>/simpleenroll/' ) url_row = HelpRow( key=format_html(_non_lazy('Certificate Request URL')), value=certificate_request_url, value_render_type=ValueRenderType.CODE ) public_key_type_row = HelpRow( key=_non_lazy('Required Public Key Type'), value=str(self.object.public_key_info), value_render_type=ValueRenderType.CODE ) est_username_row = HelpRow( key=('EST-Username'), value=self.object.common_name, value_render_type=ValueRenderType.CODE, ) if not self.object.onboarding_config: err_msg = 'Device not configured for onboarding.' raise ValueError(err_msg) est_password = HelpRow( key=('EST-PasswordU'), value=self.object.onboarding_config.est_password, value_render_type=ValueRenderType.CODE, ) return HelpSection( heading=_non_lazy('Summary'), rows=[ url_row, public_key_type_row, est_username_row, est_password ] )
[docs] def _get_key_generation_section(self) -> HelpSection: key_generation_row = HelpRow( key=_non_lazy('Generate Key-Pair'), value=self._get_key_gen_command(), value_render_type=ValueRenderType.CODE ) return HelpSection( heading=format_html(_non_lazy('Key Generation')), rows=[key_generation_row] )
[docs] def _get_key_gen_command(self) -> str: device: DeviceModel = self.object if device.domain is None: raise Http404(DeviceWithoutDomainErrorMsg) if not device.public_key_info: raise Http404(PublicKeyInfoMissingErrorMsg) try: return KeyGenCommandBuilder.get_key_gen_command( public_key_info=device.public_key_info, cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)), key_name='domain-credential-key.pem' ) except Exception as exception: raise Http404(exception) from exception
[docs] def _get_download_tls_trust_store_section(self) -> HelpSection: tls_cert_pk = self._get_tls_server_root_ca_pk() if tls_cert_pk is None: err_msg = _non_lazy('Failed to get the Trustpoint TLS Root Certificate.') raise ValueError(err_msg) download_tls_truststore_row = HelpRow( key=_non_lazy('Download TLS Trust-Store'), value=format_html( '<a href="{}" class="btn btn-primary w-100">{}</a>', reverse( 'pki:certificate-file-download-file-name', kwargs={ 'file_format': 'pem', 'pk': tls_cert_pk, 'file_name': 'trustpoint-tls-trust-store.pem' } ), _non_lazy('Download TLS Trust-Store') ), value_render_type=ValueRenderType.PLAIN ) return HelpSection( heading=_non_lazy('Download TLS Trust-Store'), rows=[download_tls_truststore_row] )
[docs] def _get_tls_server_root_ca_pk(self) -> None | int: tls_cert = ActiveTrustpointTlsServerCredentialModel.objects.first() if not tls_cert or not tls_cert.credential: raise Http404(ActiveTrustpointTlsServerCredentialModelMissingErrorMsg) root_ca_model = tls_cert.credential.get_last_in_chain() if not root_ca_model: return None return root_ca_model.pk
[docs] class DeviceOnboardingDomainCredentialEstUsernamePasswordHelpView( AbstractOnboardingDomainCredentialEstUsernamePasswordHelpView): """abc."""
[docs] page_name = DEVICES_PAGE_DEVICES_SUBCATEGORY
[docs] class OpcUaGdsOnboardingDomainCredentialEstUsernamePasswordHelpView( AbstractOnboardingDomainCredentialEstUsernamePasswordHelpView): """abc."""
[docs] page_name = DEVICES_PAGE_OPC_UA_SUBCATEGORY
[docs] class AbstractOnboardingCmpDomainCredentialHelpView(PageContextMixin, DetailView[DeviceModel]): """Abstract help view for the case of onboarding using CMP shared-secret."""
[docs] template_name = 'help/help_page.html'
[docs] pki_protocol = NoOnboardingPkiProtocol.CMP_SHARED_SECRET
[docs] http_method_names = ('get',)
[docs] model = DeviceModel
[docs] context_object_name = 'device'
[docs] page_category = DEVICES_PAGE_CATEGORY
[docs] page_name: str
[docs] onboarding_config: OnboardingConfigModel
[docs] certificate_profile: str
[docs] host: str
[docs] def get_context_data(self, **kwargs: Any) -> dict[str, Any]: """Adds information about the required OpenSSL commands to the context. Args: **kwargs: Keyword arguments passed to super().get_context_data. Returns: The context to render the page. """ context = super().get_context_data(**kwargs) if not self.object.onboarding_config: err_msg = _('Onboarding is not configured for this device.') raise Http404(err_msg) self.onboarding_config = self.object.onboarding_config if not self.object.domain: err_msg = _('No domain is configured for this device.') raise Http404(err_msg) self.host = ( f'{TlsSettings.get_first_ipv4_address()}:{self.request.META.get("SERVER_PORT", "443")}/' f'.well-known/cmp/certification/{ self.object.domain.unique_name }') help_page = HelpPage(heading=_non_lazy('Help - CMP Shared-Secret (HMAC)'), sections=[ self._get_summary_section(), self._get_download_cmp_signer_trust_store_section(), self._get_key_generation_section(), self._get_certificate_profile_select_section(), self._get_cmp_tls_client_profile_cmd_section(), self._get_cmp_tls_server_profile_cmd_section(hidden=True), self._get_cmp_opc_ua_client_profile_cmd_section(hidden=True), self._get_cmp_opc_ua_server_profile_cmd_section(hidden=True) ]) context['help_page'] = help_page context['ValueRenderType_CODE'] = ValueRenderType.CODE.value context['ValueRenderType_PLAIN'] = ValueRenderType.PLAIN.value context['clm_url'] = f'{self.page_category}:{self.page_name}_certificate_lifecycle_management' return context
[docs] def _get_certificate_profile_select_section(self) -> HelpSection: cert_profile_select_row = HelpRow( key=_non_lazy('Certificate Profile'), value=self._get_cert_profile_select_input(), value_render_type=ValueRenderType.PLAIN ) return HelpSection( heading=_non_lazy('Certificate Profile Selection'), rows=[cert_profile_select_row] )
[docs] def _get_cmp_tls_client_profile_cmd_section(self, *, hidden: bool = False) -> HelpSection: cmp_command = CmpClientCertificateCommandBuilder.get_tls_client_profile_command( host=self.host + '/tls-client', cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) openssl_cmd_tls_client_profile_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=cmp_command, value_render_type=ValueRenderType.CODE, ) return HelpSection( heading=_non_lazy('Certificate Request for TLS Client Certificates'), rows=[openssl_cmd_tls_client_profile_row], hidden=hidden, css_id=ApplicationCertificateProfile.TLS_CLIENT.name )
[docs] def _get_cmp_tls_server_profile_cmd_section(self, *, hidden: bool = False) -> HelpSection: cmp_command = CmpClientCertificateCommandBuilder.get_tls_server_profile_command( host=self.host + '/tls-server', cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) openssl_cmd_tls_server_profile_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=cmp_command, value_render_type=ValueRenderType.CODE, hidden=False ) return HelpSection( heading=_non_lazy('Certificate Request for TLS Server Certificates'), rows=[openssl_cmd_tls_server_profile_row], hidden=hidden, css_id=ApplicationCertificateProfile.TLS_SERVER.name )
[docs] def _get_cmp_opc_ua_client_profile_cmd_section(self, *, hidden: bool = False) -> HelpSection: cmp_command = CmpClientCertificateCommandBuilder.get_opc_ua_client_profile_command( host=self.host + '/opc-ua-client', cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) openssl_cmd_opc_ua_client_profile_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=cmp_command, value_render_type=ValueRenderType.CODE, hidden=False ) return HelpSection( heading=_non_lazy('Certificate Request for OPC-UA Client Certificates'), rows=[openssl_cmd_opc_ua_client_profile_row], hidden=hidden, css_id=ApplicationCertificateProfile.OPC_UA_CLIENT.name )
[docs] def _get_cmp_opc_ua_server_profile_cmd_section(self, *, hidden: bool = False) -> HelpSection: cmp_command = CmpClientCertificateCommandBuilder.get_opc_ua_server_profile_command( host=self.host + '/opc-ua-server', cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) openssl_cmd_opc_ua_server_profile_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=cmp_command, value_render_type=ValueRenderType.CODE, hidden=False ) return HelpSection( heading=_non_lazy('Certificate Request for OPC-UA Server Certificates'), rows=[openssl_cmd_opc_ua_server_profile_row], hidden=hidden, css_id=ApplicationCertificateProfile.OPC_UA_SERVER.name )
[docs] def _get_summary_section(self) -> HelpSection: if self.object.domain is None: raise ValueError certificate_request_url = ( f'https://{self.host}/<certificate_profile>' ) url_row = HelpRow( key=format_html(_non_lazy('Certificate Request URL')), value=certificate_request_url, value_render_type=ValueRenderType.CODE ) public_key_type_row = HelpRow( key=_non_lazy('Required Public Key Type'), value=str(self.object.public_key_info), value_render_type=ValueRenderType.CODE ) return HelpSection( heading=_non_lazy('Summary'), rows=[ url_row, public_key_type_row] )
[docs] def _get_cert_profile_select_input(self) -> SafeString: select_field = '<select id="cert-profile-select" class="form-select" aria-label="Certificate Profile Select">' for index, profile in enumerate(ApplicationCertificateProfile): if index == 0: select_field += format_html('<option value="{}" selected>{}</option>', profile.name, profile.label) else: select_field += format_html('<option value="{}">{}</option>', profile.name, profile.label) select_field += '</select>' return format_html(select_field)
[docs] def _get_key_generation_section(self) -> HelpSection: key_generation_row = HelpRow( key=_non_lazy('Generate Key-Pair'), value=self._get_key_gen_command(), value_render_type=ValueRenderType.CODE ) return HelpSection( heading=format_html(_non_lazy('Key Generation')), rows=[key_generation_row] )
[docs] def _get_key_gen_command(self) -> str: device: DeviceModel = self.object if device.domain is None: raise Http404(DeviceWithoutDomainErrorMsg) if not device.public_key_info: raise Http404(PublicKeyInfoMissingErrorMsg) try: return KeyGenCommandBuilder.get_key_gen_command( public_key_info=device.public_key_info, cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) except Exception as exception: raise Http404(exception) from exception
[docs] def _get_cmp_issuer_root_ca_pk(self) -> None | int: domain = self.object.domain if not domain: err_msg = 'domain not configured' raise ValueError(err_msg) issuing_ca = domain.issuing_ca if not issuing_ca: err_msg = 'issuing ca not configured' raise ValueError(err_msg) root_ca_model = issuing_ca.credential.get_last_in_chain() if not root_ca_model: return None return root_ca_model.pk
[docs] def _get_download_cmp_signer_trust_store_section(self) -> HelpSection: cmp_signer_pk = self._get_cmp_issuer_root_ca_pk() if cmp_signer_pk is None: err_msg = _non_lazy('Failed to get the CMP-Signer Root Certificate.') raise ValueError(err_msg) download_tls_truststore_row = HelpRow( key=_non_lazy('Download CMP-Signer Trust-Store'), value=format_html( '<a href="{}" class="btn btn-primary w-100">{}</a>', reverse( 'pki:certificate-file-download-file-name', kwargs={ 'file_format': 'pem', 'pk': cmp_signer_pk, 'file_name': 'domain-credential-full-chain.pem' } ), _non_lazy('Download CMP-Signer Trust-Store') ), value_render_type=ValueRenderType.PLAIN ) return HelpSection( heading=_non_lazy('Download CMP-Signer Trust-Store'), rows=[download_tls_truststore_row] )
[docs] class DeviceOnboardingCmpDomainCredentialHelpView(AbstractOnboardingCmpDomainCredentialHelpView): """Help view for the case of no onboarding using CMP shared-secret for generic devices."""
[docs] page_name = DEVICES_PAGE_DEVICES_SUBCATEGORY
[docs] class OpcUaGdsOnboardingCmpDomainCredentialHelpView(AbstractOnboardingCmpDomainCredentialHelpView): """Help view for the case of no onboarding using CMP shared-secret for generic devices."""
[docs] page_name = DEVICES_PAGE_OPC_UA_SUBCATEGORY
[docs] class AbstractDeviceOnboardingEstDomainCredentialHelpView(PageContextMixin, DetailView[DeviceModel]): """Abstract help view for the case of no onboarding using CMP shared-secret."""
[docs] template_name = 'help/help_page.html'
[docs] pki_protocol = NoOnboardingPkiProtocol.CMP_SHARED_SECRET
[docs] http_method_names = ('get',)
[docs] model = DeviceModel
[docs] context_object_name = 'device'
[docs] page_category = DEVICES_PAGE_CATEGORY
[docs] page_name: str
[docs] onboarding_config: OnboardingConfigModel
[docs] domain: DomainModel
[docs] certificate_profile: str
[docs] host: str
[docs] def get_context_data(self, **kwargs: Any) -> dict[str, Any]: """Adds information about the required OpenSSL commands to the context. Args: **kwargs: Keyword arguments passed to super().get_context_data. Returns: The context to render the page. """ context = super().get_context_data(**kwargs) if not self.object.onboarding_config: err_msg = _('Onboarding is not configured for this device.') raise Http404(err_msg) self.onboarding_config = self.object.onboarding_config if not self.object.domain: err_msg = _('No domain is configured for this device.') raise Http404(err_msg) self.domain = self.object.domain self.certificate_profile = self.kwargs.get('certificate_template') if not self.certificate_profile: err_msg = _('Failed to get certificate profile') self.host = ( f'https://{TlsSettings.get_first_ipv4_address()}:{self.request.META.get("SERVER_PORT", "443")}' ) help_page = HelpPage(heading=_non_lazy('Help - EST with Domain Credential'), sections=[ self._get_summary_section(), self._get_download_tls_trust_store_section(), self._get_key_generation_section(), self._get_certificate_profile_select_section(), self._get_cmp_tls_client_profile_cmd_section(), self._get_cmp_tls_server_profile_cmd_section(hidden=True), self._get_cmp_opc_ua_client_profile_cmd_section(hidden=True), self._get_cmp_opc_ua_server_profile_cmd_section(hidden=True), self._get_conversion_der_to_pem_section() ]) context['help_page'] = help_page context['ValueRenderType_CODE'] = ValueRenderType.CODE.value context['ValueRenderType_PLAIN'] = ValueRenderType.PLAIN.value context['clm_url'] = f'{self.page_category}:{self.page_name}_certificate_lifecycle_management' return context
[docs] def _get_conversion_der_to_pem_section(self) -> HelpSection: conversion_cmd = EstUsernamePasswordCommandBuilder.get_conversion_der_pem_command( cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) conversion_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=conversion_cmd, value_render_type=ValueRenderType.CODE ) return HelpSection( heading='Convert the certificate from DER format to PEM format (Optional)', rows=[conversion_row] )
[docs] def _get_certificate_profile_select_section(self) -> HelpSection: cert_profile_select_row = HelpRow( key=_non_lazy('Certificate Profile'), value=self._get_cert_profile_select_input(), value_render_type=ValueRenderType.PLAIN ) return HelpSection( heading=_non_lazy('Certificate Profile Selection'), rows=[cert_profile_select_row] )
[docs] def _get_enroll_row(self, certificate_profile: str) -> HelpRow: enroll_cmd = EstClientCertificateCommandBuilder.get_curl_enroll_application_credential( host=f'{ self.host }/.well-known/est/{ self.domain.unique_name }/{ certificate_profile }/simpleenroll/', cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) return HelpRow( key=_non_lazy('Enroll certificate with curl'), value=enroll_cmd, value_render_type=ValueRenderType.CODE )
[docs] def _get_cmp_tls_client_profile_cmd_section(self, *, hidden: bool = False) -> HelpSection: openssl_req_cmd = EstUsernamePasswordCommandBuilder.get_tls_client_profile_command( cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) openssl_req_cmd_tls_client_profile_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=openssl_req_cmd, value_render_type=ValueRenderType.CODE, ) return HelpSection( heading=_non_lazy('Generate PKCS#10 CSR for TLS Client Certificates'), rows=[ openssl_req_cmd_tls_client_profile_row, self._get_enroll_row(certificate_profile='tls-client') ], hidden=hidden, css_id=ApplicationCertificateProfile.TLS_CLIENT.name )
[docs] def _get_cmp_tls_server_profile_cmd_section(self, *, hidden: bool = False) -> HelpSection: openssl_req_cmd = EstUsernamePasswordCommandBuilder.get_tls_server_profile_command( cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) openssl_req_cmd_tls_server_profile_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=openssl_req_cmd, value_render_type=ValueRenderType.CODE, hidden=False ) return HelpSection( heading=_non_lazy('Generate PKCS#10 CSR for TLS Server Certificates'), rows=[ openssl_req_cmd_tls_server_profile_row, self._get_enroll_row(certificate_profile='tls-server') ], hidden=hidden, css_id=ApplicationCertificateProfile.TLS_SERVER.name )
[docs] def _get_cmp_opc_ua_client_profile_cmd_section(self, *, hidden: bool = False) -> HelpSection: openssl_req_cmd = EstUsernamePasswordCommandBuilder.get_opc_ua_client_profile_command( cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) openssl_req_cmd_opc_ua_client_profile_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=openssl_req_cmd, value_render_type=ValueRenderType.CODE, hidden=False ) return HelpSection( heading=_non_lazy('Generate PKCS#10 CSR for OPC-UA Client Certificates'), rows=[ openssl_req_cmd_opc_ua_client_profile_row, self._get_enroll_row(certificate_profile='opc-ua-client') ], hidden=hidden, css_id=ApplicationCertificateProfile.OPC_UA_CLIENT.name )
[docs] def _get_cmp_opc_ua_server_profile_cmd_section(self, *, hidden: bool = False) -> HelpSection: openssl_req_cmd = EstUsernamePasswordCommandBuilder.get_opc_ua_server_profile_command( cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) openssl_req_cmd_opc_ua_server_profile_row = HelpRow( key=_non_lazy('OpenSSL Command'), value=openssl_req_cmd, value_render_type=ValueRenderType.CODE, hidden=False ) return HelpSection( heading=_non_lazy('Generate PKCS#10 CSR for OPC-UA Server Certificates'), rows=[ openssl_req_cmd_opc_ua_server_profile_row, self._get_enroll_row(certificate_profile='opc-ua-server') ], hidden=hidden, css_id=ApplicationCertificateProfile.OPC_UA_SERVER.name )
[docs] def _get_summary_section(self) -> HelpSection: if self.object.domain is None: raise ValueError certificate_request_url = ( f'https://{ self.host }/.well-known/est/' f'{ self.object.domain.unique_name }/<certificate_profile>/simpleenroll/' ) url_row = HelpRow( key=format_html(_non_lazy('Certificate Request URL')), value=certificate_request_url, value_render_type=ValueRenderType.CODE ) public_key_type_row = HelpRow( key=_non_lazy('Required Public Key Type'), value=str(self.object.public_key_info), value_render_type=ValueRenderType.CODE ) return HelpSection( heading=_non_lazy('Summary'), rows=[ url_row, public_key_type_row ] )
[docs] def _get_cert_profile_select_input(self) -> SafeString: select_field = '<select id="cert-profile-select" class="form-select" aria-label="Certificate Profile Select">' for index, profile in enumerate(ApplicationCertificateProfile): if index == 0: select_field += format_html('<option value="{}" selected>{}</option>', profile.name, profile.label) else: select_field += format_html('<option value="{}">{}</option>', profile.name, profile.label) select_field += '</select>' return format_html(select_field)
[docs] def _get_key_generation_section(self) -> HelpSection: key_generation_row = HelpRow( key=_non_lazy('Generate Key-Pair'), value=self._get_key_gen_command(), value_render_type=ValueRenderType.CODE ) return HelpSection( heading=format_html(_non_lazy('Key Generation')), rows=[key_generation_row] )
[docs] def _get_key_gen_command(self) -> str: device: DeviceModel = self.object if device.domain is None: raise Http404(DeviceWithoutDomainErrorMsg) if not device.public_key_info: raise Http404(PublicKeyInfoMissingErrorMsg) try: return KeyGenCommandBuilder.get_key_gen_command( public_key_info=device.public_key_info, cred_number=len(IssuedCredentialModel.objects.filter(device=self.object)) ) except Exception as exception: raise Http404(exception) from exception
[docs] def _get_download_tls_trust_store_section(self) -> HelpSection: tls_cert_pk = self._get_tls_server_root_ca_pk() if tls_cert_pk is None: err_msg = _non_lazy('Failed to get the Trustpoint TLS Root Certificate.') raise ValueError(err_msg) download_tls_truststore_row = HelpRow( key=_non_lazy('Download TLS Trust-Store'), value=format_html( '<a href="{}" class="btn btn-primary w-100">{}</a>', reverse( 'pki:certificate-file-download-file-name', kwargs={ 'file_format': 'pem', 'pk': tls_cert_pk, 'file_name': 'trustpoint-tls-trust-store.pem' } ), _non_lazy('Download TLS Trust-Store') ), value_render_type=ValueRenderType.PLAIN ) return HelpSection( heading=_non_lazy('Download TLS Trust-Store'), rows=[download_tls_truststore_row] )
[docs] def _get_tls_server_root_ca_pk(self) -> None | int: tls_cert = ActiveTrustpointTlsServerCredentialModel.objects.first() if not tls_cert or not tls_cert.credential: raise Http404(ActiveTrustpointTlsServerCredentialModelMissingErrorMsg) root_ca_model = tls_cert.credential.get_last_in_chain() if not root_ca_model: return None return root_ca_model.pk
[docs] class DeviceOnboardingEstDomainCredentialHelpView(AbstractDeviceOnboardingEstDomainCredentialHelpView): """abc."""
[docs] page_name = DEVICES_PAGE_DEVICES_SUBCATEGORY
[docs] class OpcUaGdsOnboardingEstDomainCredentialHelpView(AbstractDeviceOnboardingEstDomainCredentialHelpView): """abc."""
[docs] page_name = DEVICES_PAGE_OPC_UA_SUBCATEGORY