Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Endpoint to retrieve reviewers #4

Merged
merged 4 commits into from
May 2, 2024
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
18 changes: 17 additions & 1 deletion backend/src/openarchiefbeheer/accounts/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
from django.contrib.auth.admin import UserAdmin as _UserAdmin
from django.core.exceptions import PermissionDenied, ValidationError
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _

from .forms import PreventPrivilegeEscalationMixin, UserChangeForm
from .models import User
from .models import Role, User
from .utils import validate_max_user_permissions


@admin.register(User)
class UserAdmin(_UserAdmin):
hijack_success_url = reverse_lazy("root")
form = UserChangeForm
list_display = _UserAdmin.list_display + ("role",)

def get_form(self, request, obj=None, **kwargs):
ModelForm = super().get_form(request, obj, **kwargs)
Expand All @@ -32,3 +34,17 @@ def user_change_password(self, request, id, form_url=""):
raise PermissionDenied from exc

return super().user_change_password(request, id, form_url)

def get_fieldsets(self, request, obj=None):
fieldsets = super().get_fieldsets(request, obj)
return tuple(fieldsets) + ((_("Role"), {"fields": ("role",)}),)


@admin.register(Role)
class RoleAdmin(admin.ModelAdmin):
list_display = (
"name",
"can_start_destruction",
"can_review_destruction",
"can_view_case_details",
)
Empty file.
22 changes: 22 additions & 0 deletions backend/src/openarchiefbeheer/accounts/api/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from rest_framework import serializers

from ..models import Role, User


class RoleSerializer(serializers.ModelSerializer):
class Meta:
model = Role
fields = (
"name",
"can_start_destruction",
"can_review_destruction",
"can_view_case_details",
)


class UserSerializer(serializers.ModelSerializer):
role = RoleSerializer()

class Meta:
model = User
fields = ("username", "first_name", "last_name", "email", "role")
25 changes: 25 additions & 0 deletions backend/src/openarchiefbeheer/accounts/api/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from django.db.models import QuerySet
from django.utils.translation import gettext_lazy as _

from drf_spectacular.utils import extend_schema
from rest_framework.generics import ListAPIView

from openarchiefbeheer.accounts.models import User

from .serializers import UserSerializer


@extend_schema(
summary=_("Reviewers list"),
description=_(
"List all the users that have the permission to review destruction lists."
),
responses={
200: UserSerializer(many=True),
},
)
class ReviewersView(ListAPIView):
serializer_class = UserSerializer

def get_queryset(self) -> QuerySet[User]:
return User.objects.reviewers()
3 changes: 3 additions & 0 deletions backend/src/openarchiefbeheer/accounts/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@ def create_superuser(self, username, email, password, **extra_fields):
raise ValueError("Superuser must have is_superuser=True.")

return self._create_user(username, email, password, **extra_fields)

def reviewers(self):
return self.select_related("role").filter(role__can_review_destruction=True)
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Generated by Django 4.2.11 on 2024-04-30 12:51

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
("accounts", "0001_initial"),
]

operations = [
migrations.CreateModel(
name="Role",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"name",
models.CharField(
help_text="Name of the role",
max_length=255,
unique=True,
verbose_name="name",
),
),
(
"can_start_destruction",
models.BooleanField(
default=False,
help_text="Indicates whether a user can create a list of cases to be deleted.",
verbose_name="can start destruction",
),
),
(
"can_review_destruction",
models.BooleanField(
default=False,
help_text="Indicates whether a user can review a list of cases to be deleted. They can approve it, reject it or provide feedback.",
verbose_name="can review destruction",
),
),
(
"can_view_case_details",
models.BooleanField(
default=False,
help_text="Indicates whether a user can view the contents of cases in a lists.",
verbose_name="can view case details",
),
),
],
options={
"verbose_name": "role",
"verbose_name_plural": "roles",
},
),
migrations.AddField(
model_name="user",
name="role",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="accounts.role",
verbose_name="role",
),
),
]
42 changes: 42 additions & 0 deletions backend/src/openarchiefbeheer/accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ class User(AbstractBaseUser, PermissionsMixin):
),
)
date_joined = models.DateTimeField(_("date joined"), default=timezone.now)
role = models.ForeignKey(
"accounts.Role",
on_delete=models.SET_NULL,
blank=True,
null=True,
verbose_name=_("role"),
)

objects = UserManager()

Expand All @@ -63,3 +70,38 @@ def get_full_name(self):
def get_short_name(self):
"Returns the short name for the user."
return self.first_name


class Role(models.Model):
name = models.CharField(
_("name"), max_length=255, unique=True, help_text=_("Name of the role")
)
can_start_destruction = models.BooleanField(
_("can start destruction"),
default=False,
help_text=_(
"Indicates whether a user can create a list of cases to be deleted."
),
)
can_review_destruction = models.BooleanField(
_("can review destruction"),
default=False,
help_text=_(
"Indicates whether a user can review a list of cases to be deleted. "
"They can approve it, reject it or provide feedback."
),
)
can_view_case_details = models.BooleanField(
_("can view case details"),
default=False,
help_text=_(
"Indicates whether a user can view the contents of cases in a lists."
),
)

class Meta:
verbose_name = _("role")
verbose_name_plural = _("roles")

def __str__(self):
return self.name
10 changes: 10 additions & 0 deletions backend/src/openarchiefbeheer/accounts/tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,24 @@
import factory
from factory.django import DjangoModelFactory

from ..models import Role

User = get_user_model()


class RoleFactory(DjangoModelFactory):
name = factory.Sequence(lambda n: f"Role {n}")

class Meta:
model = Role


class UserFactory(DjangoModelFactory):
username = factory.Sequence(lambda n: f"user-{n}")
first_name = factory.Faker("first_name")
last_name = factory.Faker("last_name")
password = factory.PostGenerationMethodCall("set_password", "password")
role = factory.SubFactory(RoleFactory)

class Meta:
model = User
Expand Down
28 changes: 28 additions & 0 deletions backend/src/openarchiefbeheer/api/tests/test_role_endpoints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from rest_framework import status
from rest_framework.reverse import reverse
from rest_framework.test import APITestCase

from openarchiefbeheer.accounts.tests.factories import UserFactory


class RoleEndpointTests(APITestCase):
def test_user_not_logged_in(self):
response = self.client.get(reverse("api:reviewers"))

self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

def test_retrieve_reviewers(self):
admin = UserFactory.create(is_superuser=True, role=None)
UserFactory.create_batch(2, role__can_review_destruction=True)
UserFactory.create_batch(2, role__can_review_destruction=False)

self.client.force_authenticate(user=admin)
response = self.client.get(reverse("api:reviewers"))

self.assertEqual(response.status_code, status.HTTP_200_OK)

data = response.json()

self.assertEqual(len(data), 2)
self.assertTrue(data[0]["role"]["canReviewDestruction"])
self.assertTrue(data[1]["role"]["canReviewDestruction"])
3 changes: 3 additions & 0 deletions backend/src/openarchiefbeheer/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
SpectacularRedocView,
)

from openarchiefbeheer.accounts.api.views import ReviewersView

app_name = "api"

urlpatterns = [
Expand Down Expand Up @@ -35,4 +37,5 @@
"openarchiefbeheer.api.authentication.urls", namespace="authentication"
),
),
path("v1/reviewers/", ReviewersView.as_view(), name="reviewers"),
]
Loading