Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions trustpoint/management/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ def __init__(self, *args: Any, **kwargs: Any)-> None:
Fieldset(
_('Security level presets'),
'security_mode',
css_class='border border-primary-subtle rounded-2 bg-body-tertiary p-3 mb-4',
),
Fieldset(
_('Advanced security settings'),
Expand All @@ -182,7 +183,13 @@ def __init__(self, *args: Any, **kwargs: Any)-> None:
)

security_mode = forms.ChoiceField(
choices=SecurityConfig.SecurityModeChoices, widget=forms.RadioSelect(), label=''
choices=SecurityConfig.SecurityModeChoices,
widget=forms.RadioSelect(),
label='',
help_text=_(
'Select a preset to populate the security requirements and permitted features. '
'Higher levels apply stricter restrictions.'
),
)

auto_gen_pki = forms.BooleanField(
Expand Down Expand Up @@ -220,11 +227,25 @@ def __init__(self, *args: Any, **kwargs: Any)-> None:

permitted_no_onboarding_pki_protocols = forms.MultipleChoiceField(
choices=[(c.value, c.label) for c in NoOnboardingPkiProtocol],
widget=forms.CheckboxSelectMultiple, required=False
widget=forms.CheckboxSelectMultiple,
required=False,
help_text=_(
'Choose protocols that may issue credentials without an onboarding PKI. '
'Available choices depend on the selected security level.'
),
)
permitted_onboarding_protocols = forms.MultipleChoiceField(
choices=[(c.value, c.label) for c in OnboardingProtocol],
widget=forms.CheckboxSelectMultiple, required=False
choices=[
(c.value, c.label)
for c in OnboardingProtocol
if c is not OnboardingProtocol.BRSKI
],
widget=forms.CheckboxSelectMultiple,
required=False,
help_text=_(
'Choose supported protocols that may onboard devices. '
'Available choices depend on the selected security level.'
),
)

class Meta:
Expand Down
13 changes: 9 additions & 4 deletions trustpoint/management/models/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,15 @@ class MlDsaVariantChoices(models.TextChoices):
# Default configurations keyed by mode
# ------------------------------------------------------------------

#: All OnboardingProtocol values
_ALL_ONBOARDING_PROTOCOLS: ClassVar[list[int]] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
#: All OnboardingProtocol values except MANUAL (0)
_ONBOARDING_PROTOCOLS_NO_MANUAL: ClassVar[list[int]] = [1, 2, 3, 4, 5, 6, 7, 8, 9]
#: All implemented OnboardingProtocol values
_ALL_ONBOARDING_PROTOCOLS: ClassVar[list[int]] = [
protocol.value for protocol in OnboardingProtocol if protocol is not OnboardingProtocol.BRSKI
]
#: All implemented OnboardingProtocol values except MANUAL
_ONBOARDING_PROTOCOLS_NO_MANUAL: ClassVar[list[int]] = [
protocol.value for protocol in OnboardingProtocol
if protocol not in (OnboardingProtocol.MANUAL, OnboardingProtocol.BRSKI)
]

_MODE_DEFAULTS: ClassVar[dict[str, _SecurityModeDefaults]] = {
# ----------------------------------------------------------------
Expand Down
13 changes: 13 additions & 0 deletions trustpoint/management/tests/test_forms/test_security_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ def test_form_initialization_without_instance(self):
form = SecurityConfigForm()
self.assertIsNotNone(form.fields['security_mode'])

def test_unsupported_onboarding_protocol_is_not_offered(self) -> None:
"""Test the security policy cannot permit the unsupported BRSKI protocol."""
form = SecurityConfigForm()
choices = [value for value, _label in form.fields['permitted_onboarding_protocols'].choices]
assert OnboardingProtocol.BRSKI.value not in choices

def test_security_mode_defaults_exclude_unsupported_onboarding_protocol(self) -> None:
"""Test no security preset includes the unsupported BRSKI protocol."""
for mode in SecurityConfig.SecurityModeChoices:
config = SecurityConfig(security_mode=mode)
config.apply_security_settings(save=False)
assert OnboardingProtocol.BRSKI.value not in config.permitted_onboarding_protocols

def test_security_mode_field_is_radio_select(self):
"""Test that security_mode uses RadioSelect widget."""
form = SecurityConfigForm()
Expand Down
10 changes: 7 additions & 3 deletions trustpoint/management/tests/test_security_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,13 @@ def test_numeric_restrictions_are_monotonic(self) -> None:
def test_protocol_lists_must_be_subsets(self) -> None:
mode = SecurityConfig.SecurityModeChoices.BROWNFIELD
restricted = effective_security_defaults(mode, parse_security_restrictions({
'TP_SECURITY_PERMITTED_ONBOARDING_PROTOCOLS': 'CMP_IDEVID,EST_IDEVID,BRSKI',
'TP_SECURITY_PERMITTED_ONBOARDING_PROTOCOLS': 'CMP_IDEVID,EST_IDEVID',
}))
self.assertEqual(restricted['permitted_onboarding_protocols'], [1, 3, 6])
self.assertEqual(restricted['permitted_onboarding_protocols'], [1, 3])
with self.assertRaisesRegex(SecurityConfigurationError, 'not permitted'):
effective_security_defaults(mode, parse_security_restrictions({
'TP_SECURITY_PERMITTED_ONBOARDING_PROTOCOLS': 'CMP_IDEVID,BRSKI',
}))
with self.assertRaisesRegex(SecurityConfigurationError, 'not permitted'):
effective_security_defaults(SecurityConfig.SecurityModeChoices.HARDENED, parse_security_restrictions({
'TP_SECURITY_PERMITTED_ONBOARDING_PROTOCOLS': 'MANUAL,CMP_IDEVID',
Expand Down Expand Up @@ -146,7 +150,7 @@ def test_policy_conflict_keeps_existing_security_config_and_logs(self) -> None:
}
with patch.dict('os.environ', {
'TP_SECURITY_MODE': 'BROWNFIELD',
'TP_SECURITY_PERMITTED_ONBOARDING_PROTOCOLS': 'EST_IDEVID,BRSKI',
'TP_SECURITY_PERMITTED_ONBOARDING_PROTOCOLS': 'EST_IDEVID,CMP_IDEVID',
}, clear=True), patch.object(
SecurityConfig,
'check_policy_transition',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ <h1 class="mb-1">
{# ============================================================ #}
{# Content #}
{# ============================================================ #}
<div class="card-body">
<div class="card-body text-start">
<div class="tp-card-centered-content">

<section>
<section aria-labelledby="security-policy-heading">

<div class="mb-3">
<h2 class="h4 mb-1">
<h2 id="security-policy-heading" class="h4 mb-1">
{% trans 'Security Policy' %}
</h2>

Expand Down Expand Up @@ -77,9 +77,9 @@ <h2 class="h4 mb-1">
>
{% csrf_token %}

<fieldset class="form-group tp-form-group">
<div class="tp-form-group">
{% crispy form %}
</fieldset>
</div>


{# ================================================= #}
Expand Down
6 changes: 3 additions & 3 deletions trustpoint/templates/management/tls/method_select.html
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ <h2 class="h4 mb-1">
</h2>

<p class="text-muted mb-0">
{% trans 'Generate a new keypair and TLS certificate directly in Trustpoint.' %}
{% trans 'Generate a new keypair and TLS certificate.' %}
</p>
</div>

Expand All @@ -136,7 +136,7 @@ <h2 class="h4 mb-1">
<div>
<div class="d-flex justify-content-between align-items-start">
<h3 class="h5 mb-2">
{% trans 'Generate in Trustpoint' %}
{% trans 'Self-signed' %}
</h3>

<span class="badge text-bg-primary">
Expand All @@ -145,7 +145,7 @@ <h3 class="h5 mb-2">
</div>

<p class="text-muted">
{% trans 'Generate a new keypair and TLS certificate using the Trustpoint certificate workflow.' %}
{% trans 'Generate a self-signed keypair and TLS certificate.' %}
</p>
</div>

Expand Down
Loading