Commit 1475f130 authored by Staś Kycia's avatar Staś Kycia
Browse files

Election permission manager

parent 2a8b786f
Loading
Loading
Loading
Loading
+7 −1
Changes for api/views.py: 7 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -125,13 +125,19 @@ class VoteAPIView(APIView):

class ResultsViewPermission(BasePermission):
    def has_permission(self, request, view):
        return request.user and request.user.is_authenticated and request.user.is_active
        if not request.user or not request.user.is_authenticated or not request.user.is_active:
            return False
        return True
        
class ResultsView(APIView):
    permission_classes = [ResultsViewPermission]
    
    def get(self, request, election_id):        
        election = get_object_or_404(Election, pk=election_id)
        
        if not request.user.is_superuser and not request.user == election.owner and not election.contributors.filter(user=request.user, can_view_stats=True).exists():
            raise PermissionDenied
        
        options = list(election.options.values('name', 'votes'))
        
        if election.allow_invalid:
+102 −3
Changes for elections/admin.py: 102 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -2,24 +2,123 @@ from django.contrib import admin
from django.contrib.admin import AdminSite
from django.utils.html import format_html
from django.urls import reverse
from django.db.models import Q

from .models import Election, Option
from .models import Election, Option, ElectionContributor

class OptionInline(admin.TabularInline):
    model = Option
    exclude = ['votes']
    extra = 1

class ElectionContributorInline(admin.TabularInline):
    model = ElectionContributor
    classes = ['collapse']
    extra = 0
    
    # def get_readonly_fields(self, request, obj = None):
    #     if obj is None:
    #         return super().get_readonly_fields(request, obj)
    #     if obj.owner == request.user:
    #         return []
    #     return ['can_manage_permissions']
    
    def get_formset(self, request, obj = None, **kwargs):
        formset = super().get_formset(request, obj, **kwargs)
        old_init = formset.form.__init__
        
        def new_init(form, *args, **kwargs):
            old_init(form, *args, **kwargs)
            
            if obj is None:
                return
            
            if form.instance.user_id == obj.owner.id or request.user.is_superuser:
                return
            elif form.instance.user_id == request.user.id:
                for field_name in form.fields:
                    form.fields[field_name].disabled = True
            else:
                form.fields["can_manage_permissions"].disabled = True
                    
        formset.form.__init__ = new_init
        return formset
    
    def has_add_permission(self, request, obj=None):
        if obj is None:
            return super().has_add_permission(request, obj)
        if obj.owner == request.user:
            return True
        return ElectionContributor.objects.filter(election=obj, user=request.user, can_manage_permissions=True).exists()
        
    def has_change_permission(self, request, obj=None):
        if obj is None:
            return super().has_change_permission(request, obj)
        if obj.owner == request.user:
            return True
        return ElectionContributor.objects.filter(election=obj, user_id=request.user.id, can_manage_permissions=True).exists()
    
    def has_delete_permission(self, request, obj = None):
        if obj is None:
            return super().has_delete_permission(request, obj)
        if obj.owner == request.user:
            return True
        return ElectionContributor.objects.filter(election=obj, user_id=request.user.id, can_manage_permissions=True).exists()
    
    def has_view_permission(self, request, obj = None):
        return True
            
    
