Skip to content

Commit

Permalink
Add delete Santa configuration view
Browse files Browse the repository at this point in the history
  • Loading branch information
np5 committed Nov 22, 2024
1 parent b0127ca commit c0694d9
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 3 deletions.
94 changes: 93 additions & 1 deletion tests/santa/test_setup_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ def test_configuration_without_event_links(self):
self.assertNotContains(response, reverse("santa:configuration_events_store_redirect",
args=(configuration.pk,)))

def test_configuration_with_event_links(self):
def test_configuration_some_links(self):
configuration = force_configuration()
self._login("santa.view_configuration",
"santa.view_enrollment",
Expand All @@ -220,6 +220,28 @@ def test_configuration_with_event_links(self):
self.assertTemplateUsed(response, "santa/configuration_detail.html")
self.assertContains(response, reverse("santa:configuration_events",
args=(configuration.pk,)))
self.assertNotContains(response, reverse("santa:update_configuration",
args=(configuration.pk,)))
self.assertNotContains(response, reverse("santa:delete_configuration",
args=(configuration.pk,)))

def test_configuration_all_links(self):
configuration = force_configuration()
self._login("santa.view_configuration",
"santa.change_configuration",
"santa.delete_configuration",
"santa.view_enrollment",
"santa.view_rule",
"santa.view_ruleset")
response = self.client.get(configuration.get_absolute_url())
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "santa/configuration_detail.html")
self.assertContains(response, reverse("santa:configuration_events",
args=(configuration.pk,)))
self.assertContains(response, reverse("santa:update_configuration",
args=(configuration.pk,)))
self.assertContains(response, reverse("santa:delete_configuration",
args=(configuration.pk,)))

def test_configuration_events_redirect(self):
configuration = force_configuration()
Expand Down Expand Up @@ -482,6 +504,76 @@ def test_post_update_configuration_view_remount_usb_mode_error(self):
self.assertFormError(response.context["form"],
"remount_usb_mode", "'Block USB mount' must be set to use this option")

# delete configuration

def test_delete_configuration_redirect(self):
configuration = force_configuration()
self._login_redirect(reverse("santa:delete_configuration", args=(configuration.pk,)))

def test_post_delete_configuration_view_permission_denied(self):
self._login("santa.add_configuration", "santa.view_configuration")
configuration = force_configuration()
response = self.client.post(reverse("santa:delete_configuration", args=(configuration.pk,)),
follow=True)
self.assertEqual(response.status_code, 403)

def test_post_delete_configuration_cannot_be_deleted(self):
_, enrollment = self._force_enrollment()
self._login("santa.delete_configuration", "santa.view_configuration")
response = self.client.post(reverse("santa:delete_configuration", args=(enrollment.configuration.pk,)),
follow=True)
self.assertEqual(response.status_code, 404)

@patch("zentral.core.queues.backends.kombu.EventQueues.post_event")
def test_post_delete_configuration_view(self, post_event):
configuration = force_configuration()
configuration_pk = configuration.pk
self._login("santa.delete_configuration", "santa.view_configuration")
with self.captureOnCommitCallbacks(execute=True) as callbacks:
response = self.client.post(reverse("santa:delete_configuration", args=(configuration.pk,)),
follow=True)
self.assertTemplateUsed(response, "santa/configuration_list.html")
self.assertEqual(len(callbacks), 1)
self.assertNotContains(response, configuration.name)
event = post_event.call_args_list[0].args[0]
self.assertIsInstance(event, AuditEvent)
self.assertEqual(
event.payload,
{"action": "deleted",
"object": {
"model": "santa.configuration",
"pk": str(configuration_pk),
"prev_value": {
"pk": configuration_pk,
"name": configuration.name,
"client_mode": "Monitor",
"client_certificate_auth": False,
"batch_size": 50,
"full_sync_interval": 600,
"enable_bundles": False,
"enable_transitive_rules": False,
"allowed_path_regex": "",
"blocked_path_regex": "",
"block_usb_mount": False,
"remount_usb_mode": [],
"allow_unknown_shard": 100,
"enable_all_event_upload_shard": 0,
"sync_incident_severity": 0,
"voting_realm": None,
"banned_threshold": -26,
"default_ballot_target_types": [],
"default_voting_weight": 0,
"globally_allowlisted_threshold": 50,
"partially_allowlisted_threshold": 5,
"created_at": configuration.created_at,
"updated_at": configuration.updated_at
},
}}
)
metadata = event.metadata.serialize()
self.assertEqual(metadata["objects"], {"santa_configuration": [str(configuration_pk)]})
self.assertEqual(sorted(metadata["tags"]), ["santa", "zentral"])

