Source code for cmp.views

"""This module contains the CMP endpoints (views)."""

from __future__ import annotations

from typing import TYPE_CHECKING, cast

from django.http import Http404
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from django.views.generic.base import View

from cmp.util import PkiOperation
from est.views import LoggedHttpResponse  # TEMP
from request.authentication import CmpAuthentication
from request.authorization import CmpAuthorization
from request.message_parser import CmpMessageParser
from request.message_responder.cmp import CmpMessageResponder
from request.operation_processor.general import OperationProcessor
from request.request_context import BaseRequestContext, CmpCertificateRequestContext
from request.request_validator.http_req import CmpHttpRequestValidator
from trustpoint.logger import LoggerMixin

if TYPE_CHECKING:
    from typing import Any

    from django.http import HttpRequest, HttpResponse


@method_decorator(csrf_exempt, name='dispatch')
[docs] class CmpRequestView(LoggerMixin, View): """Handles all CMP Request Messages."""
[docs] http_method_names = ('post',)
def _extract_path_params( self, kwargs: dict[str, Any], ) -> tuple[str | None, str | None, str | None]: """Extracts domain name, certificate profile, and operation from URL path parameters.""" domain_name = cast('str | None', kwargs.get('domain')) cert_profile = cast('str | None', kwargs.get('cert_profile')) operation = cast('str | None', kwargs.get('operation')) if domain_name in ['.','_']: # Handle empty domain segment domain_name = None # Handle combined 'cert_profile_or_operation' parameter cert_profile_or_operation = cast('str | None', kwargs.get('cert_profile_or_operation')) if cert_profile_or_operation: try: operation_enum = PkiOperation(cert_profile_or_operation) except ValueError: if cert_profile is not None: # cert_profile is already set, so this is invalid operation err_msg = f"Invalid CMP operation '{cert_profile_or_operation}' in URL." raise Http404(err_msg) from None cert_profile = cert_profile_or_operation else: operation = operation_enum.value # Validate operation if present elif operation is not None: try: # Validate operation PkiOperation(operation) except ValueError: err_msg = f"Invalid CMP operation '{operation}' in URL." raise Http404(err_msg) from None # Clean trailing '~' from domain name if present (due to profile syntax, but no profile given) if domain_name and domain_name.endswith('~'): domain_name = domain_name[:-1] return domain_name, cert_profile, operation
[docs] def post( self, request: HttpRequest, *args: Any, **kwargs: Any, ) -> HttpResponse: """Handles the POST requests to the CMP IR endpoint.""" del args domain_name, cert_profile, operation = self._extract_path_params(kwargs) if not cert_profile: cert_profile = 'domain_credential' ctx: BaseRequestContext try: ctx = CmpCertificateRequestContext( raw_message=request, domain_str=domain_name, protocol='cmp', operation=operation, cert_profile_str=cert_profile, ) except Exception: err_msg = 'Failed to set up CMP request context.' self.logger.exception(err_msg) return LoggedHttpResponse(err_msg, status=500) try: validator = CmpHttpRequestValidator() validator.validate(ctx) parser = CmpMessageParser() ctx = parser.parse(ctx) authenticator = CmpAuthentication() authenticator.authenticate(ctx) authorizer = CmpAuthorization( ['initialization', 'certification', 'revocation'] ) authorizer.authorize(ctx) OperationProcessor().process_operation(ctx) except Exception: self.logger.exception('Error processing CMP request') CmpMessageResponder.build_response(ctx) return ctx.to_http_response()