class ElectionAdmin(admin.ModelAdmin):
    list_display = ['name', 'start_date', 'end_date', 'is_active_status']
    ordering = ['-created_at', '-updated_at']

    exclude = ['invalid_votes', 'local_votes_entered']
    inlines = (OptionInline, )
    exclude = ['invalid_votes', 'local_votes_entered', 'owner']
    inlines = (OptionInline, ElectionContributorInline)



    @admin.display(description="Aktywne", boolean=True)
    def is_active_status(self, obj):
        return obj.is_active
    
    def save_model(self, request, obj, form, change):
        if not change:
            obj.owner = request.user
        super().save_model(request, obj, form, change)
            
    def get_queryset(self, request):
        queryset = super().get_queryset(request)
        if request.user.is_superuser:
            return queryset
        return queryset.filter(Q(owner=request.user) | Q(contributors__user_id=request.user.id))
    
    def has_add_permission(self, request):
        return super().has_add_permission(request)
    
    def has_change_permission(self, request, obj = None):
        if obj is None:
            return True
        contributor = ElectionContributor.objects.filter(election=obj, user_id=request.user.id).first()
        if contributor is not None and contributor.can_edit:
            return True
        return request.user.is_superuser or request.user == obj.owner
    
    def has_delete_permission(self, request, obj = None):
        if obj is None:
            return True
        contributor = ElectionContributor.objects.filter(election=obj, user_id=request.user.id).first()
        if contributor is not None and contributor.can_delete:
            return True
        return request.user.is_superuser or request.user == obj.owner
    
    def has_view_permission(self, request, obj = None):
        if obj is None:
            return True
        contributor = ElectionContributor.objects.filter(election=obj, user_id=request.user.id).first()
        if contributor is not None and contributor.can_view:
            return True
        return request.user.is_superuser or request.user == obj.owner
   
# Register your models here.
admin.site.register(Election, ElectionAdmin)
 No newline at end of file
+22 −0
Changes for elections/migrations/0004_election_owner.py: 22 added lines, 0 removed lines.
Original line number Diff line number Diff line
# Generated by Django 6.0.3 on 2026-05-31 15:10

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


class Migration(migrations.Migration):

    dependencies = [
        ('elections', '0003_alter_option_unique_together'),
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.AddField(
            model_name='election',
            name='owner',
            field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.PROTECT, related_name='owned_elections', to=settings.AUTH_USER_MODEL),
            preserve_default=False,
        ),
    ]
+30 −0
Changes for elections/migrations/0005_electioncontributor.py: 30 added lines, 0 removed lines.
Original line number Diff line number Diff line
# Generated by Django 6.0.3 on 2026-05-31 15:23

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


class Migration(migrations.Migration):

    dependencies = [
        ('elections', '0004_election_owner'),
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.CreateModel(
            name='ElectionContributor',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('can_view', models.BooleanField(default=True)),
                ('can_edit', models.BooleanField(default=True)),
                ('can_delete', models.BooleanField(default=True)),
                ('election', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contributors', to='elections.election')),
                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
            ],
            options={
                'unique_together': {('election', 'user')},
            },
        ),
    ]
+60 −0
Changes for elections/migrations/0006_alter_electioncontributor_options_and_more.py: 60 added lines, 0 removed lines.
Original line number Diff line number Diff line
# Generated by Django 6.0.3 on 2026-05-31 15:51

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


class Migration(migrations.Migration):

    dependencies = [
        ('elections', '0005_electioncontributor'),
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.AlterModelOptions(
            name='electioncontributor',
            options={'verbose_name': 'Współpracownik', 'verbose_name_plural': 'Współpracownicy'},
        ),
        migrations.AddField(
            model_name='electioncontributor',
            name='can_enter_local_votes',
            field=models.BooleanField(default=False, verbose_name='wprowadzanie głosów stacjonarnych'),
        ),
        migrations.AddField(
            model_name='electioncontributor',
            name='can_issue_cards',
            field=models.BooleanField(default=False, verbose_name='wydawanie kart'),
        ),
        migrations.AddField(
            model_name='electioncontributor',
            name='can_manage_permissions',
            field=models.BooleanField(default=False, verbose_name='zarządzanie uprawnieniami'),
        ),
        migrations.AddField(
            model_name='electioncontributor',
            name='can_view_stats',
            field=models.BooleanField(default=False, verbose_name='przeglądanie wyników'),
        ),
        migrations.AlterField(
            model_name='electioncontributor',
            name='can_delete',
            field=models.BooleanField(default=False, verbose_name='usuwanie'),
        ),
        migrations.AlterField(
            model_name='electioncontributor',
            name='can_edit',
            field=models.BooleanField(default=False, verbose_name='edycja'),
        ),
        migrations.AlterField(
            model_name='electioncontributor',
            name='can_view',
            field=models.BooleanField(default=True, verbose_name='wyświetlanie'),
        ),
        migrations.AlterField(
            model_name='electioncontributor',
            name='user',
            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='użytkownik'),
        ),
    ]
Loading