Реализовал систему разрешений
This commit is contained in:
@@ -22,6 +22,7 @@ from ..forms import (
|
||||
VchLinkForm,
|
||||
)
|
||||
from ..mixins import FormMessageMixin
|
||||
from ..permissions import PermissionRequiredMixin
|
||||
from ..utils import (
|
||||
add_satellite_list,
|
||||
compare_and_link_vch_load,
|
||||
@@ -41,9 +42,10 @@ class AddSatellitesView(LoginRequiredMixin, View):
|
||||
return redirect("mainapp:source_list")
|
||||
|
||||
|
||||
class AddTranspondersView(LoginRequiredMixin, FormMessageMixin, FormView):
|
||||
class AddTranspondersView(LoginRequiredMixin, PermissionRequiredMixin, FormMessageMixin, FormView):
|
||||
"""View for uploading and parsing transponder data from XML."""
|
||||
|
||||
permission_required = 'transponder_import_xml'
|
||||
template_name = "mainapp/transponders_upload.html"
|
||||
form_class = UploadFileForm
|
||||
success_message = "Файл успешно обработан"
|
||||
@@ -85,8 +87,9 @@ class AddTranspondersView(LoginRequiredMixin, FormMessageMixin, FormView):
|
||||
return reverse_lazy("mainapp:add_trans")
|
||||
|
||||
|
||||
class LoadExcelDataView(LoginRequiredMixin, FormMessageMixin, FormView):
|
||||
class LoadExcelDataView(LoginRequiredMixin, PermissionRequiredMixin, FormMessageMixin, FormView):
|
||||
"""View for loading data from Excel files."""
|
||||
permission_required = 'source_import_excel'
|
||||
|
||||
template_name = "mainapp/add_data_from_excel.html"
|
||||
form_class = LoadExcelData
|
||||
@@ -134,8 +137,9 @@ class LoadExcelDataView(LoginRequiredMixin, FormMessageMixin, FormView):
|
||||
return reverse_lazy("mainapp:load_excel_data")
|
||||
|
||||
|
||||
class LoadCsvDataView(LoginRequiredMixin, FormMessageMixin, FormView):
|
||||
class LoadCsvDataView(LoginRequiredMixin, PermissionRequiredMixin, FormMessageMixin, FormView):
|
||||
"""View for loading data from CSV files."""
|
||||
permission_required = 'source_import_csv'
|
||||
|
||||
template_name = "mainapp/add_data_from_csv.html"
|
||||
form_class = LoadCsvData
|
||||
|
||||
@@ -14,11 +14,13 @@ from openpyxl.styles import Font, Alignment
|
||||
|
||||
from mainapp.forms import KubsatFilterForm
|
||||
from mainapp.models import Source, ObjItem
|
||||
from mainapp.permissions import PermissionRequiredMixin
|
||||
from mainapp.utils import calculate_mean_coords
|
||||
|
||||
|
||||
class KubsatView(LoginRequiredMixin, FormView):
|
||||
class KubsatView(LoginRequiredMixin, PermissionRequiredMixin, FormView):
|
||||
"""Страница Кубсат с фильтрами и таблицей источников"""
|
||||
permission_required = 'kubsat_view'
|
||||
template_name = 'mainapp/kubsat_tabs.html'
|
||||
form_class = KubsatFilterForm
|
||||
|
||||
@@ -349,8 +351,9 @@ class KubsatView(LoginRequiredMixin, FormView):
|
||||
return queryset.distinct()
|
||||
|
||||
|
||||
class KubsatExportView(LoginRequiredMixin, FormView):
|
||||
class KubsatExportView(LoginRequiredMixin, PermissionRequiredMixin, FormView):
|
||||
"""Экспорт отфильтрованных данных в Excel"""
|
||||
permission_required = 'kubsat_view'
|
||||
form_class = KubsatFilterForm
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
@@ -595,8 +598,9 @@ class KubsatExportView(LoginRequiredMixin, FormView):
|
||||
return response
|
||||
|
||||
|
||||
class KubsatCreateRequestsView(LoginRequiredMixin, FormView):
|
||||
class KubsatCreateRequestsView(LoginRequiredMixin, PermissionRequiredMixin, FormView):
|
||||
"""Массовое создание заявок из отфильтрованных данных"""
|
||||
permission_required = 'request_create'
|
||||
form_class = KubsatFilterForm
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
@@ -688,8 +692,9 @@ class KubsatCreateRequestsView(LoginRequiredMixin, FormView):
|
||||
})
|
||||
|
||||
|
||||
class KubsatRecalculateCoordsView(LoginRequiredMixin, FormView):
|
||||
class KubsatRecalculateCoordsView(LoginRequiredMixin, PermissionRequiredMixin, FormView):
|
||||
"""API для пересчёта усреднённых координат по списку ObjItem ID"""
|
||||
permission_required = 'kubsat_view'
|
||||
form_class = KubsatFilterForm
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
|
||||
@@ -23,6 +23,7 @@ from mainapp.models import (
|
||||
Modulation,
|
||||
Standard,
|
||||
)
|
||||
from mainapp.permissions import PermissionRequiredMixin, has_permission
|
||||
|
||||
|
||||
class SignalMarksView(LoginRequiredMixin, View):
|
||||
@@ -324,11 +325,12 @@ class SignalMarksEntryAPIView(LoginRequiredMixin, View):
|
||||
})
|
||||
|
||||
|
||||
class SaveSignalMarksView(LoginRequiredMixin, View):
|
||||
class SaveSignalMarksView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""
|
||||
API для сохранения отметок сигналов.
|
||||
Принимает массив отметок и сохраняет их в базу.
|
||||
"""
|
||||
permission_required = 'mark_create'
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
@@ -404,10 +406,11 @@ class SaveSignalMarksView(LoginRequiredMixin, View):
|
||||
}, status=500)
|
||||
|
||||
|
||||
class CreateTechAnalyzeView(LoginRequiredMixin, View):
|
||||
class CreateTechAnalyzeView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""
|
||||
API для создания нового теханализа из модального окна.
|
||||
"""
|
||||
permission_required = 'tech_analyze_create'
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
|
||||
@@ -15,6 +15,7 @@ from django.views.generic import CreateView, DeleteView, UpdateView
|
||||
from ..forms import GeoForm, ObjItemForm, ParameterForm
|
||||
from ..mixins import CoordinateProcessingMixin, FormMessageMixin, RoleRequiredMixin
|
||||
from ..models import Geo, Modulation, ObjItem, Polarization, Satellite
|
||||
from ..permissions import PermissionRequiredMixin
|
||||
from ..utils import (
|
||||
format_coordinate,
|
||||
format_coords_display,
|
||||
@@ -24,10 +25,9 @@ from ..utils import (
|
||||
)
|
||||
|
||||
|
||||
class DeleteSelectedObjectsView(RoleRequiredMixin, View):
|
||||
class DeleteSelectedObjectsView(PermissionRequiredMixin, View):
|
||||
"""View for deleting multiple selected objects."""
|
||||
|
||||
required_roles = ["admin", "moderator"]
|
||||
permission_required = 'objitem_delete'
|
||||
|
||||
def post(self, request):
|
||||
ids = request.POST.get("ids", "")
|
||||
@@ -503,7 +503,7 @@ class ObjItemListView(LoginRequiredMixin, View):
|
||||
|
||||
|
||||
class ObjItemFormView(
|
||||
RoleRequiredMixin, CoordinateProcessingMixin, FormMessageMixin, UpdateView
|
||||
PermissionRequiredMixin, CoordinateProcessingMixin, FormMessageMixin, UpdateView
|
||||
):
|
||||
"""
|
||||
Base class for creating and editing ObjItem.
|
||||
@@ -515,7 +515,7 @@ class ObjItemFormView(
|
||||
form_class = ObjItemForm
|
||||
template_name = "mainapp/objitem_form.html"
|
||||
success_url = reverse_lazy("mainapp:source_list")
|
||||
required_roles = ["admin", "moderator"]
|
||||
permission_required = 'objitem_edit'
|
||||
|
||||
def get_success_url(self):
|
||||
"""Returns URL with saved filter parameters."""
|
||||
@@ -651,7 +651,7 @@ class ObjItemFormView(
|
||||
|
||||
class ObjItemUpdateView(ObjItemFormView):
|
||||
"""View for editing ObjItem."""
|
||||
|
||||
permission_required = 'objitem_edit'
|
||||
success_message = "Объект успешно сохранён!"
|
||||
|
||||
def set_user_fields(self):
|
||||
@@ -660,7 +660,7 @@ class ObjItemUpdateView(ObjItemFormView):
|
||||
|
||||
class ObjItemCreateView(ObjItemFormView, CreateView):
|
||||
"""View for creating ObjItem."""
|
||||
|
||||
permission_required = 'objitem_create'
|
||||
success_message = "Объект успешно создан!"
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -672,14 +672,13 @@ class ObjItemCreateView(ObjItemFormView, CreateView):
|
||||
self.object.updated_by = self.request.user.customuser
|
||||
|
||||
|
||||
class ObjItemDeleteView(RoleRequiredMixin, FormMessageMixin, DeleteView):
|
||||
class ObjItemDeleteView(PermissionRequiredMixin, FormMessageMixin, DeleteView):
|
||||
"""View for deleting ObjItem."""
|
||||
|
||||
permission_required = 'objitem_delete'
|
||||
model = ObjItem
|
||||
template_name = "mainapp/objitem_confirm_delete.html"
|
||||
success_url = reverse_lazy("mainapp:objitem_list")
|
||||
success_message = "Объект успешно удалён!"
|
||||
required_roles = ["admin", "moderator"]
|
||||
|
||||
def get_success_url(self):
|
||||
"""Returns URL with saved filter parameters."""
|
||||
|
||||
@@ -10,6 +10,7 @@ from django.views import View
|
||||
from django.utils import timezone
|
||||
|
||||
from ..models import ObjItem, Satellite, Source
|
||||
from ..permissions import PermissionRequiredMixin
|
||||
from ..utils import (
|
||||
calculate_mean_coords,
|
||||
calculate_distance_wgs84,
|
||||
@@ -24,10 +25,11 @@ from ..utils import (
|
||||
)
|
||||
|
||||
|
||||
class PointsAveragingView(LoginRequiredMixin, View):
|
||||
class PointsAveragingView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""
|
||||
View for points averaging form with date range selection and grouping.
|
||||
"""
|
||||
permission_required = 'source_averaging'
|
||||
|
||||
def get(self, request):
|
||||
# Get satellites that have sources with points with geo data
|
||||
|
||||
@@ -16,6 +16,7 @@ from django.views.generic import CreateView, UpdateView
|
||||
from ..forms import SatelliteForm
|
||||
from ..mixins import RoleRequiredMixin, FormMessageMixin
|
||||
from ..models import Satellite, Band
|
||||
from ..permissions import PermissionRequiredMixin
|
||||
from ..utils import parse_pagination_params
|
||||
|
||||
|
||||
@@ -252,15 +253,14 @@ class SatelliteListView(LoginRequiredMixin, View):
|
||||
return render(request, "mainapp/satellite_list.html", context)
|
||||
|
||||
|
||||
class SatelliteCreateView(RoleRequiredMixin, FormMessageMixin, CreateView):
|
||||
class SatelliteCreateView(PermissionRequiredMixin, FormMessageMixin, CreateView):
|
||||
"""View for creating a new satellite."""
|
||||
|
||||
permission_required = 'satellite_create'
|
||||
model = Satellite
|
||||
form_class = SatelliteForm
|
||||
template_name = "mainapp/satellite_form.html"
|
||||
success_url = reverse_lazy("mainapp:satellite_list")
|
||||
success_message = "Спутник успешно создан!"
|
||||
required_roles = ["admin", "moderator"]
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
@@ -274,15 +274,14 @@ class SatelliteCreateView(RoleRequiredMixin, FormMessageMixin, CreateView):
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class SatelliteUpdateView(RoleRequiredMixin, FormMessageMixin, UpdateView):
|
||||
class SatelliteUpdateView(PermissionRequiredMixin, FormMessageMixin, UpdateView):
|
||||
"""View for updating an existing satellite."""
|
||||
|
||||
permission_required = 'satellite_edit'
|
||||
model = Satellite
|
||||
form_class = SatelliteForm
|
||||
template_name = "mainapp/satellite_form.html"
|
||||
success_url = reverse_lazy("mainapp:satellite_list")
|
||||
success_message = "Спутник успешно обновлен!"
|
||||
required_roles = ["admin", "moderator"]
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
import json
|
||||
@@ -320,10 +319,9 @@ class SatelliteUpdateView(RoleRequiredMixin, FormMessageMixin, UpdateView):
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class DeleteSelectedSatellitesView(RoleRequiredMixin, View):
|
||||
class DeleteSelectedSatellitesView(PermissionRequiredMixin, View):
|
||||
"""View for deleting multiple selected satellites with confirmation."""
|
||||
|
||||
required_roles = ["admin", "moderator"]
|
||||
permission_required = 'satellite_delete'
|
||||
|
||||
def get(self, request):
|
||||
"""Show confirmation page with details about satellites to be deleted."""
|
||||
|
||||
@@ -17,6 +17,7 @@ from django.views import View
|
||||
from ..forms import SourceForm
|
||||
from ..models import Source, Satellite
|
||||
from ..utils import format_coords_display, parse_pagination_params
|
||||
from ..permissions import PermissionRequiredMixin, permission_required
|
||||
|
||||
|
||||
class SourceListView(LoginRequiredMixin, View):
|
||||
@@ -818,7 +819,10 @@ class SourceListView(LoginRequiredMixin, View):
|
||||
|
||||
|
||||
class AdminModeratorMixin(UserPassesTestMixin):
|
||||
"""Mixin to restrict access to admin and moderator roles only."""
|
||||
"""Mixin to restrict access to admin and moderator roles only.
|
||||
|
||||
DEPRECATED: Use PermissionRequiredMixin instead for granular permissions.
|
||||
"""
|
||||
|
||||
def test_func(self):
|
||||
return (
|
||||
@@ -832,8 +836,9 @@ class AdminModeratorMixin(UserPassesTestMixin):
|
||||
return redirect('mainapp:source_list')
|
||||
|
||||
|
||||
class SourceCreateView(LoginRequiredMixin, AdminModeratorMixin, View):
|
||||
class SourceCreateView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""View for creating new Source."""
|
||||
permission_required = 'source_create'
|
||||
|
||||
def get(self, request):
|
||||
form = SourceForm()
|
||||
@@ -874,8 +879,9 @@ class SourceCreateView(LoginRequiredMixin, AdminModeratorMixin, View):
|
||||
return render(request, 'mainapp/source_form.html', context)
|
||||
|
||||
|
||||
class SourceUpdateView(LoginRequiredMixin, AdminModeratorMixin, View):
|
||||
class SourceUpdateView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""View for editing Source with 4 coordinate fields and related ObjItems."""
|
||||
permission_required = 'source_edit'
|
||||
|
||||
def get(self, request, pk):
|
||||
source = get_object_or_404(Source, pk=pk)
|
||||
@@ -945,8 +951,9 @@ class SourceUpdateView(LoginRequiredMixin, AdminModeratorMixin, View):
|
||||
return render(request, 'mainapp/source_form.html', context)
|
||||
|
||||
|
||||
class SourceDeleteView(LoginRequiredMixin, AdminModeratorMixin, View):
|
||||
class SourceDeleteView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""View for deleting Source."""
|
||||
permission_required = 'source_delete'
|
||||
|
||||
def get(self, request, pk):
|
||||
source = get_object_or_404(Source, pk=pk)
|
||||
@@ -975,8 +982,9 @@ class SourceDeleteView(LoginRequiredMixin, AdminModeratorMixin, View):
|
||||
return redirect('mainapp:source_list')
|
||||
|
||||
|
||||
class DeleteSelectedSourcesView(LoginRequiredMixin, AdminModeratorMixin, View):
|
||||
class DeleteSelectedSourcesView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""View for deleting multiple selected sources with confirmation."""
|
||||
permission_required = 'source_delete'
|
||||
|
||||
def get(self, request):
|
||||
"""Show confirmation page with details about sources to be deleted."""
|
||||
@@ -1062,8 +1070,9 @@ class DeleteSelectedSourcesView(LoginRequiredMixin, AdminModeratorMixin, View):
|
||||
|
||||
|
||||
|
||||
class MergeSourcesView(LoginRequiredMixin, AdminModeratorMixin, View):
|
||||
class MergeSourcesView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""View for merging multiple sources into one."""
|
||||
permission_required = 'source_merge'
|
||||
|
||||
def post(self, request):
|
||||
"""Merge selected sources into the first one."""
|
||||
|
||||
@@ -12,6 +12,7 @@ from django.utils import timezone
|
||||
|
||||
from mainapp.models import SourceRequest, SourceRequestStatusHistory, Source, Satellite
|
||||
from mainapp.forms import SourceRequestForm
|
||||
from mainapp.permissions import PermissionRequiredMixin
|
||||
|
||||
import re
|
||||
import pandas as pd
|
||||
@@ -83,8 +84,9 @@ class SourceRequestListView(LoginRequiredMixin, ListView):
|
||||
return context
|
||||
|
||||
|
||||
class SourceRequestCreateView(LoginRequiredMixin, CreateView):
|
||||
class SourceRequestCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||||
"""Создание заявки на источник."""
|
||||
permission_required = 'request_create'
|
||||
model = SourceRequest
|
||||
form_class = SourceRequestForm
|
||||
template_name = 'mainapp/source_request_form.html'
|
||||
@@ -132,8 +134,9 @@ class SourceRequestCreateView(LoginRequiredMixin, CreateView):
|
||||
return super().form_invalid(form)
|
||||
|
||||
|
||||
class SourceRequestUpdateView(LoginRequiredMixin, UpdateView):
|
||||
class SourceRequestUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||||
"""Редактирование заявки на источник."""
|
||||
permission_required = 'request_edit'
|
||||
model = SourceRequest
|
||||
form_class = SourceRequestForm
|
||||
template_name = 'mainapp/source_request_form.html'
|
||||
@@ -164,8 +167,9 @@ class SourceRequestUpdateView(LoginRequiredMixin, UpdateView):
|
||||
return super().form_invalid(form)
|
||||
|
||||
|
||||
class SourceRequestDeleteView(LoginRequiredMixin, View):
|
||||
class SourceRequestDeleteView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""Удаление заявки на источник."""
|
||||
permission_required = 'request_delete'
|
||||
|
||||
def post(self, request, pk):
|
||||
try:
|
||||
@@ -182,8 +186,9 @@ class SourceRequestDeleteView(LoginRequiredMixin, View):
|
||||
}, status=404)
|
||||
|
||||
|
||||
class SourceRequestBulkDeleteView(LoginRequiredMixin, View):
|
||||
class SourceRequestBulkDeleteView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""Массовое удаление заявок."""
|
||||
permission_required = 'request_delete'
|
||||
|
||||
def post(self, request):
|
||||
import json
|
||||
@@ -688,8 +693,9 @@ class SourceDataAPIView(LoginRequiredMixin, View):
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
class SourceRequestImportView(LoginRequiredMixin, View):
|
||||
class SourceRequestImportView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""Импорт заявок из Excel файла."""
|
||||
permission_required = 'request_import'
|
||||
|
||||
def get(self, request):
|
||||
"""Отображает форму загрузки файла."""
|
||||
|
||||
@@ -10,11 +10,13 @@ from django.views.generic import TemplateView
|
||||
from django.http import JsonResponse
|
||||
|
||||
from ..models import ObjItem, Source, Satellite, Geo, SourceRequest, SourceRequestStatusHistory
|
||||
from ..permissions import PermissionRequiredMixin
|
||||
from mapsapp.models import Transponders
|
||||
|
||||
|
||||
class StatisticsView(TemplateView):
|
||||
class StatisticsView(PermissionRequiredMixin, TemplateView):
|
||||
"""Страница статистики по данным геолокации."""
|
||||
permission_required = 'statistics_view'
|
||||
|
||||
template_name = 'mainapp/statistics.html'
|
||||
|
||||
|
||||
@@ -19,13 +19,15 @@ from ..models import (
|
||||
Parameter,
|
||||
)
|
||||
from ..mixins import RoleRequiredMixin
|
||||
from ..permissions import PermissionRequiredMixin
|
||||
from ..utils import parse_pagination_params, find_matching_transponder, find_matching_lyngsat
|
||||
|
||||
|
||||
class TechAnalyzeEntryView(LoginRequiredMixin, View):
|
||||
class TechAnalyzeEntryView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""
|
||||
Представление для ввода данных технического анализа.
|
||||
"""
|
||||
permission_required = 'source_tech_analyze'
|
||||
|
||||
def get(self, request):
|
||||
satellites = Satellite.objects.all().order_by('name')
|
||||
@@ -37,10 +39,11 @@ class TechAnalyzeEntryView(LoginRequiredMixin, View):
|
||||
return render(request, 'mainapp/tech_analyze_entry.html', context)
|
||||
|
||||
|
||||
class TechAnalyzeSaveView(LoginRequiredMixin, View):
|
||||
class TechAnalyzeSaveView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""
|
||||
API endpoint для сохранения данных технического анализа.
|
||||
"""
|
||||
permission_required = 'tech_analyze_create'
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
@@ -177,7 +180,7 @@ class TechAnalyzeSaveView(LoginRequiredMixin, View):
|
||||
|
||||
|
||||
|
||||
class LinkExistingPointsView(LoginRequiredMixin, View):
|
||||
class LinkExistingPointsView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""
|
||||
API endpoint для привязки существующих точек к данным теханализа.
|
||||
|
||||
@@ -194,6 +197,7 @@ class LinkExistingPointsView(LoginRequiredMixin, View):
|
||||
* Обновить полосу частот (если 0 или None)
|
||||
* Подобрать подходящий транспондер
|
||||
"""
|
||||
permission_required = 'tech_analyze_edit'
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
@@ -388,11 +392,11 @@ class TechAnalyzeListView(LoginRequiredMixin, View):
|
||||
return render(request, 'mainapp/tech_analyze_list.html', context)
|
||||
|
||||
|
||||
class TechAnalyzeDeleteView(LoginRequiredMixin, RoleRequiredMixin, View):
|
||||
class TechAnalyzeDeleteView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""
|
||||
API endpoint для удаления выбранных записей теханализа.
|
||||
"""
|
||||
allowed_roles = ['admin', 'moderator']
|
||||
permission_required = 'tech_analyze_delete'
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
|
||||
@@ -15,7 +15,8 @@ from django.views.generic import CreateView, UpdateView
|
||||
|
||||
from mapsapp.models import Transponders
|
||||
from ..forms import TransponderForm
|
||||
from ..mixins import RoleRequiredMixin, FormMessageMixin
|
||||
from ..mixins import FormMessageMixin
|
||||
from ..permissions import PermissionRequiredMixin
|
||||
from ..models import Satellite, Polarization
|
||||
from ..utils import parse_pagination_params
|
||||
|
||||
@@ -246,15 +247,15 @@ class TransponderListView(LoginRequiredMixin, View):
|
||||
return render(request, "mainapp/transponder_list.html", context)
|
||||
|
||||
|
||||
class TransponderCreateView(RoleRequiredMixin, FormMessageMixin, CreateView):
|
||||
class TransponderCreateView(PermissionRequiredMixin, FormMessageMixin, CreateView):
|
||||
"""View for creating a new transponder."""
|
||||
|
||||
permission_required = 'transponder_create'
|
||||
model = Transponders
|
||||
form_class = TransponderForm
|
||||
template_name = "mainapp/transponder_form.html"
|
||||
success_url = reverse_lazy("mainapp:transponder_list")
|
||||
success_message = "Транспондер успешно создан!"
|
||||
required_roles = ["admin", "moderator"]
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
@@ -268,15 +269,15 @@ class TransponderCreateView(RoleRequiredMixin, FormMessageMixin, CreateView):
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class TransponderUpdateView(RoleRequiredMixin, FormMessageMixin, UpdateView):
|
||||
class TransponderUpdateView(PermissionRequiredMixin, FormMessageMixin, UpdateView):
|
||||
"""View for updating an existing transponder."""
|
||||
|
||||
permission_required = 'transponder_edit'
|
||||
model = Transponders
|
||||
form_class = TransponderForm
|
||||
template_name = "mainapp/transponder_form.html"
|
||||
success_url = reverse_lazy("mainapp:transponder_list")
|
||||
success_message = "Транспондер успешно обновлен!"
|
||||
required_roles = ["admin", "moderator"]
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
@@ -293,10 +294,10 @@ class TransponderUpdateView(RoleRequiredMixin, FormMessageMixin, UpdateView):
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class DeleteSelectedTranspondersView(RoleRequiredMixin, View):
|
||||
class DeleteSelectedTranspondersView(PermissionRequiredMixin, View):
|
||||
"""View for deleting multiple selected transponders with confirmation."""
|
||||
|
||||
required_roles = ["admin", "moderator"]
|
||||
permission_required = 'transponder_delete'
|
||||
|
||||
def get(self, request):
|
||||
"""Show confirmation page with details about transponders to be deleted."""
|
||||
|
||||
180
dbapp/mainapp/views/user_permissions.py
Normal file
180
dbapp/mainapp/views/user_permissions.py
Normal file
@@ -0,0 +1,180 @@
|
||||
"""
|
||||
Views для управления правами пользователей.
|
||||
"""
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib import messages
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.views import View
|
||||
|
||||
from ..models import CustomUser, UserPermission
|
||||
from ..permissions import (
|
||||
PERMISSIONS,
|
||||
DEFAULT_ROLE_PERMISSIONS,
|
||||
PermissionRequiredMixin,
|
||||
has_permission
|
||||
)
|
||||
|
||||
|
||||
class UserPermissionsListView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""Список пользователей с их правами."""
|
||||
permission_required = 'admin_access'
|
||||
|
||||
def get(self, request):
|
||||
users = CustomUser.objects.select_related('user').prefetch_related(
|
||||
'user_permissions'
|
||||
).order_by('user__username')
|
||||
|
||||
context = {
|
||||
'users': users,
|
||||
'permissions': PERMISSIONS,
|
||||
'default_permissions': DEFAULT_ROLE_PERMISSIONS,
|
||||
}
|
||||
return render(request, 'mainapp/user_permissions_list.html', context)
|
||||
|
||||
|
||||
class UserPermissionsEditView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""Редактирование прав конкретного пользователя."""
|
||||
permission_required = 'admin_access'
|
||||
|
||||
def get(self, request, pk):
|
||||
custom_user = get_object_or_404(CustomUser.objects.select_related('user'), pk=pk)
|
||||
|
||||
# Получаем все разрешения
|
||||
all_permissions = UserPermission.objects.all()
|
||||
|
||||
# Текущие разрешения пользователя
|
||||
user_perm_codes = set(custom_user.user_permissions.values_list('code', flat=True))
|
||||
|
||||
# Права по умолчанию для роли
|
||||
default_perms = set(DEFAULT_ROLE_PERMISSIONS.get(custom_user.role, []))
|
||||
|
||||
# Группируем разрешения по категориям
|
||||
permission_groups = {
|
||||
'Источники': [],
|
||||
'Заявки': [],
|
||||
'Точки ГЛ': [],
|
||||
'Спутники': [],
|
||||
'Транспондеры': [],
|
||||
'Тех. анализ': [],
|
||||
'Отметки': [],
|
||||
'Прочее': [],
|
||||
}
|
||||
|
||||
for code, name, desc in PERMISSIONS:
|
||||
perm_data = {
|
||||
'code': code,
|
||||
'name': name,
|
||||
'description': desc,
|
||||
'has_permission': code in user_perm_codes if custom_user.use_custom_permissions else code in default_perms,
|
||||
'is_default': code in default_perms,
|
||||
}
|
||||
|
||||
if code.startswith('source_'):
|
||||
permission_groups['Источники'].append(perm_data)
|
||||
elif code.startswith('request_'):
|
||||
permission_groups['Заявки'].append(perm_data)
|
||||
elif code.startswith('objitem_'):
|
||||
permission_groups['Точки ГЛ'].append(perm_data)
|
||||
elif code.startswith('satellite_'):
|
||||
permission_groups['Спутники'].append(perm_data)
|
||||
elif code.startswith('transponder_'):
|
||||
permission_groups['Транспондеры'].append(perm_data)
|
||||
elif code.startswith('tech_analyze_'):
|
||||
permission_groups['Тех. анализ'].append(perm_data)
|
||||
elif code.startswith('mark_'):
|
||||
permission_groups['Отметки'].append(perm_data)
|
||||
else:
|
||||
permission_groups['Прочее'].append(perm_data)
|
||||
|
||||
context = {
|
||||
'custom_user': custom_user,
|
||||
'permission_groups': permission_groups,
|
||||
'default_perms': default_perms,
|
||||
}
|
||||
return render(request, 'mainapp/user_permissions_edit.html', context)
|
||||
|
||||
def post(self, request, pk):
|
||||
custom_user = get_object_or_404(CustomUser, pk=pk)
|
||||
|
||||
# Получаем выбранные разрешения
|
||||
selected_permissions = request.POST.getlist('permissions')
|
||||
use_custom = request.POST.get('use_custom_permissions') == 'on'
|
||||
|
||||
# Обновляем флаг использования индивидуальных разрешений
|
||||
custom_user.use_custom_permissions = use_custom
|
||||
|
||||
if use_custom:
|
||||
# Очищаем текущие разрешения и добавляем новые
|
||||
custom_user.user_permissions.clear()
|
||||
|
||||
for perm_code in selected_permissions:
|
||||
perm, created = UserPermission.objects.get_or_create(code=perm_code)
|
||||
custom_user.user_permissions.add(perm)
|
||||
|
||||
custom_user.save()
|
||||
|
||||
messages.success(request, f'Права пользователя {custom_user.user.username} обновлены.')
|
||||
return redirect('mainapp:user_permissions_list')
|
||||
|
||||
|
||||
class UserPermissionsApiView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""API для управления правами пользователей."""
|
||||
permission_required = 'admin_access'
|
||||
|
||||
def post(self, request, pk):
|
||||
"""Обновление прав пользователя через AJAX."""
|
||||
import json
|
||||
|
||||
try:
|
||||
data = json.loads(request.body)
|
||||
custom_user = get_object_or_404(CustomUser, pk=pk)
|
||||
|
||||
use_custom = data.get('use_custom_permissions', False)
|
||||
permissions = data.get('permissions', [])
|
||||
|
||||
custom_user.use_custom_permissions = use_custom
|
||||
|
||||
if use_custom:
|
||||
custom_user.user_permissions.clear()
|
||||
for perm_code in permissions:
|
||||
perm, _ = UserPermission.objects.get_or_create(code=perm_code)
|
||||
custom_user.user_permissions.add(perm)
|
||||
|
||||
custom_user.save()
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'message': f'Права пользователя {custom_user.user.username} обновлены'
|
||||
})
|
||||
except Exception as e:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': str(e)
|
||||
}, status=400)
|
||||
|
||||
|
||||
|
||||
class InitPermissionsView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||
"""Инициализация всех разрешений в базе данных."""
|
||||
permission_required = 'admin_access'
|
||||
|
||||
def get(self, request):
|
||||
from ..permissions import PERMISSIONS
|
||||
|
||||
created_count = 0
|
||||
existing_count = 0
|
||||
|
||||
for code, name, description in PERMISSIONS:
|
||||
perm, created = UserPermission.objects.get_or_create(code=code)
|
||||
if created:
|
||||
created_count += 1
|
||||
else:
|
||||
existing_count += 1
|
||||
|
||||
messages.success(
|
||||
request,
|
||||
f'Разрешения инициализированы. Создано: {created_count}, уже существовало: {existing_count}'
|
||||
)
|
||||
return redirect('mainapp:user_permissions_list')
|
||||
Reference in New Issue
Block a user