Skip to content

Commit

Permalink
Add delete Santa enrollment view
Browse files Browse the repository at this point in the history
  • Loading branch information
np5 committed Nov 29, 2024
1 parent ae65a92 commit 57df162
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 4 deletions.
68 changes: 68 additions & 0 deletions tests/santa/test_setup_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,74 @@ def test_enrollment_configuration_profile(self):
self.assertEqual(response.status_code, 200)
self.assertEqual(response['Content-Type'], "application/octet-stream")

# delete enrollment

def test_delete_enrollment_redirect(self):
configuration, enrollment = self._force_enrollment()
self._login_redirect(reverse("santa:delete_enrollment", args=(configuration.pk, enrollment.pk)))

def test_delete_enrollment_permission_denied(self):
configuration, enrollment = self._force_enrollment()
self._login()
response = self.client.get(reverse("santa:delete_enrollment", args=(configuration.pk, enrollment.pk)))
self.assertEqual(response.status_code, 403)

def test_delete_enrollment_get(self):
configuration, enrollment = self._force_enrollment()
self._login("santa.delete_enrollment")
response = self.client.get(reverse("santa:delete_enrollment", args=(configuration.pk, enrollment.pk)))
self.assertTemplateUsed(response, "santa/enrollment_confirm_delete.html")
self.assertContains(response, "Delete enrollment")
self.assertContains(response, configuration.name)

@patch("zentral.contrib.inventory.models.BaseEnrollment.can_be_deleted")
def test_delete_enrollment_cannot_be_deleted_get(self, can_be_deleted):
can_be_deleted.return_value = False
configuration, enrollment = self._force_enrollment()
self.assertFalse(enrollment.can_be_deleted())
self._login("santa.delete_enrollment")
response = self.client.get(reverse("santa:delete_enrollment", args=(configuration.pk, enrollment.pk)))
self.assertEqual(response.status_code, 404)

@patch("zentral.core.queues.backends.kombu.EventQueues.post_event")
def test_delete_enrollment_post(self, post_event):
configuration, enrollment = self._force_enrollment()
enrollment_pk = enrollment.pk
enrollment_secret = enrollment.secret
mbu = enrollment_secret.meta_business_unit
self._login("santa.delete_enrollment", "santa.view_configuration")
with self.captureOnCommitCallbacks(execute=True) as callbacks:
response = self.client.post(reverse("santa:delete_enrollment", args=(configuration.pk, enrollment.pk)),
follow=True)
self.assertTemplateUsed(response, "santa/configuration_detail.html")
self.assertContains(response, configuration.name)
self.assertEqual(configuration.enrollment_set.count(), 0)
self.assertEqual(len(callbacks), 1)
event = post_event.call_args_list[0].args[0]
self.assertIsInstance(event, AuditEvent)
self.assertEqual(
event.payload,
{"action": "deleted",
"object": {
"model": "santa.enrollment",
"pk": str(enrollment_pk),
'prev_value': {'configuration': {'name': configuration.name, 'pk': configuration.pk},
'created_at': enrollment.created_at,
'enrollment_secret': {'created_at': enrollment_secret.created_at,
'is_expired': False,
'is_revoked': False,
'is_used_up': False,
'meta_business_unit': {'name': mbu.name,
'pk': mbu.pk},
'pk': enrollment_secret.pk,
'request_count': 0},
'pk': enrollment.pk}
}}
)
metadata = event.metadata.serialize()
self.assertEqual(metadata["objects"], {"santa_configuration": [str(configuration.pk)]})
self.assertEqual(sorted(metadata["tags"]), ["santa", "zentral"])

# create voting group

def test_create_voting_group_redirect(self):
Expand Down
7 changes: 5 additions & 2 deletions zentral/contrib/santa/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -783,13 +783,16 @@ class Enrollment(BaseEnrollment):
def get_description_for_distributor(self):
return "Santa configuration: {}".format(self.configuration)

def get_absolute_url(self):
return "{}#enrollment_{}".format(reverse("santa:configuration", args=(self.configuration.pk,)), self.pk)

