Skip to content

Commit

Permalink
Validate no rotation for static recovery password
Browse files Browse the repository at this point in the history
  • Loading branch information
np5 committed Aug 6, 2023
1 parent b3a33c0 commit 5b85d45
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 31 deletions.
56 changes: 39 additions & 17 deletions tests/mdm/test_api_recovery_password_configs_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,16 +170,18 @@ def test_create_recovery_password_config(self, post_event):
with self.captureOnCommitCallbacks(execute=True) as callbacks:
response = self.post(reverse("mdm_api:recovery_password_configs"),
{"name": name,
"static_password": None})
"dynamic_password": False,
"static_password": "12345678"})
self.assertEqual(response.status_code, 201)
self.assertEqual(len(callbacks), 1)
rp_config = RecoveryPasswordConfig.objects.get(name=name)
self.assertEqual(rp_config.get_static_password(), "12345678")
self.assertEqual(
response.json(),
{'id': rp_config.pk,
'name': rp_config.name,
'dynamic_password': True,
'static_password': None,
'dynamic_password': False,
'static_password': "12345678",
'rotation_interval_days': 0,
'rotate_firmware_password': False,
'created_at': rp_config.created_at.isoformat(),
Expand All @@ -196,7 +198,7 @@ def test_create_recovery_password_config(self, post_event):
"new_value": {
"pk": rp_config.pk,
"name": name,
"dynamic_password": True,
"dynamic_password": False,
"rotation_interval_days": 0,
"rotate_firmware_password": False,
"created_at": rp_config.created_at,
Expand Down Expand Up @@ -226,15 +228,18 @@ def test_update_recovery_password_config_permission_denied(self):
def test_update_recovery_password_dynamic_and_static_password_error(self):
rp_config = force_recovery_password_config()
self.set_permissions("mdm.change_recoverypasswordconfig")
static_password = get_random_string(12)
response = self.put(reverse("mdm_api:recovery_password_config", args=(rp_config.pk,)),
{"name": get_random_string(12),
"dynamic_password": True,
"static_password": static_password,
"rotation_interval_days": 17,
"static_password": get_random_string(12),
"rotation_interval_days": 0,
"rotate_firmware_password": True})
self.assertEqual(response.status_code, 400)
self.assertEqual(response.json(), {'static_password': ['Cannot be set when dynamic_password is true']})
self.assertEqual(
response.json(),
{'static_password': ['Cannot be set when dynamic_password is true'],
'rotate_firmware_password': ['Cannot be set without a rotation interval']}
)

def test_update_recovery_password_required_static_password_error(self):
rp_config = force_recovery_password_config()
Expand All @@ -245,36 +250,53 @@ def test_update_recovery_password_required_static_password_error(self):
"rotation_interval_days": 17,
"rotate_firmware_password": True})
self.assertEqual(response.status_code, 400)
self.assertEqual(response.json(), {'static_password': ['Required when dynamic_password is false']})
self.assertEqual(
response.json(),
{'static_password': ['Required when dynamic_password is false'],
'rotation_interval_days': ['Cannot be set with a static password'],
'rotate_firmware_password': ['Cannot be set with a static password']}
)

def test_update_recovery_password_required_static_rotation_error(self):
rp_config = force_recovery_password_config()
self.set_permissions("mdm.change_recoverypasswordconfig")
response = self.put(reverse("mdm_api:recovery_password_config", args=(rp_config.pk,)),
{"name": get_random_string(12),
"dynamic_password": False,
"static_password": "1234568",
"rotation_interval_days": 17,
"rotate_firmware_password": True})
self.assertEqual(response.status_code, 400)
self.assertEqual(response.json(), {
'rotation_interval_days': ['Cannot be set with a static password'],
'rotate_firmware_password': ['Cannot be set with a static password']}
)

@patch("zentral.core.queues.backends.kombu.EventQueues.post_event")
def test_update_recovery_password_config(self, post_event):
rp_config = force_recovery_password_config()
prev_value = rp_config.serialize_for_event()
self.set_permissions("mdm.change_recoverypasswordconfig")
new_name = get_random_string(12)
static_password = get_random_string(12)
with self.captureOnCommitCallbacks(execute=True) as callbacks:
response = self.put(reverse("mdm_api:recovery_password_config", args=(rp_config.pk,)),
{"name": new_name,
"dynamic_password": False,
"static_password": static_password,
"dynamic_password": True,
"rotation_interval_days": 17,
"rotate_firmware_password": True})
self.assertEqual(response.status_code, 200)
self.assertEqual(len(callbacks), 1)
rp_config.refresh_from_db()
self.assertEqual(rp_config.name, new_name)
self.assertFalse(rp_config.dynamic_password)
self.assertEqual(rp_config.get_static_password(), static_password)
self.assertTrue(rp_config.dynamic_password)
self.assertEqual(rp_config.rotation_interval_days, 17)
self.assertTrue(rp_config.rotate_firmware_password)
self.assertEqual(
response.json(),
{'id': rp_config.pk,
'name': new_name,
'dynamic_password': False,
'static_password': static_password,
'dynamic_password': True,
'static_password': None,
'rotation_interval_days': 17,
'rotate_firmware_password': True,
'created_at': rp_config.created_at.isoformat(),
Expand All @@ -291,7 +313,7 @@ def test_update_recovery_password_config(self, post_event):
"new_value": {
"pk": rp_config.pk,
"name": new_name,
"dynamic_password": False,
"dynamic_password": True,
"rotation_interval_days": 17,
"rotate_firmware_password": True,
"created_at": rp_config.created_at,
Expand Down
35 changes: 24 additions & 11 deletions tests/mdm/test_management_recovery_password_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,28 @@ def test_create_recovery_password_configuration_static_password_non_ascii(self):
"to ensure that all characters are enterable on the EFI login screen."
)

def test_create_recovery_password_configuration_static_firmware_rotation_error(self):
self._login("mdm.add_recoverypasswordconfig")
response = self.client.post(reverse("mdm:create_recovery_password_config"),
{"name": get_random_string(12),
"dynamic_password": True,
"rotation_interval_days": 0,
"rotate_firmware_password": True},
follow=True)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "mdm/recoverypasswordconfig_form.html")
self.assertFormError(
response.context["form"], "rotate_firmware_password", "Cannot be set without a rotation interval."
)

@patch("zentral.core.queues.backends.kombu.EventQueues.post_event")
def test_create_recovery_password_configuration_post(self, post_event):
self._login("mdm.add_recoverypasswordconfig", "mdm.view_recoverypasswordconfig")
name = get_random_string(12)
with self.captureOnCommitCallbacks(execute=True) as callbacks:
response = self.client.post(reverse("mdm:create_recovery_password_config"),
{"name": name,
"dynamic_password": False,
"static_password": "12345678",
"dynamic_password": True,
"rotation_interval_days": 90,
"rotate_firmware_password": True},
follow=True)
Expand All @@ -171,8 +184,8 @@ def test_create_recovery_password_configuration_post(self, post_event):
self.assertTemplateUsed(response, "mdm/recoverypasswordconfig_detail.html")
rp_config = response.context["object"]
self.assertEqual(rp_config.name, name)
self.assertFalse(rp_config.dynamic_password)
self.assertEqual(rp_config.get_static_password(), "12345678")
self.assertTrue(rp_config.dynamic_password)
self.assertIsNone(rp_config.static_password)
self.assertEqual(rp_config.rotation_interval_days, 90)
self.assertTrue(rp_config.rotate_firmware_password)
event = post_event.call_args_list[0].args[0]
Expand All @@ -186,7 +199,7 @@ def test_create_recovery_password_configuration_post(self, post_event):
"new_value": {
"pk": rp_config.pk,
"name": name,
"dynamic_password": False,
"dynamic_password": True,
"rotation_interval_days": 90,
"rotate_firmware_password": True,
"created_at": rp_config.created_at,
Expand Down Expand Up @@ -274,8 +287,8 @@ def test_update_recovery_password_configuration_post(self, post_event):
{"name": new_name,
"dynamic_password": False,
"static_password": "12345678",
"rotation_interval_days": 90,
"rotate_firmware_password": True},
"rotation_interval_days": 0,
"rotate_firmware_password": False},
follow=True)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(callbacks), 1)
Expand All @@ -285,8 +298,8 @@ def test_update_recovery_password_configuration_post(self, post_event):
self.assertEqual(rp_config2.name, new_name)
self.assertFalse(rp_config2.dynamic_password)
self.assertEqual(rp_config2.get_static_password(), "12345678")
self.assertEqual(rp_config2.rotation_interval_days, 90)
self.assertTrue(rp_config2.rotate_firmware_password)
self.assertEqual(rp_config2.rotation_interval_days, 0)
self.assertFalse(rp_config2.rotate_firmware_password)
event = post_event.call_args_list[0].args[0]
self.assertIsInstance(event, AuditEvent)
self.assertEqual(
Expand All @@ -299,8 +312,8 @@ def test_update_recovery_password_configuration_post(self, post_event):
"pk": rp_config2.pk,
"name": new_name,
"dynamic_password": False,
"rotation_interval_days": 90,
"rotate_firmware_password": True,
"rotation_interval_days": 0,
"rotate_firmware_password": False,
"created_at": rp_config2.created_at,
"updated_at": rp_config2.updated_at
},
Expand Down
9 changes: 9 additions & 0 deletions zentral/contrib/mdm/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,15 @@ def clean(self):
static_password = self.cleaned_data.get("static_password")
if not static_password and "static_password" not in self.errors:
self.add_error("static_password", "This field is required when not using dynamic passwords.")
self.cleaned_data["rotation_interval_days"] = 0
self.cleaned_data["rotate_firmware_password"] = False
else:
if (
self.cleaned_data.get("rotate_firmware_password")
and not self.cleaned_data.get("rotation_interval_days")
):
self.add_error("rotate_firmware_password",
"Cannot be set without a rotation interval.")