# enrollment

def test_create_enrollment_redirect(self):
Expand Down
10 changes: 9 additions & 1 deletion zentral/contrib/santa/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,14 @@ def summary(self):
columns = [c.name for c in cursor.description]
return [dict(zip(columns, row)) for row in cursor.fetchall()]

def for_deletion(self):
return self.annotate(
# no enrollments
enrollment_count=Count("enrollment")
).filter(
enrollment_count=0
)


class Configuration(models.Model):
MONITOR_MODE = 1
Expand Down Expand Up @@ -731,7 +739,7 @@ def serialize_for_event(self, keys_only=False):
return d

def can_be_deleted(self):
return self.enrollment_set.all().count() == 0
return Configuration.objects.for_deletion().filter(pk=self.pk).exists()


class VotingGroup(models.Model):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{% extends 'base.html' %}

{% block content %}
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item"><a href="{% url 'santa:index' %}">Santa</a></li>
<li class="breadcrumb-item"><a href="{% url 'santa:configuration_list' %}">Configurations</a></li>
<li class="breadcrumb-item"><a href="{% url 'santa:configuration' object.pk %}">{{ object }}</a></li>
<li class="breadcrumb-item active">Delete</li>
</ol>

<h2>Delete Santa configuration</h2>

<form method="post" class="update-form">{% csrf_token %}
<p>Do you really want to delete this Santa configuration?</p>
<p>
<a class="btn btn-outline-secondary" href="{{ object.get_absolute_url }}">
Cancel
</a>
<button class="btn btn-danger" type="submit">Delete</button>
</p>
</form>
{% endblock %}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ <h3 class="m-0 fs-5 text-secondary">Santa configuration</h3>
{% url 'santa:update_configuration' object.pk as url %}
{% button 'UPDATE' url "Edit Configuration" %}
{% endif %}
{% if perms.santa.delete_configuration and object.can_be_deleted %}
{% url 'santa:delete_configuration' object.pk as url %}
{% button 'DELETE' url "Delete Configuration" %}
{% endif %}
</div>
</div>

Expand Down
3 changes: 3 additions & 0 deletions zentral/contrib/santa/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
path('configurations/<int:pk>/update/',
views.UpdateConfigurationView.as_view(),
name='update_configuration'),
path('configurations/<int:pk>/delete/',
views.DeleteConfigurationView.as_view(),
name='delete_configuration'),
path('configurations/<int:pk>/enrollments/create/',
views.CreateEnrollmentView.as_view(),
name='create_enrollment'),
Expand Down
11 changes: 10 additions & 1 deletion zentral/contrib/santa/views/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from django.db import transaction
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from django.urls import reverse, reverse_lazy
from django.views.generic import DetailView, TemplateView, View
from django.views.generic.edit import DeleteView, FormView, UpdateView
from zentral.contrib.inventory.forms import EnrollmentSecretForm
Expand Down Expand Up @@ -143,6 +143,15 @@ class UpdateConfigurationView(PermissionRequiredMixin, UpdateViewWithAudit):
form_class = ConfigurationForm


class DeleteConfigurationView(PermissionRequiredMixin, DeleteViewWithAudit):
permission_required = "santa.delete_configuration"
model = Configuration
success_url = reverse_lazy("santa:configuration_list")

def get_queryset(self):
return self.model.objects.for_deletion()


# voting groups


Expand Down

0 comments on commit c0694d9

Please sign in to comment.