"""Settings views."""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from django.contrib import messages
from django.core.management import call_command
from django.shortcuts import redirect
from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext as _
from django.views import View
from django.views.generic.edit import FormView
from management.forms import NotificationConfigForm, SecurityConfigForm
from management.models import LoggingConfig, NotificationConfig, SecurityConfig
from management.security.features import AutoGenPkiFeature
from management.security.mixins import SecurityLevelMixin
from pki.util.keys import AutoGenPkiKeyAlgorithm
from trustpoint.logger import LoggerMixin
from trustpoint.page_context import PageContextMixin
if TYPE_CHECKING:
from typing import Any
from django.http import HttpRequest, HttpResponse
[docs]
LOG_LEVELS=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
[docs]
class SettingsView(PageContextMixin, SecurityLevelMixin, LoggerMixin, FormView[SecurityConfigForm]):
"""A view for managing security settings in the Trustpoint application.
This view handles the display and processing of the SecurityConfigForm,
allowing users to configure security-related settings such as security mode,
auto-generated PKI, and notification configurations.
"""
[docs]
template_name = 'management/settings.html'
[docs]
success_url = reverse_lazy('management:settings')
[docs]
page_category = 'management'
[docs]
page_name = 'settings'
[docs]
def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
"""Handle POST requests for both SecurityConfig and NotificationConfig forms.
Parameters
----------
request : HttpRequest
The HTTP request object.
*args : tuple
Positional arguments.
**kwargs : dict
Keyword arguments.
Returns:
-------
HttpResponse
A response object.
"""
# Check which form was submitted
if 'security_configuration' in request.POST:
# Handle SecurityConfigForm
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
return self.form_invalid(form)
if 'notification_configuration' in request.POST:
notification_config = NotificationConfig.get()
notification_form = NotificationConfigForm(request.POST, instance=notification_config)
self.logger.debug('NotificationConfigForm validation: is_valid=%s', notification_form.is_valid())
if notification_form.is_valid():
# Get the enabled status before and after
was_enabled = notification_config.enabled
notification_form.save()
notification_config.refresh_from_db()
cleaned_enabled = notification_config.enabled
if cleaned_enabled:
needs_init = not notification_config.notification_cycle_enabled or not was_enabled
if needs_init:
try:
notification_config.notification_cycle_enabled = True
notification_config.save(update_fields=['notification_cycle_enabled'])
call_command('init_notifications')
if not was_enabled:
messages.success(
request,
_('Notifications enabled and notification cycle initialized.')
)
else:
messages.success(
request,
_('Notification configuration saved and cycle reinitialized.')
)
except Exception: # pylint: disable=broad-except
self.logger.exception('Error initializing notifications')
messages.error(
request,
_('Notifications saved but error initializing notification cycle.')
)
else:
messages.success(request, _('Notification configuration saved successfully.'))
else:
if notification_config.notification_cycle_enabled:
notification_config.notification_cycle_enabled = False
notification_config.save(update_fields=['notification_cycle_enabled'])
messages.success(request, _('Notifications disabled successfully.'))
return redirect(self.success_url)
self.logger.error('Notification form errors: %s', notification_form.errors)
self.logger.error('Notification form non-field errors: %s', notification_form.non_field_errors())
messages.error(request, _('Error saving notification configuration'))
return self.render_to_response(
self.get_context_data(notification_form=notification_form)
)
return super().post(request, *args, **kwargs)
[docs]
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
"""Build the context dictionary for rendering the settings page.
This method adds page metadata, notification configurations, log levels,
task execution status, and the current log level to the context.
Parameters
----------
**kwargs : dict
Additional context variables.
Returns:
-------
dict[str, Any]
The context dictionary for the template.
"""
context = super().get_context_data(**kwargs)
context['page_category'] = 'management'
context['page_name'] = 'settings'
context['notification_configurations_json'] = SecurityConfig.get_settings_preview_json()
context['loglevels'] = LOG_LEVELS
current_level_num = logging.getLogger().getEffectiveLevel()
context['current_loglevel'] = logging.getLevelName(current_level_num)
# Add notification configuration form
notification_config = NotificationConfig.get()
context['notification_form'] = NotificationConfigForm(instance=notification_config)
context['notification_config'] = notification_config
return context
[docs]
class ChangeLogLevelView(View):
"""A view for changing the logging level in the Trustpoint application.
This view handles POST requests to update the logging level dynamically.
"""
[docs]
def post(self, request: HttpRequest) -> HttpResponse:
"""Handle POST requests to change the logging level.
This method validates the provided log level, updates the logger
and database configuration if valid, and redirects back to the settings page.
Parameters
----------
request : HttpRequest
The HTTP request object containing the POST data.
Returns:
-------
HttpResponse
A redirect response to the settings page.
"""
level = request.POST.get('loglevel', '').upper()
if level not in LOG_LEVELS:
messages.error(request, f'Invalid log level: {level}')
else:
logger = logging.getLogger()
logger.setLevel(getattr(logging, level))
LoggingConfig.objects.update_or_create(
id=1,
defaults={'log_level': level}
)
messages.success(request, f'Log level set to {level}')
return redirect(reverse('management:settings'))