Skip to content

Commit

Permalink
Merge pull request #4 from maykinmedia/feature/reviewers-endpoint
Browse files Browse the repository at this point in the history
Endpoint to retrieve reviewers
  • Loading branch information
SilviaAmAm authored May 2, 2024
2 parents 5706ed3 + c33a9fa commit abe7869
Show file tree
Hide file tree
Showing 10 changed files with 226 additions and 1 deletion.
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"),
]

0 comments on commit abe7869

Please sign in to comment.