Source code for management.forms
"""Forms definition."""
from __future__ import annotations
from typing import TYPE_CHECKING, Any, cast
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Fieldset, Layout
from django import forms
from django.utils.translation import gettext_lazy as _
from pki.util.keys import AutoGenPkiKeyAlgorithm
from management.models import BackupOptions, SecurityConfig
from management.security import manager
from management.security.features import AutoGenPkiFeature, SecurityFeature
if TYPE_CHECKING:
from typing import Any, ClassVar
[docs]
class SecurityConfigForm(forms.ModelForm):
"""Security configuration model form."""
[docs]
FEATURE_TO_FIELDS: dict[type[SecurityFeature], list[str]] = {
AutoGenPkiFeature: ['auto_gen_pki', 'auto_gen_pki_key_algorithm'],
}
def __init__(self, *args: Any, **kwargs: Any):
"""Initialize the SecurityConfigForm."""
super().__init__(*args, **kwargs)
# Determine the 'current_mode' from form data or instance
if 'security_mode' in self.data:
current_mode = self.data['security_mode']
else:
current_mode = self.instance.security_mode if self.instance else SecurityConfig.SecurityModeChoices.LOW
sec_manager = manager.SecurityManager()
features_not_allowed = sec_manager.get_features_to_disable(current_mode)
# Disable form fields that correspond to features not allowed
for feature_cls in features_not_allowed:
field_names = self.FEATURE_TO_FIELDS.get(feature_cls, [])
for field_name in field_names:
if field_name in self.fields:
self.fields[field_name].widget.attrs['disabled'] = 'disabled'
# Disable option to change alorithm if AutoGenPKI is already enabled
if self.instance and self.instance.auto_gen_pki:
self.fields['auto_gen_pki_key_algorithm'].widget.attrs['disabled'] = 'disabled'
self.helper.layout = Layout(
Fieldset(
_('Security level presets'),
'security_mode',
),
Fieldset(
_('Advanced security settings'),
'auto_gen_pki',
'auto_gen_pki_key_algorithm',
),
)
[docs]
security_mode = forms.ChoiceField(
choices=SecurityConfig.SecurityModeChoices, widget=forms.RadioSelect(), label=''
)
[docs]
auto_gen_pki = forms.BooleanField(
required=False,
label=_('Enable local auto-generated PKI'),
widget=forms.CheckboxInput(
attrs={
'data-sl-defaults': '[true, true, false, false, false]',
'data-hide-at-sl': '[false, false, true, true, true]',
'data-more-secure': 'false',
}
),
)
[docs]
auto_gen_pki_key_algorithm = forms.ChoiceField(
choices=AutoGenPkiKeyAlgorithm,
label=_('Key Algorithm for auto-generated PKI'),
required=False,
widget=forms.Select(attrs={'data-hide-at-sl': '[false, false, true, true, true]'}),
)
[docs]
class Meta:
[docs]
fields: ClassVar[list[str]] = ['security_mode', 'auto_gen_pki', 'auto_gen_pki_key_algorithm']
[docs]
def clean_auto_gen_pki_key_algorithm(self) -> AutoGenPkiKeyAlgorithm:
"""Keep the current value of `auto_gen_pki_key_algorithm` from the instance if the field was disabled."""
form_value = self.cleaned_data.get('auto_gen_pki_key_algorithm')
if form_value is None:
return self.instance.auto_gen_pki_key_algorithm if self.instance else AutoGenPkiKeyAlgorithm.RSA2048
return form_value
[docs]
class BackupOptionsForm(forms.ModelForm[BackupOptions]):
"""Form for editing BackupOptions settings."""
[docs]
class Meta:
"""ModelForm Meta configuration for BackupOptions."""
[docs]
fields: ClassVar[list[str]] = [
'local_storage',
'sftp_storage',
'host',
'port',
'user',
'auth_method',
'password',
'private_key',
'key_passphrase',
'remote_directory',
]
[docs]
widgets: ClassVar[dict[str, Any]] = {
'local_storage': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'sftp_storage': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'host': forms.TextInput(attrs={'class': 'form-control'}),
'port': forms.NumberInput(attrs={'class': 'form-control'}),
'user': forms.TextInput(attrs={'class': 'form-control'}),
'auth_method': forms.Select(attrs={'class': 'form-select'}),
'password': forms.PasswordInput(
attrs={'class': 'form-control'}, render_value=True
),
'private_key': forms.Textarea(attrs={'class': 'form-control', 'rows': 5}),
'key_passphrase': forms.PasswordInput(
attrs={'class': 'form-control'}, render_value=True
),
'remote_directory': forms.TextInput(attrs={'class': 'form-control'}),
}
[docs]
def clean(self) -> dict[str, Any]:
"""Validate required fields based on selected authentication method."""
cleaned: dict[str, Any] = super().clean() or {}
auth = cleaned.get('auth_method')
sftp_storage = cleaned.get('sftp_storage')
if sftp_storage:
missing_fields = []
host = cleaned.get('host', '').strip()
user = cleaned.get('user', '').strip()
remote_directory = cleaned.get('remote_directory', '').strip()
if not host:
missing_fields.append('Host')
if not user:
missing_fields.append('Username')
if not remote_directory:
missing_fields.append('Remote Directory')
if missing_fields:
self.add_error(
None ,
f"The following fields are required when SFTP storage is enabled: {', '.join(missing_fields)}."
)
if auth:
pwd = cleaned.get('password', '').strip()
key = cleaned.get('private_key', '').strip()
if auth == BackupOptions.AuthMethod.PASSWORD and not pwd:
self.add_error('password', 'Password is required when using password authentication.')
if auth == BackupOptions.AuthMethod.SSH_KEY and not key:
self.add_error('private_key', 'Private key is required when using SSH Key authentication.')
return cleaned
[docs]
class IPv4AddressForm(forms.Form):
"""A form for selecting and updating an IPv4 address.
This form provides an interface for selecting an IPv4 address from
a list of Subject Alternative Names (SANs).
Attributes:
ipv4_address: A choice field for selecting the IPv4 address.
"""
def __init__(self, *args: Any, **kwargs: Any) -> None:
"""Initialize the IPv4AddressForm."""
san_ips = kwargs.pop('san_ips', [])
saved_ipv4_address = kwargs.get('initial', {}).get('ipv4_address')
if saved_ipv4_address and saved_ipv4_address not in san_ips:
san_ips.insert(0, saved_ipv4_address)
super().__init__(*args, **kwargs)
ipv4_field = cast(forms.ChoiceField, self.fields['ipv4_address'])
ipv4_field.choices = [(ip, ip) for ip in san_ips]