class TestOneOfPermission(BluebottleTestCase): def setUp(self): self.permission = OneOf(ResourceOwnerPermission, ResourcePermission)() self.user = BlueBottleUserFactory.create() self.initiative = InitiativeFactory.create(owner=self.user) self.user.groups.clear() def test_permission_owner(self): self.user.user_permissions.add( Permission.objects.get(codename='api_read_own_initiative')) self.assertTrue( self.permission.has_action_permission('GET', self.user, Initiative)) def test_permission(self): self.user.user_permissions.add( Permission.objects.get(codename='api_read_initiative')) self.assertTrue( self.permission.has_action_permission('GET', self.user, Initiative)) def test_object_permission(self): self.user.user_permissions.add( Permission.objects.get(codename='api_read_own_initiative')) self.assertTrue( self.permission.has_object_action_permission('GET', self.user, obj=self.initiative)) def test_object_permission_no_owner_permission(self): self.user.user_permissions.add( Permission.objects.get(codename='api_read_initiative')) self.user.save() self.assertTrue( self.permission.has_object_action_permission('GET', self.user, obj=self.initiative)) def test_object_permission_no_owner(self): self.initiative.owner = BlueBottleUserFactory.create() self.initiative.save() self.user.user_permissions.add( Permission.objects.get(codename='api_read_own_initiative')) self.assertFalse( self.permission.has_object_action_permission('GET', self.user, obj=self.initiative))
class TextWallpostList(WallpostOwnerFilterMixin, SetAuthorMixin, ListCreateAPIView): queryset = TextWallpost.objects.all() serializer_class = TextWallpostSerializer filter_class = WallpostFilter pagination_class = WallpostPagination permission_classes = ( OneOf(ResourcePermission, RelatedResourceOwnerPermission), RelatedManagementOrReadOnlyPermission, DonationOwnerPermission, ) def get_queryset(self, queryset=None): queryset = super(TextWallpostList, self).get_queryset() parent_type = self.request.query_params.get('parent_type', None) parent_id = self.request.query_params.get('parent_id', None) white_listed_apps = ['initiatives', 'assignments', 'events', 'funding'] content_type = ContentType.objects.filter( app_label__in=white_listed_apps).get(model=parent_type) queryset = queryset.filter(content_type=content_type) queryset = queryset.filter(object_id=parent_id) queryset = queryset.order_by('-created') return queryset def perform_create(self, serializer): self.check_object_permissions( self.request, serializer.Meta.model(author=self.request.user, **serializer.validated_data)) return super(TextWallpostList, self).perform_create(serializer)
class ManageOrderList(views.ListCreateAPIView): queryset = Order.objects.all() serializer_class = ManageOrderSerializer filter_fields = ('status', ) pagination_class = BluebottlePagination permission_classes = (OneOf(ResourcePermission, ResourceOwnerPermission), ) def get_queryset(self): queryset = super(ManageOrderList, self).get_queryset() if self.request.user.is_authenticated(): return queryset.filter(user=self.request.user) else: order_id = getattr(self.request.session, anonymous_order_id_session_key, 0) return queryset.filter(id=order_id) def perform_create(self, serializer): if self.request.user.is_authenticated(): serializer.save(user=self.request.user) else: serializer.save() self.request.session[ anonymous_order_id_session_key] = serializer.instance.id self.request.session.save()
class ActivityList(JsonApiViewMixin, ListAPIView): queryset = Activity.objects.select_related( 'owner', 'initiative', 'initiative__owner', 'initiative__location', 'initiative__theme', 'initiative__place', 'initiative__image', 'initiative__activity_manager', 'initiative__location__country', 'initiative__organization', ) serializer_class = ActivityListSerializer model = Activity filter_backends = (ActivitySearchFilter, ) permission_classes = (OneOf(ResourcePermission, ActivityOwnerPermission), ) prefetch_for_includes = { 'initiative': ['initiative'], 'location': ['location'], 'country': ['country'], 'owner': ['owner'], }
class BaseTaskList(ListCreateAPIView): queryset = Task.objects.all() pagination_class = TaskPreviewPagination permission_classes = (OneOf(ResourcePermission, TaskPermission), ) def get_queryset(self): qs = super(BaseTaskList, self).get_queryset() user = self.request.user model = super(BaseTaskList, self).model permission = '{}.api_read_{}'.format(model._meta.app_label, model._meta.model_name) if not self.request.user.has_perm(permission): qs = qs.filter( Q(project__owner=user) | Q(project__task_manager=user)) return qs def perform_create(self, serializer): if serializer.validated_data['project'].status.slug in ( 'closed', 'done-complete', 'done-incomplete', 'voting-done'): raise serializers.ValidationError( 'It is not allowed to add tasks to closed projects') self.check_object_permissions( self.request, serializer.Meta.model(**serializer.validated_data)) serializer.validated_data['deadline'] = get_midnight_datetime( serializer.validated_data['deadline']) serializer.save(author=self.request.user)
class WallpostList(WallpostOwnerFilterMixin, ListAPIView): queryset = Wallpost.objects.all() serializer_class = WallpostSerializer pagination_class = BluebottlePagination permission_classes = (OneOf(ResourcePermission, RelatedResourceOwnerPermission), RelatedManagementOrReadOnlyPermission) def get_queryset(self, queryset=queryset): queryset = super(WallpostList, self).get_queryset() # Some custom filtering projects slugs. parent_type = self.request.query_params.get('parent_type', None) parent_id = self.request.query_params.get('parent_id', None) if parent_type == 'project': content_type = ContentType.objects.get_for_model(Project) else: white_listed_apps = ['projects', 'tasks', 'fundraisers'] content_type = ContentType.objects.filter( app_label__in=white_listed_apps).get(model=parent_type) queryset = queryset.filter(content_type=content_type) if parent_type == 'project' and parent_id: try: project = Project.objects.get(slug=parent_id) except Project.DoesNotExist: return Wallpost.objects.none() queryset = queryset.filter(object_id=project.id) else: queryset = queryset.filter(object_id=parent_id) queryset = queryset.order_by('-created') return queryset
class MediaWallpostPhotoList(OwnerListViewMixin, SetAuthorMixin, ListCreateAPIView): queryset = MediaWallpostPhoto.objects.all() serializer_class = MediaWallpostPhotoSerializer pagination_class = MediaWallpostPhotoPagination permission_classes = (OneOf(ResourcePermission, ResourceOwnerPermission), ) owner_filter_field = 'author' def create(self, request, *args, **kwargs): # FIXME """ Work around browser issues. Adding photos to a wallpost works correctly in Chrome. Firefox (at least FF 24) sends the ```mediawallpost``` value to Django with the value 'null', which is then interpreted as a string in Django. This is incorrect behaviour, as ```mediawallpost``` is a relation. Eventually, this leads to HTTP400 errors, effectively breaking photo uploads in FF. The quick fix is detecting this incorrect 'null' string in ```request.POST``` and setting it to an empty string. ```request.POST``` is mutable because of the multipart nature. NOTE: This is something that should be fixed in the Ember app or maybe even Ember itself. """ post = request.POST.get('mediawallpost', False) if post and post == u'null': request.POST['mediawallpost'] = u'' return super(MediaWallpostPhotoList, self).create(request, *args, **kwargs)
class RewardDetail(RetrieveUpdateDestroyAPIView): queryset = Reward.objects.all() serializer_class = RewardSerializer permission_classes = (OneOf(ResourcePermission, RelatedResourceOwnerPermission), NoDonationsOrReadOnly)
class ProjectImageCreate(CreateAPIView): permission_classes = ( OneOf(ResourcePermission, RelatedResourceOwnerPermission), ) queryset = ProjectImage.objects.all() serializer_class = ProjectImageSerializer
class ReactionList(OwnerListViewMixin, SetAuthorMixin, ListCreateAPIView): queryset = Reaction.objects.all() serializer_class = ReactionSerializer permission_classes = (OneOf(ResourcePermission, ResourceOwnerPermission), ) pagination_class = BluebottlePagination filter_fields = ('wallpost', ) owner_filter_field = 'author'
class MyTaskDetail(RetrieveUpdateDestroyAPIView): queryset = Task.objects.all() permission_classes = (OneOf(ResourcePermission, ResourceOwnerPermission), ) serializer_class = MyTasksSerializer def perform_update(self, serializer): serializer.validated_data['deadline'] = get_midnight_datetime( serializer.validated_data['deadline']) serializer.save()
class ProjectPreviewDetail(RetrieveAPIView): queryset = Project.objects.all() serializer_class = ProjectPreviewSerializer lookup_field = 'slug' permission_classes = (OneOf(ResourcePermission, ResourceOwnerPermission), ) def get_queryset(self): qs = super(ProjectPreviewDetail, self).get_queryset() return qs
class ImpactGoalList(JsonApiViewMixin, CreateAPIView): queryset = ImpactGoal.objects.filter() related_permission_classes = { 'activity': [ OneOf(ResourcePermission, ActivityOwnerPermission), ] } permission_classes = [] serializer_class = ImpactGoalSerializer
class ImpactGoalDetail(JsonApiViewMixin, RetrieveUpdateDestroyAPIView): queryset = ImpactGoal.objects.filter() related_permission_classes = { 'activity': [ OneOf(ResourcePermission, ActivityOwnerPermission), ] } permission_classes = [] serializer_class = ImpactGoalSerializer
class ManageProjectDocumentDetail(RetrieveUpdateDestroyAPIView): queryset = ProjectDocument.objects.all() serializer_class = ProjectDocumentSerializer pagination_class = ManageProjectDocumentPagination filter = ('project', ) permission_classes = (OneOf(ResourcePermission, RelatedResourceOwnerPermission), ) def perform_update(self, serializer): serializer.save(author=self.request.user, ip_address=get_client_ip(self.request))
class TaskFileList(OwnerListViewMixin, ListCreateAPIView): queryset = TaskFile.objects.all() serializer_class = TaskFileSerializer pagination_class = TaskPagination filter_fields = ('task', ) permission_classes = (OneOf(ResourcePermission, ResourceOwnerPermission), ) owner_filter_field = 'author' def perform_create(self, serializer): serializer.save(author=self.request.user)
class ApplicantDetail(JsonApiViewMixin, AutoPrefetchMixin, RetrieveUpdateAPIView): queryset = Applicant.objects.all() serializer_class = ApplicantSerializer permission_classes = (OneOf(ResourcePermission, ResourceOwnerPermission, ApplicantPermission), ) prefetch_for_includes = { 'activity': ['activity'], 'user': ['user'], 'document': ['document'], }
class ProjectList(OwnerListViewMixin, ListAPIView): queryset = Project.objects.all() pagination_class = BluebottlePagination serializer_class = ProjectSerializer owner_filter_field = 'owner' permission_classes = (OneOf(ResourcePermission, ResourceOwnerPermission), ) def get_queryset(self): qs = super(ProjectList, self).get_queryset() status = self.request.query_params.get('status', None) if status: qs = qs.filter(Q(status_id=status)) return qs.filter(status__viewable=True)
class MediaWallpostList(TextWallpostList, SetAuthorMixin): queryset = MediaWallpost.objects.all() serializer_class = MediaWallpostSerializer filter_class = WallpostFilter pagination_class = WallpostPagination permission_classes = (OneOf(ResourcePermission, RelatedResourceOwnerPermission), RelatedManagementOrReadOnlyPermission) def perform_create(self, serializer): self.check_object_permissions( self.request, serializer.Meta.model(**serializer.validated_data)) return super(MediaWallpostList, self).perform_create(serializer)
class RelatedActivityImageList(JsonApiViewMixin, AutoPrefetchMixin, CreateAPIView): def get_queryset(self): return RelatedImage.objects.filter( content_type=ContentType.objects.get_for_model(Activity)) serializer_class = RelatedActivityImageSerializer related_permission_classes = { 'content_object': [ OneOf(ResourcePermission, ActivityOwnerPermission), ] } permission_classes = []
class ActivityDetail(JsonApiViewMixin, AutoPrefetchMixin, RetrieveUpdateDestroyAPIView): queryset = Activity.objects.all() serializer_class = ActivitySerializer model = Activity lookup_field = 'pk' permission_classes = (OneOf(ResourcePermission, ActivityOwnerPermission), ) prefetch_for_includes = { 'initiative': ['initiative'], 'location': ['location'], 'owner': ['owner'], 'contributions': ['contributions'] }
class EventDetail(JsonApiViewMixin, AutoPrefetchMixin, RetrieveUpdateAPIView): queryset = Event.objects.all() serializer_class = EventSerializer permission_classes = ( ActivityStatusPermission, OneOf(ResourcePermission, ActivityOwnerPermission), ) prefetch_for_includes = { 'initiative': ['initiative'], 'location': ['location'], 'owner': ['owner'], 'contributions': ['contributions'], }
class ProjectPreviewList(ProjectListSearchMixin, OwnerListViewMixin, ListAPIView): queryset = Project.objects.all() pagination_class = ProjectPagination serializer_class = ProjectPreviewSerializer owner_filter_field = 'owner' permission_classes = (OneOf(ResourcePermission, ResourceOwnerPermission), ) def get_queryset(self): qs = super(ProjectPreviewList, self).get_queryset() query = self.request.query_params qs = self.search(qs, query) qs.select_related('task') return qs.filter(status__viewable=True)
class ManageProjectDetail(RetrieveUpdateAPIView): queryset = Project.objects.all() permission_classes = (ResourceOwnerPermission, OneOf(IsEditableOrReadOnly, CanEditOwnRunningProjects)) serializer_class = ManageProjectSerializer lookup_field = 'slug' def get_object(self): # Call the superclass object = super(ManageProjectDetail, self).get_object() # store the current state self.current_status = object.status return object
class RewardList(OwnerListViewMixin, ListCreateAPIView): queryset = Reward.objects.all() serializer_class = RewardSerializer permission_classes = (OneOf(ResourcePermission, RelatedResourceOwnerPermission), ) pagination_class = RewardPagination owner_filter_field = 'project__owner' def get_queryset(self): qs = super(RewardList, self).get_queryset() project_slug = self.request.query_params.get('project', None) if project_slug: qs = qs.filter(project__slug=project_slug) return qs
class AssignmentList(JsonApiViewMixin, AutoPrefetchMixin, ListCreateAPIView): queryset = Assignment.objects.all() serializer_class = AssignmentSerializer permission_classes = ( ActivityTypePermission, OneOf(ResourcePermission, ActivityOwnerPermission), ) def perform_create(self, serializer): serializer.save(owner=self.request.user) prefetch_for_includes = { 'initiative': ['initiative'], 'location': ['location'], 'owner': ['owner'], }
class InitiativeList(JsonApiViewMixin, AutoPrefetchMixin, ListCreateAPIView): queryset = Initiative.objects.prefetch_related('place', 'location', 'owner', 'activity_manager', 'image', 'categories', 'theme') def get_serializer_class(self): if self.request.method == 'POST' or self.request.GET.get( 'filter[owner.id]'): return InitiativeSerializer else: return InitiativeListSerializer permission_classes = (OneOf(ResourcePermission, ResourceOwnerPermission), ) filter_backends = (InitiativeSearchFilter, ) filter_fields = { 'owner__id': ( 'exact', 'in', ), } prefetch_for_includes = { 'owner': ['owner'], 'reviewer': ['reviewer'], 'promoter': ['promoter'], 'activity_manager': ['activity_manager'], 'theme': ['theme'], 'place': ['place'], 'location': ['location'], 'categories': ['categories'], 'image': ['image'], 'organization': ['organization'], 'organization_contact': ['organization_contact'], 'activities': ['activities'], } def perform_create(self, serializer): self.check_object_permissions( self.request, serializer.Meta.model(owner=self.request.user, **serializer.validated_data)) serializer.save(owner=self.request.user)
class ApplicantList(JsonApiViewMixin, AutoPrefetchMixin, ListCreateAPIView): queryset = Applicant.objects.all() serializer_class = ApplicantSerializer permission_classes = (OneOf(ResourcePermission, ResourceOwnerPermission), ) prefetch_for_includes = { 'assignment': ['assignment'], 'user': ['user'], 'document': ['document'], } def perform_create(self, serializer): self.check_related_object_permissions( self.request, serializer.Meta.model(**serializer.validated_data)) self.check_object_permissions( self.request, serializer.Meta.model(**serializer.validated_data)) serializer.save(user=self.request.user)
class FundingDetail(JsonApiViewMixin, AutoPrefetchMixin, RetrieveUpdateAPIView): queryset = Funding.objects.select_related( 'initiative', 'initiative__owner', ).prefetch_related('rewards') serializer_class = FundingSerializer permission_classes = ( ActivityStatusPermission, OneOf(ResourcePermission, ActivityOwnerPermission), ) prefetch_for_includes = { 'initiative': ['initiative'], 'owner': ['owner'], 'rewards': ['reward'], 'budgetlines': ['budgetlines'], 'payment_methods': ['payment_methods'], 'fundraisers': ['fundraisers'] }
class FundingList(JsonApiViewMixin, AutoPrefetchMixin, ListCreateAPIView): queryset = Funding.objects.all() serializer_class = FundingListSerializer permission_classes = ( ActivityTypePermission, OneOf(ResourcePermission, ActivityOwnerPermission), ) prefetch_for_includes = { 'initiative': ['initiative'], 'owner': ['owner'], } def perform_create(self, serializer): self.check_related_object_permissions( self.request, serializer.Meta.model(**serializer.validated_data)) self.check_object_permissions( self.request, serializer.Meta.model(**serializer.validated_data)) serializer.save(owner=self.request.user)