def serialize_for_event(self):
enrollment_dict = super().serialize_for_event()
enrollment_dict["configuration"] = self.configuration.serialize_for_event(keys_only=True)
return enrollment_dict

def get_absolute_url(self):
return "{}#enrollment_{}".format(reverse("santa:configuration", args=(self.configuration.pk,)), self.pk)
def linked_objects_keys_for_event(self):
return {"santa_configuration": ((self.configuration.pk,),)}


class EnrolledMachineManager(models.Manager):
Expand Down
11 changes: 11 additions & 0 deletions zentral/contrib/santa/templates/santa/configuration_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ <h2 class="m-0">Enrollment{{ enrollments_count|pluralize }} ({{ enrollments_coun
<th scope="col">Version</th>
<th scope="col">Distributor</th>
<th scope="col"></th>
{% if perms.santa.delete_enrollment %}
<th scope="col"></th>
{% endif %}
</tr>
</thead>
<tbody>
Expand Down Expand Up @@ -280,6 +283,14 @@ <h2 class="m-0">Enrollment{{ enrollments_count|pluralize }} ({{ enrollments_coun
<span class="text-danger">Enrollment used up.</span>
{% endif %}
</td>
{% if perms.santa.delete_enrollment %}
<td>
{% if enrollment.can_be_deleted %}
{% url 'santa:delete_enrollment' object.pk enrollment.pk as url %}
{% button 'DELETE' url "Delete enrollment" %}
{% endif %}
</td>
{% endif %}
</tr>
{% endwith %}
{% endwith %}
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="{{ object.get_absolute_url }}">{{ object.configuration }}</a></li>
<li class="breadcrumb-item active">Delete enrollment</li>
</ol>

<h2>Delete Santa enrollment</h2>

<form method="post" class="update-form">{% csrf_token %}
<p>Do you really want to delete this Santa enrollment?</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 %}
3 changes: 3 additions & 0 deletions zentral/contrib/santa/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
path('configurations/<int:pk>/enrollments/create/',
views.CreateEnrollmentView.as_view(),
name='create_enrollment'),
path('configurations/<int:configuration_pk>/enrollments/<int:pk>/delete/',
views.DeleteEnrollmentView.as_view(),
name='delete_enrollment'),
path('configurations/<int:configuration_pk>/target_states/<int:pk>/reset/',
views.ResetTargetStateView.as_view(),
name='reset_target_state'),
Expand Down
25 changes: 23 additions & 2 deletions zentral/contrib/santa/views/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from urllib.parse import urlencode
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db import transaction
from django.http import HttpResponseRedirect
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse, reverse_lazy
from django.views.generic import DetailView, TemplateView, View
Expand All @@ -16,7 +16,7 @@
ConfigurationForm, EnrollmentForm,
VotingGroupForm,
RuleForm, RuleSearchForm, UpdateRuleForm)
from zentral.contrib.santa.models import Configuration, Rule, Target, VotingGroup
from zentral.contrib.santa.models import Configuration, Enrollment, Rule, Target, VotingGroup
from zentral.contrib.santa.terraform import iter_resources
from zentral.core.stores.conf import frontend_store, stores
from zentral.core.stores.views import EventsView, FetchEventsView, EventsStoreRedirectView
Expand Down Expand Up @@ -248,6 +248,27 @@ def post(self, request, *args, **kwargs):
return self.forms_invalid(secret_form, enrollment_form)


class DeleteEnrollmentView(PermissionRequiredMixin, DeleteViewWithAudit):
permission_required = "santa.delete_enrollment"
model = Enrollment

def get_queryset(self):
return self.model.objects.select_related(
"configuration"
).filter(
configuration__pk=self.kwargs["configuration_pk"]
)

def get_object(self):
obj = super().get_object()
if not obj.can_be_deleted():
raise Http404
return obj

def get_success_url(self):
return self.object.configuration.get_absolute_url()


# rules


Expand Down

0 comments on commit 57df162

Please sign in to comment.