Site admin interface
parent
fcaa4855e8
commit
15d92664f9
|
@ -29,13 +29,17 @@ class AuthViewSet(viewsets.ViewSet):
|
||||||
def _create_response(self, user):
|
def _create_response(self, user):
|
||||||
serializer = UserSerializer(user)
|
serializer = UserSerializer(user)
|
||||||
response_data = serializer.data
|
response_data = serializer.data
|
||||||
|
|
||||||
|
domain = get_active_domain()
|
||||||
|
response_data['is_site_owner'] = domain.user_is_owner(user)
|
||||||
|
response_data['is_site_staff'] = domain.user_is_staff(user)
|
||||||
response_data["auth_token"] = auth.get_token_for_user(user)
|
response_data["auth_token"] = auth.get_token_for_user(user)
|
||||||
return response_data
|
return response_data
|
||||||
|
|
||||||
def _create_domain_member(self, user):
|
def _create_domain_member(self, user):
|
||||||
domain = get_active_domain()
|
domain = get_active_domain()
|
||||||
|
|
||||||
if DomainMember.objects.filter(domain=domain, user=user).count() == 0:
|
if domain.members.filter(user=user).count() == 0:
|
||||||
domain_member = DomainMember(domain=domain, user=user, email=user.email,
|
domain_member = DomainMember(domain=domain, user=user, email=user.email,
|
||||||
is_owner=False, is_staff=False)
|
is_owner=False, is_staff=False)
|
||||||
domain_member.save()
|
domain_member.save()
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import Domain
|
from .models import Domain, DomainMember
|
||||||
|
|
||||||
|
class DomainMemberInline(admin.TabularInline):
|
||||||
|
model = DomainMember
|
||||||
|
|
||||||
class DomainAdmin(admin.ModelAdmin):
|
class DomainAdmin(admin.ModelAdmin):
|
||||||
list_display = ('domain', 'name')
|
list_display = ('domain', 'name')
|
||||||
search_fields = ('domain', 'name')
|
search_fields = ('domain', 'name')
|
||||||
|
inlines = [ DomainMemberInline, ]
|
||||||
|
|
||||||
admin.site.register(Domain, DomainAdmin)
|
admin.site.register(Domain, DomainAdmin)
|
||||||
|
|
|
@ -2,13 +2,38 @@
|
||||||
|
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.permissions import AllowAny
|
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||||
|
|
||||||
from .serializers import DomainSerializer
|
from django.http import Http404
|
||||||
|
|
||||||
|
from greenmine.base.api import ModelCrudViewSet, UpdateModelMixin
|
||||||
|
from .serializers import DomainSerializer, DomainMemberSerializer
|
||||||
|
from .permissions import DomainMembersPermission, DomainPermission
|
||||||
|
from .models import DomainMember, Domain
|
||||||
|
|
||||||
|
|
||||||
class DomainViewSet(viewsets.ViewSet):
|
class DomainViewSet(UpdateModelMixin, viewsets.GenericViewSet):
|
||||||
permission_classes = (AllowAny,)
|
permission_classes = (IsAuthenticated, DomainPermission,)
|
||||||
|
serializer_class = DomainSerializer
|
||||||
|
queryset = Domain.objects.all()
|
||||||
|
|
||||||
def list(self, request, **kwargs):
|
def list(self, request, **kwargs):
|
||||||
return Response(DomainSerializer(request.domain).data)
|
domain_data = DomainSerializer(request.domain).data
|
||||||
|
if request.domain.user_is_normal_user(request.user):
|
||||||
|
domain_data['projects'] = None
|
||||||
|
elif request.user.is_anonymous():
|
||||||
|
domain_data['projects'] = None
|
||||||
|
return Response(domain_data)
|
||||||
|
|
||||||
|
def update(self, request, **kwargs):
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
def create(self, request, **kwargs):
|
||||||
|
self.kwargs['pk'] = request.domain.pk
|
||||||
|
return super().update(request, pk=request.domain.pk, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class DomainMembersViewSet(ModelCrudViewSet):
|
||||||
|
permission_classes = (IsAuthenticated, DomainMembersPermission,)
|
||||||
|
serializer_class = DomainMemberSerializer
|
||||||
|
queryset = DomainMember.objects.all()
|
||||||
|
|
|
@ -45,9 +45,18 @@ class Domain(models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.domain
|
return self.domain
|
||||||
|
|
||||||
|
def user_is_owner(self, user):
|
||||||
|
return self.members.filter(id=user.id, is_owner=True).count() > 0
|
||||||
|
|
||||||
|
def user_is_staff(self, user):
|
||||||
|
return self.members.filter(id=user.id, is_staff=True).count() > 0
|
||||||
|
|
||||||
|
def user_is_normal_user(self, user):
|
||||||
|
return self.members.filter(id=user.id, is_owner=False, is_staff=False).count() > 0
|
||||||
|
|
||||||
|
|
||||||
class DomainMember(models.Model):
|
class DomainMember(models.Model):
|
||||||
domain = models.ForeignKey("Domain", related_name="+", null=True)
|
domain = models.ForeignKey("Domain", related_name="members", null=True)
|
||||||
user = models.ForeignKey("users.User", related_name="+", null=True)
|
user = models.ForeignKey("users.User", related_name="+", null=True)
|
||||||
|
|
||||||
email = models.EmailField(max_length=255)
|
email = models.EmailField(max_length=255)
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
from rest_framework import permissions
|
||||||
|
|
||||||
|
from greenmine.base.domains.models import DomainMember
|
||||||
|
from greenmine.base.domains import get_active_domain
|
||||||
|
|
||||||
|
|
||||||
|
class DomainPermission(permissions.BasePermission):
|
||||||
|
safe_methods = ['HEAD', 'OPTIONS', 'GET']
|
||||||
|
|
||||||
|
def has_object_permission(self, request, view, obj):
|
||||||
|
if request.method in self.safe_methods:
|
||||||
|
return True
|
||||||
|
|
||||||
|
domain = get_active_domain()
|
||||||
|
return domain.user_is_owner(request.user)
|
||||||
|
|
||||||
|
|
||||||
|
class DomainMembersPermission(permissions.BasePermission):
|
||||||
|
safe_methods = ['HEAD', 'OPTIONS']
|
||||||
|
|
||||||
|
def has_permission(self, request, view):
|
||||||
|
if request.method in self.safe_methods:
|
||||||
|
return True
|
||||||
|
|
||||||
|
domain = get_active_domain()
|
||||||
|
if request.method in ["POST", "PUT", "PATCH", "GET"]:
|
||||||
|
return domain.user_is_owner(request.user)
|
||||||
|
else:
|
||||||
|
return False
|
|
@ -1,10 +1,22 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from .models import Domain
|
from .models import Domain, DomainMember
|
||||||
|
from greenmine.base.users.serializers import UserSerializer
|
||||||
|
|
||||||
|
|
||||||
class DomainSerializer(serializers.ModelSerializer):
|
class DomainSerializer(serializers.ModelSerializer):
|
||||||
|
projects = serializers.SerializerMethodField('get_projects')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Domain
|
model = Domain
|
||||||
fields = ('public_register', 'default_language')
|
fields = ('public_register', 'default_language', "projects")
|
||||||
|
|
||||||
|
def get_projects(self, obj):
|
||||||
|
return map(lambda x: {"id": x.id, "name": x.name }, obj.projects.all().order_by('name'))
|
||||||
|
|
||||||
|
|
||||||
|
class DomainMemberSerializer(serializers.ModelSerializer):
|
||||||
|
user = UserSerializer()
|
||||||
|
class Meta:
|
||||||
|
model = DomainMember
|
||||||
|
|
|
@ -16,6 +16,7 @@ from djmail.template_mail import MagicMailBuilder
|
||||||
from greenmine.base import filters
|
from greenmine.base import filters
|
||||||
from greenmine.base import exceptions as exc
|
from greenmine.base import exceptions as exc
|
||||||
from greenmine.base.api import ModelCrudViewSet, ModelListViewSet, RetrieveModelMixin
|
from greenmine.base.api import ModelCrudViewSet, ModelListViewSet, RetrieveModelMixin
|
||||||
|
from greenmine.base.domains import get_active_domain
|
||||||
from greenmine.base.notifications.api import NotificationSenderMixin
|
from greenmine.base.notifications.api import NotificationSenderMixin
|
||||||
from greenmine.projects.aggregates.tags import get_all_tags
|
from greenmine.projects.aggregates.tags import get_all_tags
|
||||||
|
|
||||||
|
@ -27,6 +28,29 @@ from .aggregates import stats
|
||||||
from .aggregates import filters as filters_aggr
|
from .aggregates import filters as filters_aggr
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectAdminViewSet(ModelCrudViewSet):
|
||||||
|
model = models.Project
|
||||||
|
serializer_class = serializers.ProjectDetailSerializer
|
||||||
|
list_serializer_class = serializers.ProjectSerializer
|
||||||
|
permission_classes = (IsAuthenticated, permissions.ProjectAdminPermission)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
domain = get_active_domain()
|
||||||
|
return domain.projects.all()
|
||||||
|
|
||||||
|
def pre_save(self, obj):
|
||||||
|
obj.owner = self.request.user
|
||||||
|
|
||||||
|
# FIXME
|
||||||
|
|
||||||
|
# Assign domain only if it current
|
||||||
|
# value is None
|
||||||
|
if not obj.domain:
|
||||||
|
obj.domain = self.request.domain
|
||||||
|
|
||||||
|
super().pre_save(obj)
|
||||||
|
|
||||||
|
|
||||||
class ProjectViewSet(ModelCrudViewSet):
|
class ProjectViewSet(ModelCrudViewSet):
|
||||||
model = models.Project
|
model = models.Project
|
||||||
serializer_class = serializers.ProjectDetailSerializer
|
serializer_class = serializers.ProjectDetailSerializer
|
||||||
|
|
|
@ -1,17 +1,41 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from greenmine.base.permissions import BasePermission
|
from greenmine.base.permissions import BasePermission
|
||||||
|
from greenmine.base.domains import get_active_domain
|
||||||
|
|
||||||
|
|
||||||
class ProjectPermission(BasePermission):
|
class ProjectPermission(BasePermission):
|
||||||
get_permission = "view_project"
|
get_permission = "view_project"
|
||||||
post_permission = "add_project"
|
post_permission = None
|
||||||
put_permission = "change_project"
|
put_permission = "change_project"
|
||||||
patch_permission = "change_project"
|
patch_permission = "change_project"
|
||||||
delete_permission = "delete_project"
|
delete_permission = None
|
||||||
safe_methods = ["HEAD", "OPTIONS"]
|
safe_methods = ["HEAD", "OPTIONS"]
|
||||||
path_to_project = []
|
path_to_project = []
|
||||||
|
|
||||||
|
class ProjectAdminPermission(BasePermission):
|
||||||
|
def has_permission(self, request, view):
|
||||||
|
if request.method in self.safe_methods:
|
||||||
|
return True
|
||||||
|
|
||||||
|
domain = get_active_domain()
|
||||||
|
if request.method in ["POST", "PUT", "GET", "PATCH"]:
|
||||||
|
return domain.user_is_staff(request.user)
|
||||||
|
elif request.method == "DELETE":
|
||||||
|
return domain.user_is_owner(request.user)
|
||||||
|
return super().has_permission(request, view)
|
||||||
|
|
||||||
|
def has_object_permission(self, request, view, obj):
|
||||||
|
if request.method in self.safe_methods:
|
||||||
|
return True
|
||||||
|
|
||||||
|
domain = get_active_domain()
|
||||||
|
if request.method in ["POST", "PUT", "GET", "PATCH"]:
|
||||||
|
return domain.user_is_staff(request.user)
|
||||||
|
elif request.method == "DELETE":
|
||||||
|
return domain.user_is_owner(request.user)
|
||||||
|
return super().has_object_permission(request, view, obj)
|
||||||
|
|
||||||
|
|
||||||
class MembershipPermission(BasePermission):
|
class MembershipPermission(BasePermission):
|
||||||
get_permission = "view_membership"
|
get_permission = "view_membership"
|
||||||
|
|
|
@ -418,8 +418,8 @@ class ProjectsTestCase(test.TestCase):
|
||||||
password=self.user3.username)
|
password=self.user3.username)
|
||||||
self.assertTrue(response)
|
self.assertTrue(response)
|
||||||
response = self.client.delete(reverse("projects-detail", args=(self.project1.id,)))
|
response = self.client.delete(reverse("projects-detail", args=(self.project1.id,)))
|
||||||
self.assertEqual(response.status_code, 204)
|
self.assertEqual(response.status_code, 403)
|
||||||
self.assertEqual(Project.objects.all().count(), 3)
|
self.assertEqual(Project.objects.all().count(), 4)
|
||||||
self.client.logout()
|
self.client.logout()
|
||||||
|
|
||||||
def test_delete_project_by_not_membership(self):
|
def test_delete_project_by_not_membership(self):
|
||||||
|
|
|
@ -4,11 +4,11 @@ from greenmine.base import routers
|
||||||
from greenmine.base.auth.api import AuthViewSet
|
from greenmine.base.auth.api import AuthViewSet
|
||||||
from greenmine.base.users.api import RolesViewSet, UsersViewSet
|
from greenmine.base.users.api import RolesViewSet, UsersViewSet
|
||||||
from greenmine.base.searches.api import SearchViewSet
|
from greenmine.base.searches.api import SearchViewSet
|
||||||
from greenmine.base.domains.api import DomainViewSet
|
from greenmine.base.domains.api import DomainViewSet, DomainMembersViewSet
|
||||||
from greenmine.projects.api import (ProjectViewSet, MembershipViewSet, InvitationViewSet,
|
from greenmine.projects.api import (ProjectViewSet, MembershipViewSet, InvitationViewSet,
|
||||||
UserStoryStatusViewSet, PointsViewSet, TaskStatusViewSet,
|
UserStoryStatusViewSet, PointsViewSet, TaskStatusViewSet,
|
||||||
IssueStatusViewSet, IssueTypeViewSet, PriorityViewSet,
|
IssueStatusViewSet, IssueTypeViewSet, PriorityViewSet,
|
||||||
SeverityViewSet) #, QuestionStatusViewSet)
|
SeverityViewSet, ProjectAdminViewSet) #, QuestionStatusViewSet)
|
||||||
from greenmine.projects.milestones.api import MilestoneViewSet
|
from greenmine.projects.milestones.api import MilestoneViewSet
|
||||||
from greenmine.projects.userstories.api import UserStoryViewSet, UserStoryAttachmentViewSet
|
from greenmine.projects.userstories.api import UserStoryViewSet, UserStoryAttachmentViewSet
|
||||||
from greenmine.projects.tasks.api import TaskViewSet, TaskAttachmentViewSet
|
from greenmine.projects.tasks.api import TaskViewSet, TaskAttachmentViewSet
|
||||||
|
@ -30,6 +30,8 @@ router.register(r"search", SearchViewSet, base_name="search")
|
||||||
|
|
||||||
# greenmine.base.domains
|
# greenmine.base.domains
|
||||||
router.register(r"sites", DomainViewSet, base_name="sites")
|
router.register(r"sites", DomainViewSet, base_name="sites")
|
||||||
|
router.register(r"site-members", DomainMembersViewSet, base_name="site-members")
|
||||||
|
router.register(r"site-projects", ProjectAdminViewSet, base_name="site-projects")
|
||||||
|
|
||||||
# greenmine.projects
|
# greenmine.projects
|
||||||
router.register(r"projects", ProjectViewSet, base_name="projects")
|
router.register(r"projects", ProjectViewSet, base_name="projects")
|
||||||
|
|
Loading…
Reference in New Issue