From 3fbce304b5bb9662e9639f47c0e51a7d54571e03 Mon Sep 17 00:00:00 2001 From: Ankur Date: Tue, 31 Oct 2023 21:32:42 +0100 Subject: [PATCH 01/20] API and serializers for position --- src/__init__.py | 0 src/involvement/serializers/position_serializer.py | 6 ++++++ src/involvement/urls.py | 8 +++++++- src/involvement/views/__init__.py | 1 + src/involvement/views/position_api.py | 10 ++++++++++ 5 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/__init__.py create mode 100644 src/involvement/serializers/position_serializer.py create mode 100644 src/involvement/views/position_api.py diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/involvement/serializers/position_serializer.py b/src/involvement/serializers/position_serializer.py new file mode 100644 index 00000000..3449a5f2 --- /dev/null +++ b/src/involvement/serializers/position_serializer.py @@ -0,0 +1,6 @@ +from involvement.models.position import Position +from rest_framework import serializers + +class PositionSerializer(serializers.ModelSerializer): + class Meta: + model = Position diff --git a/src/involvement/urls.py b/src/involvement/urls.py index 7de575a5..9435b9e6 100644 --- a/src/involvement/urls.py +++ b/src/involvement/urls.py @@ -1,6 +1,10 @@ from django.conf.urls import re_path - +from rest_framework import routers from involvement import views +# from position_api import PositionViewSet + +router = routers.SimpleRouter() +router.register(r'^position', views.position_api.PositionViewSet, basename="PositionView") urlpatterns = [ re_path( @@ -19,3 +23,5 @@ name='involvement_position_extend' ), ] + +urlpatterns += router.urls diff --git a/src/involvement/views/__init__.py b/src/involvement/views/__init__.py index 6614ab97..cad3281c 100644 --- a/src/involvement/views/__init__.py +++ b/src/involvement/views/__init__.py @@ -9,6 +9,7 @@ from .position_create_view import PositionCreateView from .position_edit_view import PositionEditView from .position_inspect_view import PositionInspectView +from .position_api import PositionViewSet from .role_create_view import RoleCreateView from .role_edit_view import RoleEditView from .application_create_view import ApplicationCreateView diff --git a/src/involvement/views/position_api.py b/src/involvement/views/position_api.py new file mode 100644 index 00000000..a98a02d9 --- /dev/null +++ b/src/involvement/views/position_api.py @@ -0,0 +1,10 @@ +from rest_framework import viewsets +from involvement.serializers.position_serializer import PositionSerializer +from rest_framework.permissions import AllowAny +from involvement.models.position import Position + +class PositionViewSet(viewsets.ModelViewSet): + serializer_class = PositionSerializer + permission_classes = [AllowAny] + queryset = Position.objects.all() + From 8c8150180cdef2a2687cfa74f7f0378d88f5ead4 Mon Sep 17 00:00:00 2001 From: Ankur Date: Wed, 8 Nov 2023 18:58:26 +0100 Subject: [PATCH 02/20] Event API --- src/events/serializers/serializers.py | 26 ++++++++++++++++++++ src/events/urls.py | 11 +++++++++ src/events/views/__init__.py | 1 + src/events/views/api.py | 34 +++++++++++++++++++++++++++ src/involvement/urls.py | 1 - 5 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/events/serializers/serializers.py create mode 100644 src/events/views/api.py diff --git a/src/events/serializers/serializers.py b/src/events/serializers/serializers.py new file mode 100644 index 00000000..4f4aa7c2 --- /dev/null +++ b/src/events/serializers/serializers.py @@ -0,0 +1,26 @@ +from events.models.costs import Costs +from events.models.event import Event +from events.models.participant import Participant +from events.models.application import EventApplication +from events.models.ticket import Ticket +from rest_framework import serializers + +class CostsSerializer(serializers.ModelSerializer): + class Meta: + model = Costs + +class EventSerializer(serializers.ModelSerializer): + class Meta: + model = Event + +class ParticipantSerializer(serializers.ModelSerializer): + class Meta: + model = Participant + +class EventApplicationSerializer(serializers.ModelSerializer): + class Meta: + model = EventApplication + +class TicketSerializer(serializers.ModelSerializer): + class Meta: + model = Ticket diff --git a/src/events/urls.py b/src/events/urls.py index b730b2c6..e653d474 100644 --- a/src/events/urls.py +++ b/src/events/urls.py @@ -1,6 +1,15 @@ from django.urls import path, re_path +from rest_framework import routers from events import views +router = routers.SimpleRouter() +router.register(r'^costs', views.api.CostsViewSet, basename="CostsView") +router.register(r'^event', views.api.EventViewSet, basename="EventView") +router.register(r'^eventapplication', views.api.EventApplicationViewSet, basename="EventApplicationView") +router.register(r'^ticket', views.api.TicketViewSet, basename="TicketView") +router.register(r'^participant', views.api.ParticipantViewSet, basename="ParticipantView") + + urlpatterns = [ path('event/', views.EventView.as_view(), @@ -31,3 +40,5 @@ name='events_event_modeladmin_export_participants' ), ] + +urlpatterns += router.urls diff --git a/src/events/views/__init__.py b/src/events/views/__init__.py index 0bbf7678..d8b99b36 100644 --- a/src/events/views/__init__.py +++ b/src/events/views/__init__.py @@ -5,3 +5,4 @@ from .admin_unassign_unpaid import * from .admin_remove_applications import * from .admin_export_participants import * +from .api import * diff --git a/src/events/views/api.py b/src/events/views/api.py new file mode 100644 index 00000000..15e7616f --- /dev/null +++ b/src/events/views/api.py @@ -0,0 +1,34 @@ +from rest_framework import viewsets +from events.serializers.serializers import * +from rest_framework.permissions import AllowAny +from events.models.costs import Costs +from events.models.event import Event +from events.models.participant import Participant +from events.models.application import EventApplication +from events.models.ticket import Ticket + +class CostsViewSet(viewsets.ModelViewSet): + serializer_class = CostsSerializer + permission_classes = [AllowAny] + queryset = Costs.objects.all() + +class EventViewSet(viewsets.ModelViewSet): + serializer_class = EventSerializer + permission_classes = [AllowAny] + queryset = Event.objects.all() + +class ParticipantViewSet(viewsets.ModelViewSet): + serializer_class = ParticipantSerializer + permission_classes = [AllowAny] + queryset = Participant.objects.all() + +class EventApplicationViewSet(viewsets.ModelViewSet): + serializer_class = EventApplicationSerializer + permission_classes = [AllowAny] + queryset = EventApplication.objects.all() + +class TicketViewSet(viewsets.ModelViewSet): + serializer_class = TicketSerializer + permission_classes = [AllowAny] + queryset = Ticket.objects.all() + diff --git a/src/involvement/urls.py b/src/involvement/urls.py index 9435b9e6..3b9f56f4 100644 --- a/src/involvement/urls.py +++ b/src/involvement/urls.py @@ -1,7 +1,6 @@ from django.conf.urls import re_path from rest_framework import routers from involvement import views -# from position_api import PositionViewSet router = routers.SimpleRouter() router.register(r'^position', views.position_api.PositionViewSet, basename="PositionView") From 3db3b56484888699c77bb79d113e2ca38b730902 Mon Sep 17 00:00:00 2001 From: Ankur Date: Wed, 8 Nov 2023 19:00:01 +0100 Subject: [PATCH 03/20] Minor change in URL --- src/events/urls.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/events/urls.py b/src/events/urls.py index e653d474..be806b41 100644 --- a/src/events/urls.py +++ b/src/events/urls.py @@ -3,11 +3,11 @@ from events import views router = routers.SimpleRouter() -router.register(r'^costs', views.api.CostsViewSet, basename="CostsView") -router.register(r'^event', views.api.EventViewSet, basename="EventView") -router.register(r'^eventapplication', views.api.EventApplicationViewSet, basename="EventApplicationView") -router.register(r'^ticket', views.api.TicketViewSet, basename="TicketView") -router.register(r'^participant', views.api.ParticipantViewSet, basename="ParticipantView") +router.register(r'^api/costs', views.api.CostsViewSet, basename="CostsView") +router.register(r'^api/event', views.api.EventViewSet, basename="EventView") +router.register(r'^api/eventapplication', views.api.EventApplicationViewSet, basename="EventApplicationView") +router.register(r'^api/ticket', views.api.TicketViewSet, basename="TicketView") +router.register(r'^api/participant', views.api.ParticipantViewSet, basename="ParticipantView") urlpatterns = [ From a61ab3684046fb8b5bafd715b084b2f58dd4d25e Mon Sep 17 00:00:00 2001 From: Ankur Date: Wed, 8 Nov 2023 19:05:37 +0100 Subject: [PATCH 04/20] Adding fields --- src/events/serializers/serializers.py | 5 +++++ src/involvement/serializers/position_serializer.py | 1 + 2 files changed, 6 insertions(+) diff --git a/src/events/serializers/serializers.py b/src/events/serializers/serializers.py index 4f4aa7c2..eaf926be 100644 --- a/src/events/serializers/serializers.py +++ b/src/events/serializers/serializers.py @@ -8,19 +8,24 @@ class CostsSerializer(serializers.ModelSerializer): class Meta: model = Costs + fields = '__all__' class EventSerializer(serializers.ModelSerializer): class Meta: model = Event + fields = '__all__' class ParticipantSerializer(serializers.ModelSerializer): class Meta: model = Participant + fields = '__all__' class EventApplicationSerializer(serializers.ModelSerializer): class Meta: model = EventApplication + fields = '__all__' class TicketSerializer(serializers.ModelSerializer): class Meta: model = Ticket + fields = '__all__' diff --git a/src/involvement/serializers/position_serializer.py b/src/involvement/serializers/position_serializer.py index 3449a5f2..968cd965 100644 --- a/src/involvement/serializers/position_serializer.py +++ b/src/involvement/serializers/position_serializer.py @@ -4,3 +4,4 @@ class PositionSerializer(serializers.ModelSerializer): class Meta: model = Position + fields = '__all__' From 4645bdedfa52ba0038f4c12172134c462148a225 Mon Sep 17 00:00:00 2001 From: Ankur Date: Wed, 8 Nov 2023 19:47:18 +0100 Subject: [PATCH 05/20] Depth --- .../serializers/position_serializer.py | 6 +++ src/involvement/urls.py | 1 + src/involvement/views/position_api.py | 6 ++- src/moore/settings/dev.py | 40 +++++++++---------- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/involvement/serializers/position_serializer.py b/src/involvement/serializers/position_serializer.py index 968cd965..bc5d9d92 100644 --- a/src/involvement/serializers/position_serializer.py +++ b/src/involvement/serializers/position_serializer.py @@ -5,3 +5,9 @@ class PositionSerializer(serializers.ModelSerializer): class Meta: model = Position fields = '__all__' + +class PositionDepthSerializer(serializers.ModelSerializer): + class Meta: + model = Position + fields = '__all__' + depth = 1 diff --git a/src/involvement/urls.py b/src/involvement/urls.py index 3b9f56f4..b1929946 100644 --- a/src/involvement/urls.py +++ b/src/involvement/urls.py @@ -4,6 +4,7 @@ router = routers.SimpleRouter() router.register(r'^position', views.position_api.PositionViewSet, basename="PositionView") +router.register(r'^position2', views.position_api.Position2ViewSet, basename="Position2View") urlpatterns = [ re_path( diff --git a/src/involvement/views/position_api.py b/src/involvement/views/position_api.py index a98a02d9..c6ced2e1 100644 --- a/src/involvement/views/position_api.py +++ b/src/involvement/views/position_api.py @@ -1,5 +1,5 @@ from rest_framework import viewsets -from involvement.serializers.position_serializer import PositionSerializer +from involvement.serializers.position_serializer import PositionSerializer, PositionDepthSerializer from rest_framework.permissions import AllowAny from involvement.models.position import Position @@ -8,3 +8,7 @@ class PositionViewSet(viewsets.ModelViewSet): permission_classes = [AllowAny] queryset = Position.objects.all() +class Position2ViewSet(viewsets.ModelViewSet): + serializer_class = PositionDepthSerializer + permission_classes = [AllowAny] + queryset = Position.objects.all() diff --git a/src/moore/settings/dev.py b/src/moore/settings/dev.py index d6e2ea99..81c29ed4 100644 --- a/src/moore/settings/dev.py +++ b/src/moore/settings/dev.py @@ -21,31 +21,31 @@ # https://docs.djangoproject.com/en/1.10/ref/settings/#databases -if IS_RUNNING_TEST: - DATABASES = { +# if IS_RUNNING_TEST: +DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } - # Override search backend to not use postgres - WAGTAILSEARCH_BACKENDS = { - 'default': { - 'BACKEND': 'wagtail.search.backends.database', - } - } - -elif 'DOCKER' in os.environ: - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': 'moore', - 'USER': 'moore', - 'HOST': 'moore-db', - 'PASSWORD': 'moore', - 'PORT': 5432, - } - } + # # Override search backend to not use postgres + # WAGTAILSEARCH_BACKENDS = { + # 'default': { + # 'BACKEND': 'wagtail.search.backends.database', + # } + # } + +# elif 'DOCKER' in os.environ: +# DATABASES = { +# 'default': { +# 'ENGINE': 'django.db.backends.postgresql', +# 'NAME': 'moore', +# 'USER': 'moore', +# 'HOST': 'moore-db', +# 'PASSWORD': 'moore', +# 'PORT': 5432, +# } +# } # Base URL to use when referring to full URLs within the Wagtail admin # backend - e.g. in notification emails. Don't include '/admin' or a From 26cc620f125b31999e21817cc9f661886b615ff5 Mon Sep 17 00:00:00 2001 From: Ankur Date: Wed, 22 Nov 2023 17:56:59 +0100 Subject: [PATCH 06/20] Changed to authenticated or readonly --- src/events/views/api.py | 12 ++++++------ src/involvement/views/position_api.py | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/events/views/api.py b/src/events/views/api.py index 15e7616f..6c479f93 100644 --- a/src/events/views/api.py +++ b/src/events/views/api.py @@ -1,6 +1,6 @@ from rest_framework import viewsets from events.serializers.serializers import * -from rest_framework.permissions import AllowAny +from rest_framework.permissions import IsAuthenticatedOrReadOnly from events.models.costs import Costs from events.models.event import Event from events.models.participant import Participant @@ -9,26 +9,26 @@ class CostsViewSet(viewsets.ModelViewSet): serializer_class = CostsSerializer - permission_classes = [AllowAny] + permission_classes = [IsAuthenticatedOrReadOnly] queryset = Costs.objects.all() class EventViewSet(viewsets.ModelViewSet): serializer_class = EventSerializer - permission_classes = [AllowAny] + permission_classes = [IsAuthenticatedOrReadOnly] queryset = Event.objects.all() class ParticipantViewSet(viewsets.ModelViewSet): serializer_class = ParticipantSerializer - permission_classes = [AllowAny] + permission_classes = [IsAuthenticatedOrReadOnly] queryset = Participant.objects.all() class EventApplicationViewSet(viewsets.ModelViewSet): serializer_class = EventApplicationSerializer - permission_classes = [AllowAny] + permission_classes = [IsAuthenticatedOrReadOnly] queryset = EventApplication.objects.all() class TicketViewSet(viewsets.ModelViewSet): serializer_class = TicketSerializer - permission_classes = [AllowAny] + permission_classes = [IsAuthenticatedOrReadOnly] queryset = Ticket.objects.all() diff --git a/src/involvement/views/position_api.py b/src/involvement/views/position_api.py index c6ced2e1..b1d35a0c 100644 --- a/src/involvement/views/position_api.py +++ b/src/involvement/views/position_api.py @@ -1,14 +1,14 @@ from rest_framework import viewsets from involvement.serializers.position_serializer import PositionSerializer, PositionDepthSerializer -from rest_framework.permissions import AllowAny +from rest_framework.permissions import IsAuthenticatedOrReadOnly from involvement.models.position import Position class PositionViewSet(viewsets.ModelViewSet): serializer_class = PositionSerializer - permission_classes = [AllowAny] + permission_classes = [IsAuthenticatedOrReadOnly] queryset = Position.objects.all() class Position2ViewSet(viewsets.ModelViewSet): serializer_class = PositionDepthSerializer - permission_classes = [AllowAny] + permission_classes = [IsAuthenticatedOrReadOnly] queryset = Position.objects.all() From a82dbf1df4af4e1838d848df848992ce26448225 Mon Sep 17 00:00:00 2001 From: Ankur Date: Thu, 14 Dec 2023 16:25:05 +0100 Subject: [PATCH 07/20] Permissions 1 --- src/customPermissions.py | 16 ++++++++++++++++ src/events/views/api.py | 10 +++++----- src/involvement/views/position_api.py | 8 ++++---- 3 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 src/customPermissions.py diff --git a/src/customPermissions.py b/src/customPermissions.py new file mode 100644 index 00000000..6fe57201 --- /dev/null +++ b/src/customPermissions.py @@ -0,0 +1,16 @@ +from rest_framework.permissions import BasePermission + +class ReadAndCreate(BasePermission): + """ + Authenticated user can create but not delete or update. + """ + def has_permission(self, request, view): + return True if request.method in ["GET", "HEAD", "OPTIONS", "POST"] else False + +class ReadCreateUpdate(BasePermission): + """ + Authenticated user can create and update but not delete. + """ + def has_permission(self, request, view): + return True if request.method not in ["DELETE"] else False + diff --git a/src/events/views/api.py b/src/events/views/api.py index 6c479f93..720e41f9 100644 --- a/src/events/views/api.py +++ b/src/events/views/api.py @@ -7,27 +7,27 @@ from events.models.application import EventApplication from events.models.ticket import Ticket -class CostsViewSet(viewsets.ModelViewSet): +class CostsViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = CostsSerializer permission_classes = [IsAuthenticatedOrReadOnly] queryset = Costs.objects.all() -class EventViewSet(viewsets.ModelViewSet): +class EventViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = EventSerializer permission_classes = [IsAuthenticatedOrReadOnly] queryset = Event.objects.all() -class ParticipantViewSet(viewsets.ModelViewSet): +class ParticipantViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = ParticipantSerializer permission_classes = [IsAuthenticatedOrReadOnly] queryset = Participant.objects.all() -class EventApplicationViewSet(viewsets.ModelViewSet): +class EventApplicationViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = EventApplicationSerializer permission_classes = [IsAuthenticatedOrReadOnly] queryset = EventApplication.objects.all() -class TicketViewSet(viewsets.ModelViewSet): +class TicketViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = TicketSerializer permission_classes = [IsAuthenticatedOrReadOnly] queryset = Ticket.objects.all() diff --git a/src/involvement/views/position_api.py b/src/involvement/views/position_api.py index b1d35a0c..9d854f65 100644 --- a/src/involvement/views/position_api.py +++ b/src/involvement/views/position_api.py @@ -1,14 +1,14 @@ -from rest_framework import viewsets +from rest_framework import viewsets, mixins from involvement.serializers.position_serializer import PositionSerializer, PositionDepthSerializer -from rest_framework.permissions import IsAuthenticatedOrReadOnly +from rest_framework.permissions import IsAuthenticatedOrReadOnly, from involvement.models.position import Position -class PositionViewSet(viewsets.ModelViewSet): +class PositionViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = PositionSerializer permission_classes = [IsAuthenticatedOrReadOnly] queryset = Position.objects.all() -class Position2ViewSet(viewsets.ModelViewSet): +class Position2ViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = PositionDepthSerializer permission_classes = [IsAuthenticatedOrReadOnly] queryset = Position.objects.all() From 621235345777837957e9571730b0a2b776238e61 Mon Sep 17 00:00:00 2001 From: Ankur Date: Wed, 17 Jan 2024 21:18:10 +0100 Subject: [PATCH 08/20] The Team API is needed as a publicly readable entity because the positions and the team descriptions are open to the public. If a new team is created or edited, it will most likely be through the admin console, meaning that we probably shouldn't have writeable APIs for this. Ludwig I'm writing this in a commit to show you that we need a ticketing system. In most ticketing systems you can set up an automation where referencing the ticket number will associate your commit to that ticket. --- src/involvement/serializers/team_serializer.py | 9 +++++++++ src/involvement/urls.py | 3 +++ src/involvement/views/__init__.py | 1 + src/involvement/views/position_api.py | 4 ++-- src/involvement/views/team_read_api.py | 11 +++++++++++ 5 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 src/involvement/serializers/team_serializer.py create mode 100644 src/involvement/views/team_read_api.py diff --git a/src/involvement/serializers/team_serializer.py b/src/involvement/serializers/team_serializer.py new file mode 100644 index 00000000..f92541a7 --- /dev/null +++ b/src/involvement/serializers/team_serializer.py @@ -0,0 +1,9 @@ +from involvement.models.team import Team +from rest_framework import serializers + +#Serializer for team +class TeamSerializer(serializers.ModelSerializer): + class Meta: + model = Team + fields = '__all__' + diff --git a/src/involvement/urls.py b/src/involvement/urls.py index b1929946..d9f01aae 100644 --- a/src/involvement/urls.py +++ b/src/involvement/urls.py @@ -2,9 +2,12 @@ from rest_framework import routers from involvement import views +#API URLs router = routers.SimpleRouter() router.register(r'^position', views.position_api.PositionViewSet, basename="PositionView") router.register(r'^position2', views.position_api.Position2ViewSet, basename="Position2View") +router.register(r'^teams', views.team_read_api.TeamViewSet, basename="Position2View") + urlpatterns = [ re_path( diff --git a/src/involvement/views/__init__.py b/src/involvement/views/__init__.py index cad3281c..3f33c5f2 100644 --- a/src/involvement/views/__init__.py +++ b/src/involvement/views/__init__.py @@ -16,3 +16,4 @@ from .application_edit_view import ApplicationEditView from .application_inspect_view import ApplicationInspectView from .role_inspect_view import RoleInspectView +from .team_read_api import TeamViewSet diff --git a/src/involvement/views/position_api.py b/src/involvement/views/position_api.py index 9d854f65..926e0870 100644 --- a/src/involvement/views/position_api.py +++ b/src/involvement/views/position_api.py @@ -1,6 +1,6 @@ -from rest_framework import viewsets, mixins +from rest_framework import viewsets from involvement.serializers.position_serializer import PositionSerializer, PositionDepthSerializer -from rest_framework.permissions import IsAuthenticatedOrReadOnly, +from rest_framework.permissions import IsAuthenticatedOrReadOnly from involvement.models.position import Position class PositionViewSet(viewsets.ReadOnlyModelViewSet): diff --git a/src/involvement/views/team_read_api.py b/src/involvement/views/team_read_api.py new file mode 100644 index 00000000..a4fb5f66 --- /dev/null +++ b/src/involvement/views/team_read_api.py @@ -0,0 +1,11 @@ +from rest_framework import viewsets +from involvement.serializers.team_serializer import TeamSerializer +from rest_framework.permissions import AllowAny +from involvement.models.team import Team + +#Read Teams API +class TeamViewSet(viewsets.ReadOnlyModelViewSet): + serializer_class = TeamSerializer + permission_classes = [AllowAny] + queryset = Team.objects.all() + From 182194b0e618449823667d5162b99ada517acc51 Mon Sep 17 00:00:00 2001 From: Ankur Date: Wed, 17 Jan 2024 22:47:14 +0100 Subject: [PATCH 09/20] Adding Role ReadOnly --- src/involvement/serializers/role_serializer.py | 8 ++++++++ src/involvement/views/__init__.py | 1 + src/involvement/views/role_read_api.py | 10 ++++++++++ 3 files changed, 19 insertions(+) create mode 100644 src/involvement/serializers/role_serializer.py create mode 100644 src/involvement/views/role_read_api.py diff --git a/src/involvement/serializers/role_serializer.py b/src/involvement/serializers/role_serializer.py new file mode 100644 index 00000000..1c4b3676 --- /dev/null +++ b/src/involvement/serializers/role_serializer.py @@ -0,0 +1,8 @@ +from involvement.models.role import Role +from rest_framework import serializers + +#Role serializer +class RoleSerializer(serializers.ModelSerializer): + class Meta: + model = Role + fields = '__all__' diff --git a/src/involvement/views/__init__.py b/src/involvement/views/__init__.py index 3f33c5f2..131892ed 100644 --- a/src/involvement/views/__init__.py +++ b/src/involvement/views/__init__.py @@ -17,3 +17,4 @@ from .application_inspect_view import ApplicationInspectView from .role_inspect_view import RoleInspectView from .team_read_api import TeamViewSet +from .role_read_api import RoleViewSet diff --git a/src/involvement/views/role_read_api.py b/src/involvement/views/role_read_api.py new file mode 100644 index 00000000..c13b3766 --- /dev/null +++ b/src/involvement/views/role_read_api.py @@ -0,0 +1,10 @@ +from rest_framework import viewsets +from involvement.serializers.role_serializer import RoleSerializer +from rest_framework.permissions import AllowAny +from involvement.models.role import Role + +#Role view +class RoleViewSet(viewsets.ReadOnlyModelViewSet): + serializer_class = RoleSerializer + permission_classes = [AllowAny] + queryset = Role.objects.all() From e83e50154275dec95b34b2392d3e9321b2b10448 Mon Sep 17 00:00:00 2001 From: Ankur Date: Wed, 17 Jan 2024 22:48:37 +0100 Subject: [PATCH 10/20] Minor change --- src/involvement/urls.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/involvement/urls.py b/src/involvement/urls.py index d9f01aae..5b2deffc 100644 --- a/src/involvement/urls.py +++ b/src/involvement/urls.py @@ -6,7 +6,8 @@ router = routers.SimpleRouter() router.register(r'^position', views.position_api.PositionViewSet, basename="PositionView") router.register(r'^position2', views.position_api.Position2ViewSet, basename="Position2View") -router.register(r'^teams', views.team_read_api.TeamViewSet, basename="Position2View") +router.register(r'^teams', views.team_read_api.TeamViewSet, basename="TeamsView") +router.register(r'^roles', views.role_read_api.RoleViewSet, basename="RolesView") urlpatterns = [ From f3709816c91e5d510178b6f0ed284d6bfc157302 Mon Sep 17 00:00:00 2001 From: Ankur Date: Fri, 19 Jan 2024 13:25:44 +0100 Subject: [PATCH 11/20] Read APIs for Applications --- .../serializers/application_serializer.py | 10 ++++++++++ src/involvement/urls.py | 2 +- src/involvement/views/__init__.py | 1 + src/involvement/views/application_api.py | 14 ++++++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/involvement/serializers/application_serializer.py create mode 100644 src/involvement/views/application_api.py diff --git a/src/involvement/serializers/application_serializer.py b/src/involvement/serializers/application_serializer.py new file mode 100644 index 00000000..3eac488c --- /dev/null +++ b/src/involvement/serializers/application_serializer.py @@ -0,0 +1,10 @@ +from involvement.models.application import Application +from rest_framework import serializers + +#Role serializer +class ApplicationSerializer(serializers.ModelSerializer): + class Meta: + model = Application + fields = '__all__' + depth = 1 + diff --git a/src/involvement/urls.py b/src/involvement/urls.py index 5b2deffc..b4cd2a19 100644 --- a/src/involvement/urls.py +++ b/src/involvement/urls.py @@ -8,7 +8,7 @@ router.register(r'^position2', views.position_api.Position2ViewSet, basename="Position2View") router.register(r'^teams', views.team_read_api.TeamViewSet, basename="TeamsView") router.register(r'^roles', views.role_read_api.RoleViewSet, basename="RolesView") - +router.register(r'^application', views.application_api.ApplicationViewSet, basename="ApplicationView") urlpatterns = [ re_path( diff --git a/src/involvement/views/__init__.py b/src/involvement/views/__init__.py index 131892ed..bce2a547 100644 --- a/src/involvement/views/__init__.py +++ b/src/involvement/views/__init__.py @@ -18,3 +18,4 @@ from .role_inspect_view import RoleInspectView from .team_read_api import TeamViewSet from .role_read_api import RoleViewSet +from .application_api import ApplicationViewSet diff --git a/src/involvement/views/application_api.py b/src/involvement/views/application_api.py new file mode 100644 index 00000000..90fd2373 --- /dev/null +++ b/src/involvement/views/application_api.py @@ -0,0 +1,14 @@ +from rest_framework import viewsets +from involvement.serializers.application_serializer import ApplicationSerializer +from rest_framework.permissions import IsAuthenticated +from involvement.models.application import Application + +#Role view +class ApplicationViewSet(viewsets.ReadOnlyModelViewSet): + serializer_class = ApplicationSerializer + permission_classes = [IsAuthenticated] + + def get_queryset(self): + user = self.request.user + queryset = Application.objects.filter(applicant=user) + return queryset From d8b8234a3288c20b3e67569d8407e7cbb90420af Mon Sep 17 00:00:00 2001 From: Ankur Date: Fri, 19 Jan 2024 14:28:21 +0100 Subject: [PATCH 12/20] Creation, Deletion and Update of Application implemented, it only works on own application --- src/{ => involvement}/customPermissions.py | 6 ++++++ src/involvement/views/application_api.py | 9 ++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) rename src/{ => involvement}/customPermissions.py (69%) diff --git a/src/customPermissions.py b/src/involvement/customPermissions.py similarity index 69% rename from src/customPermissions.py rename to src/involvement/customPermissions.py index 6fe57201..3d201426 100644 --- a/src/customPermissions.py +++ b/src/involvement/customPermissions.py @@ -14,3 +14,9 @@ class ReadCreateUpdate(BasePermission): def has_permission(self, request, view): return True if request.method not in ["DELETE"] else False +class OwnApplicationPermission(BasePermission): + """ + Object-level permission to only allow updating his own profile + """ + def has_object_permission(self, request, view, obj): + return obj.applicant == request.user diff --git a/src/involvement/views/application_api.py b/src/involvement/views/application_api.py index 90fd2373..b2332d2b 100644 --- a/src/involvement/views/application_api.py +++ b/src/involvement/views/application_api.py @@ -1,14 +1,17 @@ -from rest_framework import viewsets +from rest_framework import viewsets, mixins from involvement.serializers.application_serializer import ApplicationSerializer from rest_framework.permissions import IsAuthenticated from involvement.models.application import Application +from involvement.customPermissions import OwnApplicationPermission #Role view -class ApplicationViewSet(viewsets.ReadOnlyModelViewSet): +class ApplicationViewSet(viewsets.ModelViewSet): serializer_class = ApplicationSerializer - permission_classes = [IsAuthenticated] + permission_classes = [IsAuthenticated, OwnApplicationPermission] def get_queryset(self): user = self.request.user queryset = Application.objects.filter(applicant=user) return queryset + + From df3d09a2e5bccf75d57697e4be7899a97f8e635d Mon Sep 17 00:00:00 2001 From: Ankur Date: Sun, 21 Jan 2024 08:26:20 +0100 Subject: [PATCH 13/20] API for event application for own events --- src/events/customPermissions.py | 8 ++++++++ src/events/serializers/serializers.py | 1 + src/events/views/api.py | 11 ++++++++--- 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 src/events/customPermissions.py diff --git a/src/events/customPermissions.py b/src/events/customPermissions.py new file mode 100644 index 00000000..20296152 --- /dev/null +++ b/src/events/customPermissions.py @@ -0,0 +1,8 @@ +from rest_framework.permissions import BasePermission + +class OwnApplicationPermission(BasePermission): + """ + Object-level permission to only allow updating his own profile + """ + def has_object_permission(self, request, view, obj): + return obj.event_applicant == request.user diff --git a/src/events/serializers/serializers.py b/src/events/serializers/serializers.py index eaf926be..b4a23129 100644 --- a/src/events/serializers/serializers.py +++ b/src/events/serializers/serializers.py @@ -24,6 +24,7 @@ class EventApplicationSerializer(serializers.ModelSerializer): class Meta: model = EventApplication fields = '__all__' + depth = 1 class TicketSerializer(serializers.ModelSerializer): class Meta: diff --git a/src/events/views/api.py b/src/events/views/api.py index 720e41f9..8e549e45 100644 --- a/src/events/views/api.py +++ b/src/events/views/api.py @@ -6,6 +6,7 @@ from events.models.participant import Participant from events.models.application import EventApplication from events.models.ticket import Ticket +from events.customPermissions import OwnApplicationPermission class CostsViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = CostsSerializer @@ -22,10 +23,14 @@ class ParticipantViewSet(viewsets.ReadOnlyModelViewSet): permission_classes = [IsAuthenticatedOrReadOnly] queryset = Participant.objects.all() -class EventApplicationViewSet(viewsets.ReadOnlyModelViewSet): +class EventApplicationViewSet(viewsets.ModelViewSet): serializer_class = EventApplicationSerializer - permission_classes = [IsAuthenticatedOrReadOnly] - queryset = EventApplication.objects.all() + permission_classes = [IsAuthenticated, OwnApplicationPermission] + + def get_queryset(self): + user = self.request.user + queryset = EventApplication.objects.filter(event_applicant=user) + return queryset class TicketViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = TicketSerializer From 3c1a7e6f7fb58e4b29f441928168bfc917b2948d Mon Sep 17 00:00:00 2001 From: Ankur Date: Wed, 24 Jan 2024 23:25:18 +0100 Subject: [PATCH 14/20] Tickets done --- src/events/views/api.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/events/views/api.py b/src/events/views/api.py index 8e549e45..cbdc7fd6 100644 --- a/src/events/views/api.py +++ b/src/events/views/api.py @@ -1,6 +1,6 @@ from rest_framework import viewsets from events.serializers.serializers import * -from rest_framework.permissions import IsAuthenticatedOrReadOnly +from rest_framework.permissions import IsAuthenticatedOrReadOnly, AllowAny, IsAuthenticated from events.models.costs import Costs from events.models.event import Event from events.models.participant import Participant @@ -10,7 +10,7 @@ class CostsViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = CostsSerializer - permission_classes = [IsAuthenticatedOrReadOnly] + permission_classes = [AllowAny] queryset = Costs.objects.all() class EventViewSet(viewsets.ReadOnlyModelViewSet): @@ -34,6 +34,10 @@ def get_queryset(self): class TicketViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = TicketSerializer - permission_classes = [IsAuthenticatedOrReadOnly] - queryset = Ticket.objects.all() + permission_classes = [IsAuthenticated] + + def get_queryset(self): + user = self.request.user + queryset = EventApplication.objects.filter(owner=user) + return queryset From ce1de7d3833ff616d30a9a5a5d5a8ee6e2c07da7 Mon Sep 17 00:00:00 2001 From: Ankur Date: Sat, 27 Jan 2024 20:43:34 +0100 Subject: [PATCH 15/20] Small commit to extend events API --- src/events/serializers/serializers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/events/serializers/serializers.py b/src/events/serializers/serializers.py index b4a23129..450585b7 100644 --- a/src/events/serializers/serializers.py +++ b/src/events/serializers/serializers.py @@ -14,6 +14,7 @@ class EventSerializer(serializers.ModelSerializer): class Meta: model = Event fields = '__all__' + depth = 1 class ParticipantSerializer(serializers.ModelSerializer): class Meta: From 1f116ea9c9e1edae667a1204619ff0aee318781a Mon Sep 17 00:00:00 2001 From: Ankur Date: Sun, 28 Jan 2024 20:08:01 +0100 Subject: [PATCH 16/20] CSRF and Me API --- src/customPermissions.py | 7 +++++++ src/events/views/api.py | 11 ++++++----- src/involvement/views/application_api.py | 3 ++- src/involvement/views/position_api.py | 5 +++-- src/involvement/views/role_read_api.py | 4 ++-- src/involvement/views/team_read_api.py | 4 ++-- src/members/serializers/serializers.py | 8 ++++++++ src/members/urls.py | 6 ++++++ src/members/views.py | 17 ++++++++++++++--- 9 files changed, 50 insertions(+), 15 deletions(-) create mode 100644 src/customPermissions.py create mode 100644 src/members/serializers/serializers.py diff --git a/src/customPermissions.py b/src/customPermissions.py new file mode 100644 index 00000000..2f2b2d60 --- /dev/null +++ b/src/customPermissions.py @@ -0,0 +1,7 @@ +from rest_framework.authentication import SessionAuthentication, BasicAuthentication + +class CsrfExemptSessionAuthentication(SessionAuthentication): + + def enforce_csrf(self, request): + return + diff --git a/src/events/views/api.py b/src/events/views/api.py index cbdc7fd6..aa81f300 100644 --- a/src/events/views/api.py +++ b/src/events/views/api.py @@ -7,25 +7,26 @@ from events.models.application import EventApplication from events.models.ticket import Ticket from events.customPermissions import OwnApplicationPermission +from src.customPermissions import CsrfExemptSessionAuthentication class CostsViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = CostsSerializer - permission_classes = [AllowAny] + permission_classes = [AllowAny, CsrfExemptSessionAuthentication] queryset = Costs.objects.all() class EventViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = EventSerializer - permission_classes = [IsAuthenticatedOrReadOnly] + permission_classes = [IsAuthenticatedOrReadOnly, CsrfExemptSessionAuthentication] queryset = Event.objects.all() class ParticipantViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = ParticipantSerializer - permission_classes = [IsAuthenticatedOrReadOnly] + permission_classes = [IsAuthenticatedOrReadOnly, CsrfExemptSessionAuthentication] queryset = Participant.objects.all() class EventApplicationViewSet(viewsets.ModelViewSet): serializer_class = EventApplicationSerializer - permission_classes = [IsAuthenticated, OwnApplicationPermission] + permission_classes = [IsAuthenticated, OwnApplicationPermission, CsrfExemptSessionAuthentication] def get_queryset(self): user = self.request.user @@ -34,7 +35,7 @@ def get_queryset(self): class TicketViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = TicketSerializer - permission_classes = [IsAuthenticated] + permission_classes = [IsAuthenticated, CsrfExemptSessionAuthentication] def get_queryset(self): user = self.request.user diff --git a/src/involvement/views/application_api.py b/src/involvement/views/application_api.py index b2332d2b..12e614b7 100644 --- a/src/involvement/views/application_api.py +++ b/src/involvement/views/application_api.py @@ -3,11 +3,12 @@ from rest_framework.permissions import IsAuthenticated from involvement.models.application import Application from involvement.customPermissions import OwnApplicationPermission +from src.customPermissions import CsrfExemptSessionAuthentication #Role view class ApplicationViewSet(viewsets.ModelViewSet): serializer_class = ApplicationSerializer - permission_classes = [IsAuthenticated, OwnApplicationPermission] + permission_classes = [IsAuthenticated, OwnApplicationPermission, CsrfExemptSessionAuthentication] def get_queryset(self): user = self.request.user diff --git a/src/involvement/views/position_api.py b/src/involvement/views/position_api.py index 926e0870..d82cdf10 100644 --- a/src/involvement/views/position_api.py +++ b/src/involvement/views/position_api.py @@ -2,13 +2,14 @@ from involvement.serializers.position_serializer import PositionSerializer, PositionDepthSerializer from rest_framework.permissions import IsAuthenticatedOrReadOnly from involvement.models.position import Position +from src.customPermissions import CsrfExemptSessionAuthentication class PositionViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = PositionSerializer - permission_classes = [IsAuthenticatedOrReadOnly] + permission_classes = [IsAuthenticatedOrReadOnly, CsrfExemptSessionAuthentication] queryset = Position.objects.all() class Position2ViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = PositionDepthSerializer - permission_classes = [IsAuthenticatedOrReadOnly] + permission_classes = [IsAuthenticatedOrReadOnly, CsrfExemptSessionAuthentication] queryset = Position.objects.all() diff --git a/src/involvement/views/role_read_api.py b/src/involvement/views/role_read_api.py index c13b3766..b2e0a334 100644 --- a/src/involvement/views/role_read_api.py +++ b/src/involvement/views/role_read_api.py @@ -2,9 +2,9 @@ from involvement.serializers.role_serializer import RoleSerializer from rest_framework.permissions import AllowAny from involvement.models.role import Role - +from src.customPermissions import CsrfExemptSessionAuthentication #Role view class RoleViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = RoleSerializer - permission_classes = [AllowAny] + permission_classes = [AllowAny, CsrfExemptSessionAuthentication] queryset = Role.objects.all() diff --git a/src/involvement/views/team_read_api.py b/src/involvement/views/team_read_api.py index a4fb5f66..fc259663 100644 --- a/src/involvement/views/team_read_api.py +++ b/src/involvement/views/team_read_api.py @@ -2,10 +2,10 @@ from involvement.serializers.team_serializer import TeamSerializer from rest_framework.permissions import AllowAny from involvement.models.team import Team - +from src.customPermissions import CsrfExemptSessionAuthentication #Read Teams API class TeamViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = TeamSerializer - permission_classes = [AllowAny] + permission_classes = [AllowAny, CsrfExemptSessionAuthentication] queryset = Team.objects.all() diff --git a/src/members/serializers/serializers.py b/src/members/serializers/serializers.py new file mode 100644 index 00000000..baa2ff97 --- /dev/null +++ b/src/members/serializers/serializers.py @@ -0,0 +1,8 @@ +from members.models.member import Member +from rest_framework import serializers + +class MemberSerializer(serializers.ModelSerializer): + class Meta: + model = Member + fields = '__all__' + depth = 1 diff --git a/src/members/urls.py b/src/members/urls.py index 3f266a08..2ce7f98d 100644 --- a/src/members/urls.py +++ b/src/members/urls.py @@ -1,10 +1,14 @@ from django.conf.urls import re_path, include from django.urls import reverse_lazy from django.views.generic import CreateView +from rest_framework import routers from members import views from members.forms import RegistrationForm +router = routers.SimpleRouter() +router.register(r'^me', views.MemberViewSet, basename="MemberViewSet") + urlpatterns = [ re_path(r'^profile/$', views.ProfileView.as_view(), name='profile'), re_path( @@ -25,3 +29,5 @@ views.CustomPasswordResetView.as_view(), name='password_reset_custom'), ] + +urlpatterns += router.urls diff --git a/src/members/views.py b/src/members/views.py index 49a6471c..bce00627 100644 --- a/src/members/views.py +++ b/src/members/views.py @@ -11,13 +11,15 @@ from django.views.decorators.csrf import csrf_protect from django.utils.decorators import method_decorator from members.forms import MemberForm, CustomPasswordResetForm -from members.models import Section, StudyProgram +from members.models import Section, StudyProgram, Member from rest_framework.decorators import api_view from rest_framework.response import Response from members.serializers import MemberCheckSerializer from utils.melos_client import MelosClient -from rest_framework import status - +from rest_framework import status, viewsets +from serializers.serializers import MemberSerializer +from rest_framework.permissions import IsAuthenticated +from src.customPermissions import CsrfExemptSessionAuthentication class ProfileView(LoginRequiredMixin, UpdateView): template_name = 'members/profile.html' @@ -134,3 +136,12 @@ def member_check_api(request): status=status_code, headers={"Access-Control-Allow-Origin": "*"} ) + +class MemberViewSet(viewsets.ReadOnlyModelViewSet): + serializer_class = MemberSerializer + permission_classes = [IsAuthenticated, CsrfExemptSessionAuthentication] + + def get_queryset(self): + user = self.request.user.id + queryset = Member.objects.filter(id=user) + return queryset From 1dc7b6f75864c0f73a5cf792fca9ef88c590aa08 Mon Sep 17 00:00:00 2001 From: Ankur Date: Sun, 28 Jan 2024 20:26:18 +0100 Subject: [PATCH 17/20] CSRF fixed --- src/customPermissions.py | 2 +- src/events/customPermissions.py | 5 +++++ src/events/views/api.py | 20 ++++++++++++-------- src/involvement/customPermissions.py | 4 ++++ src/involvement/views/application_api.py | 7 ++++--- src/involvement/views/position_api.py | 10 ++++++---- src/involvement/views/role_read_api.py | 7 ++++--- src/involvement/views/team_read_api.py | 7 ++++--- src/members/customPermissions.py | 5 +++++ src/members/serializers.py | 6 ++++++ src/members/serializers/serializers.py | 8 -------- src/members/views.py | 9 +++++---- 12 files changed, 56 insertions(+), 34 deletions(-) create mode 100644 src/members/customPermissions.py delete mode 100644 src/members/serializers/serializers.py diff --git a/src/customPermissions.py b/src/customPermissions.py index 2f2b2d60..4ef8a001 100644 --- a/src/customPermissions.py +++ b/src/customPermissions.py @@ -1,4 +1,4 @@ -from rest_framework.authentication import SessionAuthentication, BasicAuthentication +from rest_framework.authentication import SessionAuthentication class CsrfExemptSessionAuthentication(SessionAuthentication): diff --git a/src/events/customPermissions.py b/src/events/customPermissions.py index 20296152..52b41406 100644 --- a/src/events/customPermissions.py +++ b/src/events/customPermissions.py @@ -1,4 +1,9 @@ from rest_framework.permissions import BasePermission +from rest_framework.authentication import SessionAuthentication +class CsrfExemptSessionAuthentication(SessionAuthentication): + + def enforce_csrf(self, request): + return class OwnApplicationPermission(BasePermission): """ diff --git a/src/events/views/api.py b/src/events/views/api.py index aa81f300..40df14f0 100644 --- a/src/events/views/api.py +++ b/src/events/views/api.py @@ -1,4 +1,4 @@ -from rest_framework import viewsets +from rest_framework import viewsets, authentication from events.serializers.serializers import * from rest_framework.permissions import IsAuthenticatedOrReadOnly, AllowAny, IsAuthenticated from events.models.costs import Costs @@ -6,27 +6,30 @@ from events.models.participant import Participant from events.models.application import EventApplication from events.models.ticket import Ticket -from events.customPermissions import OwnApplicationPermission -from src.customPermissions import CsrfExemptSessionAuthentication +from events.customPermissions import OwnApplicationPermission, CsrfExemptSessionAuthentication class CostsViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = CostsSerializer - permission_classes = [AllowAny, CsrfExemptSessionAuthentication] + authentication_classes = (CsrfExemptSessionAuthentication, authentication.BasicAuthentication) + permission_classes = [AllowAny] queryset = Costs.objects.all() class EventViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = EventSerializer - permission_classes = [IsAuthenticatedOrReadOnly, CsrfExemptSessionAuthentication] + authentication_classes = (CsrfExemptSessionAuthentication, authentication.BasicAuthentication) + permission_classes = [IsAuthenticatedOrReadOnly] queryset = Event.objects.all() class ParticipantViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = ParticipantSerializer - permission_classes = [IsAuthenticatedOrReadOnly, CsrfExemptSessionAuthentication] + authentication_classes = (CsrfExemptSessionAuthentication, authentication.BasicAuthentication) + permission_classes = [IsAuthenticatedOrReadOnly] queryset = Participant.objects.all() class EventApplicationViewSet(viewsets.ModelViewSet): serializer_class = EventApplicationSerializer - permission_classes = [IsAuthenticated, OwnApplicationPermission, CsrfExemptSessionAuthentication] + authentication_classes = (CsrfExemptSessionAuthentication, authentication.BasicAuthentication) + permission_classes = [IsAuthenticated, OwnApplicationPermission] def get_queryset(self): user = self.request.user @@ -35,7 +38,8 @@ def get_queryset(self): class TicketViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = TicketSerializer - permission_classes = [IsAuthenticated, CsrfExemptSessionAuthentication] + authentication_classes = (CsrfExemptSessionAuthentication, authentication.BasicAuthentication) + permission_classes = [IsAuthenticated] def get_queryset(self): user = self.request.user diff --git a/src/involvement/customPermissions.py b/src/involvement/customPermissions.py index 3d201426..12237845 100644 --- a/src/involvement/customPermissions.py +++ b/src/involvement/customPermissions.py @@ -1,5 +1,9 @@ from rest_framework.permissions import BasePermission +from rest_framework.authentication import SessionAuthentication +class CsrfExemptSessionAuthentication(SessionAuthentication): + def enforce_csrf(self, request): + return class ReadAndCreate(BasePermission): """ Authenticated user can create but not delete or update. diff --git a/src/involvement/views/application_api.py b/src/involvement/views/application_api.py index 12e614b7..18fa2868 100644 --- a/src/involvement/views/application_api.py +++ b/src/involvement/views/application_api.py @@ -1,14 +1,15 @@ -from rest_framework import viewsets, mixins +from rest_framework import viewsets, mixins, authentication from involvement.serializers.application_serializer import ApplicationSerializer from rest_framework.permissions import IsAuthenticated from involvement.models.application import Application from involvement.customPermissions import OwnApplicationPermission -from src.customPermissions import CsrfExemptSessionAuthentication +from involvement.customPermissions import CsrfExemptSessionAuthentication #Role view class ApplicationViewSet(viewsets.ModelViewSet): serializer_class = ApplicationSerializer - permission_classes = [IsAuthenticated, OwnApplicationPermission, CsrfExemptSessionAuthentication] + authentication_classes = (CsrfExemptSessionAuthentication, authentication.BasicAuthentication) + permission_classes = [IsAuthenticated, OwnApplicationPermission] def get_queryset(self): user = self.request.user diff --git a/src/involvement/views/position_api.py b/src/involvement/views/position_api.py index d82cdf10..fbbbd953 100644 --- a/src/involvement/views/position_api.py +++ b/src/involvement/views/position_api.py @@ -1,15 +1,17 @@ -from rest_framework import viewsets +from rest_framework import viewsets, authentication from involvement.serializers.position_serializer import PositionSerializer, PositionDepthSerializer from rest_framework.permissions import IsAuthenticatedOrReadOnly from involvement.models.position import Position -from src.customPermissions import CsrfExemptSessionAuthentication +from involvement.customPermissions import CsrfExemptSessionAuthentication class PositionViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = PositionSerializer - permission_classes = [IsAuthenticatedOrReadOnly, CsrfExemptSessionAuthentication] + authentication_classes = (CsrfExemptSessionAuthentication, authentication.BasicAuthentication) + permission_classes = [IsAuthenticatedOrReadOnly] queryset = Position.objects.all() class Position2ViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = PositionDepthSerializer - permission_classes = [IsAuthenticatedOrReadOnly, CsrfExemptSessionAuthentication] + authentication_classes = (CsrfExemptSessionAuthentication, authentication.BasicAuthentication) + permission_classes = [IsAuthenticatedOrReadOnly] queryset = Position.objects.all() diff --git a/src/involvement/views/role_read_api.py b/src/involvement/views/role_read_api.py index b2e0a334..7add581d 100644 --- a/src/involvement/views/role_read_api.py +++ b/src/involvement/views/role_read_api.py @@ -1,10 +1,11 @@ -from rest_framework import viewsets +from rest_framework import viewsets, authentication from involvement.serializers.role_serializer import RoleSerializer from rest_framework.permissions import AllowAny from involvement.models.role import Role -from src.customPermissions import CsrfExemptSessionAuthentication +from involvement.customPermissions import CsrfExemptSessionAuthentication #Role view class RoleViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = RoleSerializer - permission_classes = [AllowAny, CsrfExemptSessionAuthentication] + authentication_classes = (CsrfExemptSessionAuthentication, authentication.BasicAuthentication) + permission_classes = [AllowAny] queryset = Role.objects.all() diff --git a/src/involvement/views/team_read_api.py b/src/involvement/views/team_read_api.py index fc259663..4ac1ab1f 100644 --- a/src/involvement/views/team_read_api.py +++ b/src/involvement/views/team_read_api.py @@ -1,11 +1,12 @@ -from rest_framework import viewsets +from rest_framework import viewsets, authentication from involvement.serializers.team_serializer import TeamSerializer from rest_framework.permissions import AllowAny from involvement.models.team import Team -from src.customPermissions import CsrfExemptSessionAuthentication +from involvement.customPermissions import CsrfExemptSessionAuthentication #Read Teams API class TeamViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = TeamSerializer - permission_classes = [AllowAny, CsrfExemptSessionAuthentication] + authentication_classes = (CsrfExemptSessionAuthentication, authentication.BasicAuthentication) + permission_classes = [AllowAny] queryset = Team.objects.all() diff --git a/src/members/customPermissions.py b/src/members/customPermissions.py new file mode 100644 index 00000000..eb9892a4 --- /dev/null +++ b/src/members/customPermissions.py @@ -0,0 +1,5 @@ +from rest_framework.authentication import SessionAuthentication +class CsrfExemptSessionAuthentication(SessionAuthentication): + + def enforce_csrf(self, request): + return diff --git a/src/members/serializers.py b/src/members/serializers.py index 4a05fb44..19b514a7 100644 --- a/src/members/serializers.py +++ b/src/members/serializers.py @@ -1,6 +1,12 @@ from rest_framework import serializers from utils.validators import SSNValidator +from members.models.member import Member +class MemberSerializer(serializers.ModelSerializer): + class Meta: + model = Member + fields = '__all__' + depth = 1 class MemberCheckSerializer(serializers.Serializer): ssn = serializers.CharField() diff --git a/src/members/serializers/serializers.py b/src/members/serializers/serializers.py deleted file mode 100644 index baa2ff97..00000000 --- a/src/members/serializers/serializers.py +++ /dev/null @@ -1,8 +0,0 @@ -from members.models.member import Member -from rest_framework import serializers - -class MemberSerializer(serializers.ModelSerializer): - class Meta: - model = Member - fields = '__all__' - depth = 1 diff --git a/src/members/views.py b/src/members/views.py index bce00627..913eb01f 100644 --- a/src/members/views.py +++ b/src/members/views.py @@ -16,10 +16,10 @@ from rest_framework.response import Response from members.serializers import MemberCheckSerializer from utils.melos_client import MelosClient -from rest_framework import status, viewsets -from serializers.serializers import MemberSerializer +from rest_framework import status, viewsets, authentication +from members.serializers import MemberSerializer from rest_framework.permissions import IsAuthenticated -from src.customPermissions import CsrfExemptSessionAuthentication +from members.customPermissions import CsrfExemptSessionAuthentication class ProfileView(LoginRequiredMixin, UpdateView): template_name = 'members/profile.html' @@ -139,7 +139,8 @@ def member_check_api(request): class MemberViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = MemberSerializer - permission_classes = [IsAuthenticated, CsrfExemptSessionAuthentication] + authentication_classes = (CsrfExemptSessionAuthentication, authentication.BasicAuthentication) + permission_classes = [IsAuthenticated] def get_queryset(self): user = self.request.user.id From 7d12bd1454e788683d30190ab22e284980914266 Mon Sep 17 00:00:00 2001 From: Ankur Date: Sun, 28 Jan 2024 21:49:11 +0100 Subject: [PATCH 18/20] Most APIs --- src/events/customPermissions.py | 8 ++++++ src/events/views/api.py | 16 ++++++++---- src/involvement/customPermissions.py | 16 ++++++++++++ .../serializers/application_serializer.py | 5 ++++ src/involvement/views/application_api.py | 26 ++++++++++++++++--- src/involvement/views/position_api.py | 4 +-- 6 files changed, 65 insertions(+), 10 deletions(-) diff --git a/src/events/customPermissions.py b/src/events/customPermissions.py index 52b41406..e73b0a71 100644 --- a/src/events/customPermissions.py +++ b/src/events/customPermissions.py @@ -11,3 +11,11 @@ class OwnApplicationPermission(BasePermission): """ def has_object_permission(self, request, view, obj): return obj.event_applicant == request.user + +class ParticipantAllowancePermission(BasePermission): + + def has_object_permission(self, request, view, obj): + if request.method not in ["GET", "HEAD", "OPTIONS"]: + return not obj.ticket.locked + else: + return True diff --git a/src/events/views/api.py b/src/events/views/api.py index 40df14f0..c1c51f90 100644 --- a/src/events/views/api.py +++ b/src/events/views/api.py @@ -6,7 +6,8 @@ from events.models.participant import Participant from events.models.application import EventApplication from events.models.ticket import Ticket -from events.customPermissions import OwnApplicationPermission, CsrfExemptSessionAuthentication +from events.customPermissions import OwnApplicationPermission, CsrfExemptSessionAuthentication, \ + ParticipantAllowancePermission class CostsViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = CostsSerializer @@ -20,11 +21,16 @@ class EventViewSet(viewsets.ReadOnlyModelViewSet): permission_classes = [IsAuthenticatedOrReadOnly] queryset = Event.objects.all() -class ParticipantViewSet(viewsets.ReadOnlyModelViewSet): +class ParticipantViewSet(viewsets.ModelViewSet): serializer_class = ParticipantSerializer authentication_classes = (CsrfExemptSessionAuthentication, authentication.BasicAuthentication) - permission_classes = [IsAuthenticatedOrReadOnly] - queryset = Participant.objects.all() + permission_classes = [IsAuthenticatedOrReadOnly, ParticipantAllowancePermission] + + def get_queryset(self): + user = self.request.user + tickets = list(Ticket.objects.filter.values_list(owner=user)) + queryset = Participant.objects.filter(ticket__in=tickets) + return queryset class EventApplicationViewSet(viewsets.ModelViewSet): serializer_class = EventApplicationSerializer @@ -43,6 +49,6 @@ class TicketViewSet(viewsets.ReadOnlyModelViewSet): def get_queryset(self): user = self.request.user - queryset = EventApplication.objects.filter(owner=user) + queryset = Ticket.objects.filter(owner=user) return queryset diff --git a/src/involvement/customPermissions.py b/src/involvement/customPermissions.py index 12237845..6504233f 100644 --- a/src/involvement/customPermissions.py +++ b/src/involvement/customPermissions.py @@ -1,5 +1,6 @@ from rest_framework.permissions import BasePermission from rest_framework.authentication import SessionAuthentication +from datetime import date class CsrfExemptSessionAuthentication(SessionAuthentication): def enforce_csrf(self, request): @@ -24,3 +25,18 @@ class OwnApplicationPermission(BasePermission): """ def has_object_permission(self, request, view, obj): return obj.applicant == request.user + +class DeleteApplicationPermission(BasePermission): + def has_object_permission(self, request, view, obj): + if obj.status in ["Draft", "Submitted"]: + return True + return False + + +class EditApplicationPermission(BasePermission): + def has_object_permission(self, request, view, obj): + if obj.status in ["Draft"]: + if obj.position.recruitment_end > date.today(): + return True + return False + return False diff --git a/src/involvement/serializers/application_serializer.py b/src/involvement/serializers/application_serializer.py index 3eac488c..916186aa 100644 --- a/src/involvement/serializers/application_serializer.py +++ b/src/involvement/serializers/application_serializer.py @@ -8,3 +8,8 @@ class Meta: fields = '__all__' depth = 1 +class ApplicationEditSerializer(serializers.ModelSerializer): + class Meta: + model = Application + exclude = ["status"] + depth = 1 diff --git a/src/involvement/views/application_api.py b/src/involvement/views/application_api.py index 18fa2868..82a02991 100644 --- a/src/involvement/views/application_api.py +++ b/src/involvement/views/application_api.py @@ -1,12 +1,14 @@ from rest_framework import viewsets, mixins, authentication -from involvement.serializers.application_serializer import ApplicationSerializer +from involvement.serializers.application_serializer import ApplicationSerializer, ApplicationEditSerializer from rest_framework.permissions import IsAuthenticated from involvement.models.application import Application -from involvement.customPermissions import OwnApplicationPermission +from involvement.customPermissions import OwnApplicationPermission, DeleteApplicationPermission, \ + EditApplicationPermission from involvement.customPermissions import CsrfExemptSessionAuthentication #Role view -class ApplicationViewSet(viewsets.ModelViewSet): +class ApplicationViewSet(viewsets.GenericViewSet, mixins.CreateModelMixin, mixins.ListModelMixin, \ + mixins.RetrieveModelMixin): serializer_class = ApplicationSerializer authentication_classes = (CsrfExemptSessionAuthentication, authentication.BasicAuthentication) permission_classes = [IsAuthenticated, OwnApplicationPermission] @@ -16,4 +18,22 @@ def get_queryset(self): queryset = Application.objects.filter(applicant=user) return queryset +class ApplicationDeleteViewSet(viewsets.GenericViewSet, mixins.DestroyModelMixin): + serializer_class = ApplicationSerializer + authentication_classes = (CsrfExemptSessionAuthentication, authentication.BasicAuthentication) + permission_classes = [IsAuthenticated, OwnApplicationPermission, DeleteApplicationPermission] + + def get_queryset(self): + user = self.request.user + queryset = Application.objects.filter(applicant=user) + return queryset +class ApplicationEditViewSet(viewsets.GenericViewSet, mixins.UpdateModelMixin): + serializer_class = ApplicationEditSerializer + authentication_classes = (CsrfExemptSessionAuthentication, authentication.BasicAuthentication) + permission_classes = [IsAuthenticated, OwnApplicationPermission, EditApplicationPermission] + + def get_queryset(self): + user = self.request.user + queryset = Application.objects.filter(applicant=user) + return queryset diff --git a/src/involvement/views/position_api.py b/src/involvement/views/position_api.py index fbbbd953..7f27122b 100644 --- a/src/involvement/views/position_api.py +++ b/src/involvement/views/position_api.py @@ -1,6 +1,6 @@ -from rest_framework import viewsets, authentication +from rest_framework import viewsets, authentication, mixins from involvement.serializers.position_serializer import PositionSerializer, PositionDepthSerializer -from rest_framework.permissions import IsAuthenticatedOrReadOnly +from rest_framework.permissions import IsAuthenticatedOrReadOnly, IsAuthenticated from involvement.models.position import Position from involvement.customPermissions import CsrfExemptSessionAuthentication From 2be8a8954c962292e4861b5777f9ba1f6a080c5c Mon Sep 17 00:00:00 2001 From: Ankur Date: Sun, 28 Jan 2024 21:53:13 +0100 Subject: [PATCH 19/20] Added URLs --- src/involvement/urls.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/involvement/urls.py b/src/involvement/urls.py index b4cd2a19..2f64db08 100644 --- a/src/involvement/urls.py +++ b/src/involvement/urls.py @@ -9,6 +9,8 @@ router.register(r'^teams', views.team_read_api.TeamViewSet, basename="TeamsView") router.register(r'^roles', views.role_read_api.RoleViewSet, basename="RolesView") router.register(r'^application', views.application_api.ApplicationViewSet, basename="ApplicationView") +router.register(r'^application-update', views.application_api.ApplicationEditViewSet, basename="ApplicationEditView") +router.register(r'^application-delete', views.application_api.ApplicationDeleteViewSet, basename="ApplicationDeleteView") urlpatterns = [ re_path( From aee9851ad1c7a919a043ce9f6daa8f8b9c16873e Mon Sep 17 00:00:00 2001 From: Ankur Date: Mon, 29 Jan 2024 22:24:02 +0100 Subject: [PATCH 20/20] Final API --- src/events/customPermissions.py | 3 +++ src/events/views/api.py | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/events/customPermissions.py b/src/events/customPermissions.py index e73b0a71..29cf3f62 100644 --- a/src/events/customPermissions.py +++ b/src/events/customPermissions.py @@ -1,5 +1,6 @@ from rest_framework.permissions import BasePermission from rest_framework.authentication import SessionAuthentication +from datetime import datetime class CsrfExemptSessionAuthentication(SessionAuthentication): def enforce_csrf(self, request): @@ -10,6 +11,8 @@ class OwnApplicationPermission(BasePermission): Object-level permission to only allow updating his own profile """ def has_object_permission(self, request, view, obj): + if obj.event.end_of_application < datetime.now(): + return False return obj.event_applicant == request.user class ParticipantAllowancePermission(BasePermission): diff --git a/src/events/views/api.py b/src/events/views/api.py index c1c51f90..2d71e887 100644 --- a/src/events/views/api.py +++ b/src/events/views/api.py @@ -1,4 +1,4 @@ -from rest_framework import viewsets, authentication +from rest_framework import viewsets, authentication, mixins from events.serializers.serializers import * from rest_framework.permissions import IsAuthenticatedOrReadOnly, AllowAny, IsAuthenticated from events.models.costs import Costs @@ -32,14 +32,18 @@ def get_queryset(self): queryset = Participant.objects.filter(ticket__in=tickets) return queryset -class EventApplicationViewSet(viewsets.ModelViewSet): +class EventApplicationViewSet(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.CreateModelMixin, mixins.RetrieveModelMixin, \ + mixins.DestroyModelMixin): serializer_class = EventApplicationSerializer authentication_classes = (CsrfExemptSessionAuthentication, authentication.BasicAuthentication) permission_classes = [IsAuthenticated, OwnApplicationPermission] def get_queryset(self): user = self.request.user - queryset = EventApplication.objects.filter(event_applicant=user) + if self.action in ['retrieve', 'list']: + queryset = EventApplication.objects.filter(event_applicant=user) + else: + queryset = EventApplication.objects.all() return queryset class TicketViewSet(viewsets.ReadOnlyModelViewSet):