diff --git a/lib/main/base_views.py b/lib/main/base_views.py index 6fd930e306..9c83dd331f 100644 --- a/lib/main/base_views.py +++ b/lib/main/base_views.py @@ -28,6 +28,36 @@ class BaseList(generics.ListCreateAPIView): def get_queryset(self): return self._get_queryset().filter(active=True) +class BaseSubList(BaseList): + + ''' used for subcollections with an overriden post ''' + + def post(self, request, *args, **kwargs): + + parent_id = kwargs['pk'] + sub_id = request.DATA.get('id') + main = self.__class__.parent_model.objects.get(pk=parent_id) + subs = self.__class__.model.objects.filter(pk=sub_id) + if len(subs) != 1: + return Response(status=status.HTTP_400_BAD_REQUEST) + sub = subs[0] + relationship = getattr(main, self.__class__.relationship) + + if not 'disassociate' in request.DATA: + if not request.user.is_superuser or not self.__class__.parent_model.can_user_attach(request.user, main, sub, self.__class__.relationship): + print "cond1" + raise PermissionDenied() + if sub in relationship.all(): + return Response(status=status.HTTP_409_CONFLICT) + relationship.add(sub) + else: + if not request.user.is_superuser and not self.__class__.parent_model.can_user_unattach(request.user, main, sub, self.__class__.relationship): + print "cond2" + raise PermissionDenied() + relationship.remove(sub) + return Response(status=status.HTTP_204_NO_CONTENT) + + class BaseDetail(generics.RetrieveUpdateDestroyAPIView): def pre_save(self, obj): diff --git a/lib/main/models/__init__.py b/lib/main/models/__init__.py index 36e040ca5f..89bb0bc52c 100644 --- a/lib/main/models/__init__.py +++ b/lib/main/models/__init__.py @@ -32,7 +32,7 @@ class CommonModel(models.Model): return unicode(self.name) @classmethod - def can_user_administrate(cls, user): + def can_user_administrate(cls, user, obj): raise exceptions.NotImplementedError() @classmethod @@ -43,6 +43,20 @@ class CommonModel(models.Model): def can_user_read(cls, user, obj): raise exceptions.NotImplementedError() + @classmethod + def can_user_attach(cls, user, obj, sub_obj, relationship): + if relationship in [ 'projects', 'admins', 'users' ]: + if not sub_obj.can_user_read(user, sub_obj): + return False + return cls.can_user_administrate(user, obj) + else: + raise Exception("unknown relationship type: %s" % relationship) + return False + + @classmethod + def can_user_unattach(cls, user, obj, relationship): + return cls.can_user_attach(user, obj, relationship) + class Tag(models.Model): ''' @@ -97,11 +111,16 @@ class Organization(CommonModel): @classmethod def can_user_administrate(cls, user, obj): - return user in obj.admins.all() + if user.is_superuser: + return True + rc = user in obj.admins.all() + return rc @classmethod def can_user_read(cls, user, obj): - return cls.can_user_administrate(user,obj) or user in obj.users.all() + rc = cls.can_user_administrate(user,obj) or user in obj.users.all() + return rc + @classmethod def can_user_delete(cls, user, obj): @@ -205,12 +224,22 @@ class Project(CommonModel): @classmethod def can_user_administrate(cls, user, obj): - organizations = Organization.filter(admins__in = [ user ], projects__in = [ obj ]) - organizations = self.organizations() + if user.is_superuser: + return True + organizations = Organization.objects.filter(admins__in = [ user ], projects__in = [ obj ]) for org in organizations: if org in project.organizations(): return True - return True + return False + + @classmethod + def can_user_read(cls, user, obj): + if cls.can_user_administrate(user,obj): + return True + # and also if I happen to be on a team inside the project + # FIXME: add this too + return False + class Permission(CommonModel): ''' diff --git a/lib/main/views.py b/lib/main/views.py index 083f9c17ec..d218728e67 100644 --- a/lib/main/views.py +++ b/lib/main/views.py @@ -12,7 +12,7 @@ from rest_framework.response import Response from rest_framework import status import exceptions import datetime -from base_views import BaseList, BaseDetail +from base_views import BaseList, BaseDetail, BaseSubList class OrganizationsList(BaseList): @@ -88,9 +88,11 @@ class OrganizationsAdminsList(BaseList): ).distinct() -class OrganizationsProjectsList(BaseList): +class OrganizationsProjectsList(BaseSubList): model = Project + parent_model = Organization + relationship = 'projects' serializer_class = ProjectSerializer permission_classes = (CustomRbac,) @@ -109,43 +111,6 @@ class OrganizationsProjectsList(BaseList): teams__users__in = [ self.request.user ] ).distinct() - # BOOKMARK - def post(self, request, *args, **kwargs): - - # POST { pk: 7, disassociate: True } - - organization_id = kwargs['pk'] - project_id = request.DATA.get('id') - organization = Organization.objects.get(pk=organization_id) - projects = Project.objects.filter(pk=project_id) - if len(projects) != 1: - return Response(status=status.HTTP_400_BAD_REQUEST) - project = projects[0] - - # you can only add a project to an organization if you are a superuser or - # the person who created the project. TODO -- want to defer this question - # to the model. (FIXME) - - if not 'disassociate' in request.DATA: - # admin of another org can't add a project to their org - if not request.user.is_superuser or project.created_by == request.user: - raise PermissionDenied() - if project in organization.projects.all(): - return Response(status=status.HTTP_409_CONFLICT) - organization.projects.add(project) - else: - # to disassociate, be the org admin or a superuser - # FIXME: sprinkle these throughout the object layer & simplify - if not request.user.is_superuser and not project.can_user_administrate(request.user): - raise PermissionDenied() - organization.projects.remove(project) - # multiple attempts to delete the same thing aren't an error, we're cool - return Response(status=status.HTTP_204_NO_CONTENT) - - - - - class OrganizationsTagsList(BaseList): # FIXME: guts & tests pass @@ -156,20 +121,5 @@ class ProjectsDetail(BaseDetail): serializer_class = ProjectSerializer permission_classes = (CustomRbac,) -# #def item_permissions_check(self, request, obj): -# -# # to get, must be in a team assigned to this project -# # or be an org admin of an org this project is in -# -# raise exceptions.NotImplementedError() -# -# #is_admin = request.user in obj.admins.all() -# #is_user = request.user in obj.users.all() -# # -# #if request.method == 'GET': -# # return is_admin or is_user -# #elif request.method in [ 'PUT' ]: -# # return is_admin -# #return False