def save(self):
if self.instance.pk and not self.cleaned_data.get("dynamic_password"):
Expand Down
18 changes: 15 additions & 3 deletions zentral/contrib/mdm/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,23 @@ class Meta:
def validate(self, data):
dynamic_password = data.get("dynamic_password", True)
static_password = data.get("get_static_password")
rotation_interval_days = data.get("rotation_interval_days")
rotate_firmware_password = data.get("rotate_firmware_password")
errors = {}
if dynamic_password:
if static_password:
raise serializers.ValidationError({"static_password": "Cannot be set when dynamic_password is true"})
elif not static_password:
raise serializers.ValidationError({"static_password": "Required when dynamic_password is false"})
errors["static_password"] = "Cannot be set when dynamic_password is true"
else:
if not static_password:
errors["static_password"] = "Required when dynamic_password is false"
if rotation_interval_days:
errors["rotation_interval_days"] = "Cannot be set with a static password"
if rotate_firmware_password:
errors["rotate_firmware_password"] = "Cannot be set with a static password"
if rotate_firmware_password and not rotation_interval_days:
errors["rotate_firmware_password"] = "Cannot be set without a rotation interval"
if errors:
raise serializers.ValidationError(errors)
return data

def create(self, validated_data):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ <h3>{% if object %}Update recovery password configuration <i>{{ object }}</i>{%
var checked = $("#id_dynamic_password").prop("checked");
var $input = $("#id_static_password");
$input.parent().parent().toggle(!checked);
var $rotIntInput = $("#id_rotation_interval_days");
$rotIntInput.parent().parent().toggle(checked);
var $rotFirmPwd = $("#id_rotate_firmware_password");
$rotFirmPwd.parent().parent().parent().parent().toggle(checked);
}

$(document).ready(function () {
Expand Down

0 comments on commit 5b85d45

Please sign in to comment.