Skip to content

Commit

Permalink
Forbid deleting the last staff user
Browse files Browse the repository at this point in the history
  • Loading branch information
felixrindt committed Aug 24, 2023
1 parent 2f4ed4b commit 47db966
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 1 deletion.
16 changes: 16 additions & 0 deletions ephios/core/forms/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,22 @@ def save(self, commit=True):
return userprofile


class DeleteUserProfileForm(Form):
def __init__(self, *args, **kwargs):
self.instance = kwargs.pop("instance")
super().__init__(*args, **kwargs)

def clean(self):
other_staff = UserProfile.objects.filter(is_staff=True).exclude(pk=self.instance.pk)
if self.instance.is_staff and not other_staff.exists():
raise ValidationError(
_(
"At least one user must be technical administrator. "
"Please promote another user before deleting this one."
)
)


class QualificationGrantForm(ModelForm):
model = QualificationGrant

Expand Down
2 changes: 2 additions & 0 deletions ephios/core/templates/core/userprofile_confirm_delete.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{% extends "base.html" %}
{% load crispy_forms_filters %}
{% load i18n %}
{% load static %}

Expand All @@ -12,6 +13,7 @@ <h1>{% translate "Delete user" %}</h1>
</div>

<form method="post">{% csrf_token %}
{{ form|as_crispy_errors }}
<p>{% blocktranslate trimmed with name=userprofile.get_full_name %}
Are you sure you want to delete the user {{ name }} ({{ userprofile }})?
{% endblocktranslate %}</p>
Expand Down
13 changes: 12 additions & 1 deletion ephios/core/views/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@
from dynamic_preferences.registries import global_preferences_registry

from ephios.api.access.auth import revoke_all_access_tokens
from ephios.core.forms.users import GroupForm, QualificationGrantFormset, UserProfileForm
from ephios.core.forms.users import (
DeleteUserProfileForm,
GroupForm,
QualificationGrantFormset,
UserProfileForm,
)
from ephios.core.models import QualificationGrant, UserProfile
from ephios.core.services.notifications.types import (
NewProfileNotification,
Expand Down Expand Up @@ -130,6 +135,12 @@ class UserProfileDeleteView(CustomPermissionRequiredMixin, DeleteView):
model = UserProfile
permission_required = "core.delete_userprofile"
template_name = "core/userprofile_confirm_delete.html"
form_class = DeleteUserProfileForm

def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["instance"] = self.object
return kwargs

def get_success_url(self):
messages.info(
Expand Down
20 changes: 20 additions & 0 deletions tests/core/test_views_userprofile.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,23 @@ def test_staffuser_can_change_is_staff_flag(self, django_app, volunteer, superus
form["is_staff"] = True
form.submit()
assert UserProfile.objects.get(id=volunteer.id).is_staff

def test_staff_flag_cannot_be_removed_from_last_staff_user(self, django_app, superuser):
form = django_app.get(
reverse("core:userprofile_edit", kwargs={"pk": superuser.id}), user=superuser
).form
form["is_staff"] = False
response = form.submit()
assert response.status_code == 200
assert "least one user must be technical administrator" in response.text
assert UserProfile.objects.get(id=superuser.id).is_staff

def test_last_staff_user_cannot_be_deleted(self, django_app, groups, manager, superuser):
response = django_app.get(
reverse("core:userprofile_delete", kwargs={"pk": superuser.id}),
user=manager,
)
response = response.form.submit()
assert response.status_code == 200
assert "least one user must be technical administrator" in response.text
assert UserProfile.objects.get(id=superuser.id).is_staff

0 comments on commit 47db966

Please sign in to comment.