Example #1
0
 def setUp(self):
     self.get_permission = Mock
     self.patch_permission = Mock
     self.post_permission = Mock
     self.put_permission = Mock
     self.permission = ByHttpMethod({
         'get': self.get_permission,
     })
     self.set_permission_mock('get', True)
 def setUp(self):
     self.get_permission = Mock
     self.patch_permission = Mock
     self.post_permission = Mock
     self.put_permission = Mock
     self.permission = ByHttpMethod({"get": self.get_permission})
     self.set_permission_mock("get", True)
Example #3
0
class InAppProductViewSet(CORSMixin, MarketplaceView, ModelViewSet):
    serializer_class = InAppProductSerializer
    cors_allowed_methods = ('get', 'post', 'put', 'delete')
    permission_classes = [
        ByHttpMethod({
            'options': AllowAny,  # Needed for CORS.
            'get': AllowAuthor,
            'post': AllowAuthor,
            'put': AllowAuthor,
        })
    ]
    authentication_classes = [
        RestOAuthAuthentication, RestSharedSecretAuthentication,
        RestAnonymousAuthentication
    ]

    def destroy(self):
        raise NotImplemented('destroy is not allowed')

    def pre_save(self, in_app_product):
        in_app_product.webapp = self.get_app()

    def get_queryset(self):
        return InAppProduct.objects.filter(webapp=self.get_app())

    def get_app(self):
        if not hasattr(self, 'app'):
            app_slug = self.kwargs['app_slug']
            self.app = get_object_or_404(Webapp, app_slug=app_slug)
        return self.app

    def get_authors(self):
        return self.get_app().authors.all()
Example #4
0
 def setUp(self):
     self.get_permission = Mock
     self.patch_permission = Mock
     self.post_permission = Mock
     self.put_permission = Mock
     self.permission = ByHttpMethod({
         'get': self.get_permission,
     })
     self.set_permission_mock('get', True)
Example #5
0
class AppEscalate(_AppAction, CreateAPIView, DestroyAPIView):
    permission_classes = [ByHttpMethod({
        'options': AllowAny,
        'post': GroupPermission('Apps', 'Review'),
        'delete': GroupPermission('Apps', 'Edit'),
    })]
    verb = "escalate"

    def delete(self, request, pk, *a, **kw):
        app = self.get_object()
        handler = ReviewApp(request, app, app.latest_version, ())
        handler.set_data(request.QUERY_PARAMS)
        handler.process_clear_escalation()
        return Response()
Example #6
0
class TestByHttpMethodPermission(TestCase):
    def setUp(self):
        self.get_permission = Mock
        self.patch_permission = Mock
        self.post_permission = Mock
        self.put_permission = Mock
        self.permission = ByHttpMethod({
            'get': self.get_permission,
        })
        self.set_permission_mock('get', True)

    def set_permission_mock(self, method, value):
        mock = self.permission.method_permissions[method]
        mock.has_permission.return_value = value

    def set_object_permission_mock(self, method, value):
        mock = self.permission.method_permissions[method]
        mock.has_object_permission.return_value = value

    def test_get(self):
        self.request = RequestFactory().get('/')
        eq_(self.permission.has_permission(self.request, 'myview'), True)
        self.set_permission_mock('get', False)
        eq_(self.permission.has_permission(self.request, 'myview'), False)

    def test_get_obj(self):
        obj = Mock()
        self.request = RequestFactory().get('/')
        self.set_object_permission_mock('get', True)
        eq_(self.permission.has_object_permission(self.request, 'myview', obj),
            True)
        self.set_object_permission_mock('get', False)
        eq_(self.permission.has_object_permission(self.request, 'myview', obj),
            False)

    def test_missing_method(self):
        self.request = RequestFactory().post('/')
        eq_(self.permission.has_permission(self.request, 'myview'), False)

        obj = Mock()
        self.request = RequestFactory().post('/')
        eq_(self.permission.has_object_permission(self.request, 'myview', obj),
            False)

        self.request = RequestFactory().options('/')
        eq_(self.permission.has_permission(self.request, 'myview'), False)
Example #7
0
class TestByHttpMethodPermission(TestCase):
    def setUp(self):
        self.get_permission = Mock
        self.patch_permission = Mock
        self.post_permission = Mock
        self.put_permission = Mock
        self.permission = ByHttpMethod({
            'get': self.get_permission,
        })
        self.set_permission_mock('get', True)

    def set_permission_mock(self, method, value):
        mock = self.permission.method_permissions[method]
        mock.has_permission.return_value = value

    def set_object_permission_mock(self, method, value):
        mock = self.permission.method_permissions[method]
        mock.has_object_permission.return_value = value

    def test_get(self):
        self.request = RequestFactory().get('/')
        eq_(self.permission.has_permission(self.request, 'myview'), True)
        self.set_permission_mock('get', False)
        eq_(self.permission.has_permission(self.request, 'myview'), False)

    def test_get_obj(self):
        obj = Mock()
        self.request = RequestFactory().get('/')
        self.set_object_permission_mock('get', True)
        eq_(self.permission.has_object_permission(self.request, 'myview', obj),
            True)
        self.set_object_permission_mock('get', False)
        eq_(self.permission.has_object_permission(self.request, 'myview', obj),
            False)

    def test_missing_method(self):
        self.request = RequestFactory().post('/')
        eq_(self.permission.has_permission(self.request, 'myview'), False)

        obj = Mock()
        self.request = RequestFactory().post('/')
        eq_(self.permission.has_object_permission(self.request, 'myview', obj),
            False)

        self.request = RequestFactory().options('/')
        eq_(self.permission.has_permission(self.request, 'myview'), False)
Example #8
0
class InAppProductViewSet(CORSMixin, MarketplaceView, ModelViewSet):
    serializer_class = InAppProductSerializer
    cors_allowed_methods = ('get', 'post', 'put', 'patch', 'delete')
    cors_allowed_headers = ('content-type', 'accept', 'x-fxpay-version')
    lookup_field = 'guid'
    permission_classes = [
        ByHttpMethod({
            'options': AllowAny,  # Needed for CORS.
            'get': AllowAny,
            'post': AllowAuthor,
            'put': AllowAuthor,
            'patch': AllowAuthor,
        })
    ]
    authentication_classes = [
        RestOAuthAuthentication, RestSharedSecretAuthentication,
        RestAnonymousAuthentication
    ]
    filter_backends = (MktFilterBackend, )
    filter_fields = filter_munge = ('active', )

    def destroy(self):
        raise NotImplemented('destroy is not allowed')

    def pre_save(self, in_app_product):
        in_app_product.webapp = self.get_app()

    def get_queryset(self):
        return InAppProduct.objects.filter(webapp=self.get_app())

    def get_app(self):
        origin = self.kwargs['origin']
        if not hasattr(self, 'app'):
            if origin.startswith('marketplace:'):
                lookup = dict(guid=origin.replace('marketplace:', '', 1))
            else:
                lookup = dict(app_domain=origin)
            self.app = get_object_or_404(Webapp, **lookup)
        return self.app

    def get_authors(self):
        return self.get_app().authors.all()
Example #9
0
class RatingViewSet(CORSMixin, MarketplaceView, ModelViewSet):
    # Unfortunately, the model class name for ratings is "Review".
    queryset = Review.objects.valid()
    cors_allowed_methods = ('get', 'post', 'put', 'delete')
    permission_classes = [
        ByHttpMethod({
            'options':
            AllowAny,  # Needed for CORS.
            'get':
            AllowAny,
            'post':
            IsAuthenticated,
            'put':
            AnyOf(AllowOwner, GroupPermission('Addons', 'Edit')),
            'delete':
            AnyOf(AllowOwner, AllowRelatedAppOwner,
                  GroupPermission('Users', 'Edit'),
                  GroupPermission('Addons', 'Edit')),
        })
    ]
    authentication_classes = [
        RestOAuthAuthentication, RestSharedSecretAuthentication,
        RestAnonymousAuthentication
    ]
    serializer_class = RatingSerializer
    paginator_class = RatingPaginator

    # FIXME: Add throttling ? Original tastypie version didn't have it...

    def get_queryset(self):
        qs = super(RatingViewSet, self).get_queryset()
        # Mature regions show only reviews from within that region.
        # FIXME: what is client_data, how is it filled ? There was no tests
        # for this.
        if not self.request.REGION.adolescent:
            qs = qs.filter(client_data__region=self.request.REGION.id)
        return qs

    def filter_queryset(self, queryset):
        """
        Custom filter method allowing us to filter on app slug/pk and user pk
        (or the special user value "mine"). A full FilterSet is overkill here.
        """
        filters = {}
        app = self.request.GET.get('app')
        user = self.request.GET.get('user')
        if app:
            self.app = self.get_app(app)
            filters['addon'] = self.app
        if user:
            filters['user'] = self.get_user(user)

        if filters:
            queryset = queryset.filter(**filters)
        return queryset

    def get_user(self, ident):
        pk = ident
        if pk == 'mine':
            user = amo.get_user()
            if not user:
                # You must be logged in to use "mine".
                raise NotAuthenticated()
            pk = user.pk
        return pk

    def get_app(self, ident):
        try:
            app = Webapp.objects.by_identifier(ident)
        except Webapp.DoesNotExist:
            raise Http404
        current_region = get_region()
        if ((not app.is_public() or not app.listed_in(region=current_region))
                and not check_addon_ownership(self.request, app)):
            # App owners and admin can see the app even if it's not public
            # or not available in the current region. Regular users or
            # anonymous users can't.
            raise PermissionDenied('The app requested is not public or not '
                                   'available in region "%s".' %
                                   current_region.slug)
        return app

    def list(self, request, *args, **kwargs):
        response = super(RatingViewSet, self).list(request, *args, **kwargs)
        app = getattr(self, 'app', None)
        if app:
            user, info = self.get_extra_data(app, request.amo_user)
            response.data['user'] = user
            response.data['info'] = info
        return response

    def destroy(self, request, *args, **kwargs):
        obj = self.get_object()
        amo.log(amo.LOG.DELETE_REVIEW, obj.addon, obj)
        log.debug('[Review:%s] Deleted by %s' %
                  (obj.pk, self.request.amo_user.id))
        return super(RatingViewSet, self).destroy(request, *args, **kwargs)

    def post_save(self, obj, created=False):
        app = obj.addon
        if created:
            amo.log(amo.LOG.ADD_REVIEW, app, obj)
            log.debug('[Review:%s] Created by user %s ' %
                      (obj.pk, self.request.amo_user.id))
            record_action('new-review', self.request, {'app-id': app.id})
        else:
            amo.log(amo.LOG.EDIT_REVIEW, app, obj)
            log.debug('[Review:%s] Edited by %s' %
                      (obj.pk, self.request.amo_user.id))

    def partial_update(self, *args, **kwargs):
        # We don't need/want PATCH for now.
        raise MethodNotAllowed('PATCH is not supported for this endpoint.')

    def get_extra_data(self, app, amo_user):
        extra_user = None

        if amo_user and not amo_user.is_anonymous():
            if app.is_premium():
                # If the app is premium, you need to purchase it to rate it.
                can_rate = app.has_purchased(amo_user)
            else:
                # If the app is free, you can not be one of the authors.
                can_rate = not app.has_author(amo_user)

            filters = {'addon': app, 'user': amo_user}
            if app.is_packaged:
                filters['version'] = app.current_version

            extra_user = {
                'can_rate': can_rate,
                'has_rated': Review.objects.valid().filter(**filters).exists()
            }

        extra_info = {
            'average': app.average_rating,
            'slug': app.app_slug,
            'current_version': getattr(app.current_version, 'version', None)
        }

        return extra_user, extra_info

    @action(methods=['POST'], permission_classes=[AllowAny])
    def flag(self, request, pk=None):
        self.kwargs[self.lookup_field] = pk
        self.get_object()  # Will check that the Review instance is valid.
        request._request.CORS = RatingFlagViewSet.cors_allowed_methods
        view = RatingFlagViewSet.as_view({'post': 'create'})
        return view(request, *self.args, **{'review': pk})
Example #10
0
class RatingViewSet(CORSMixin, MarketplaceView, ModelViewSet):
    # Unfortunately, the model class name for ratings is "Review".
    # We prefetch 'version' because it's often going to be similar, and select
    # related 'user' to avoid extra queries.
    queryset = (Review.objects.valid()
                .prefetch_related('version')
                .select_related('user'))
    cors_allowed_methods = ('get', 'post', 'put', 'delete')
    permission_classes = [ByHttpMethod({
        'options': AllowAny,  # Needed for CORS.
        'get': AllowAny,
        'head': AllowAny,
        'post': IsAuthenticated,
        'put': AnyOf(AllowOwner,
                     GroupPermission('Apps', 'Edit')),
        'delete': AnyOf(AllowOwner,
                        AllowRelatedAppOwner,
                        GroupPermission('Users', 'Edit'),
                        GroupPermission('Apps', 'Edit')),
    })]
    authentication_classes = [RestOAuthAuthentication,
                              RestSharedSecretAuthentication,
                              RestAnonymousAuthentication]
    serializer_class = RatingSerializer

    def paginator_class(self, *args, **kwargs):
        paginator = super(RatingViewSet, self).paginator_class(*args, **kwargs)
        if hasattr(self, 'app'):
            # If an app is passed, we want the paginator count to match the
            # number of reviews on the app, without doing an extra query.
            paginator._count = self.app.total_reviews
        return paginator

    # FIXME: Add throttling ? Original tastypie version didn't have it...

    def filter_queryset(self, queryset):
        """
        Custom filter method allowing us to filter on app slug/pk and user pk
        (or the special user value "mine"). A full FilterSet is overkill here.
        """
        filters = Q()
        app = self.request.GET.get('app')
        user = self.request.GET.get('user')
        lang = self.request.GET.get('lang')
        match_lang = self.request.GET.get('match_lang')
        if app:
            self.app = self.get_app(app)
            filters &= Q(addon=self.app)
        if user:
            filters &= Q(user=self.get_user(user))
        elif lang and match_lang == '1':
            filters &= Q(lang=lang)

        if filters:
            queryset = queryset.filter(filters)
        return queryset

    def get_user(self, ident):
        pk = ident
        if pk == 'mine':
            user = mkt.get_user()
            if not user or not user.is_authenticated():
                # You must be logged in to use "mine".
                raise NotAuthenticated()
            pk = user.pk
        return pk

    def get_app(self, ident):
        try:
            app = Webapp.objects.by_identifier(ident)
        except Webapp.DoesNotExist:
            raise Http404
        current_region = get_region()
        if ((not app.is_public() or
             not app.listed_in(region=current_region)) and
                not check_addon_ownership(self.request, app)):
            # App owners and admin can see the app even if it's not public
            # or not available in the current region. Regular users or
            # anonymous users can't.
            raise PermissionDenied(
                'The app requested is not public or not available in region '
                '"%s".' % current_region.slug)
        return app

    def list(self, request, *args, **kwargs):
        response = super(RatingViewSet, self).list(request, *args, **kwargs)
        app = getattr(self, 'app', None)
        if app:
            user, info = self.get_extra_data(app, request.user)
            response.data['user'] = user
            response.data['info'] = info
        return response

    def destroy(self, request, *args, **kwargs):
        obj = self.get_object()
        mkt.log(mkt.LOG.DELETE_REVIEW, obj.addon, obj,
                details=dict(title=unicode(obj.title),
                             body=unicode(obj.body),
                             addon_id=obj.addon.id,
                             addon_title=unicode(obj.addon.name)))
        log.debug('[Review:%s] Deleted by %s' %
                  (obj.pk, self.request.user.id))
        return super(RatingViewSet, self).destroy(request, *args, **kwargs)

    def post_save(self, obj, created=False):
        app = obj.addon
        if created:
            mkt.log(mkt.LOG.ADD_REVIEW, app, obj)
            log.debug('[Review:%s] Created by user %s ' %
                      (obj.pk, self.request.user.id))
            record_action('new-review', self.request, {'app-id': app.id})
        else:
            mkt.log(mkt.LOG.EDIT_REVIEW, app, obj)
            log.debug('[Review:%s] Edited by %s' %
                      (obj.pk, self.request.user.id))

    def partial_update(self, *args, **kwargs):
        # We don't need/want PATCH for now.
        raise MethodNotAllowed('PATCH is not supported for this endpoint.')

    def get_extra_data(self, app, user):
        extra_user = None

        if user.is_authenticated():
            if app.is_premium():
                # If the app is premium, you need to purchase it to rate it.
                can_rate = app.has_purchased(user)
            else:
                # If the app is free, you can not be one of the authors.
                can_rate = not app.has_author(user)

            filters = {
                'addon': app,
                'user': user
            }
            if app.is_packaged:
                filters['version'] = app.current_version

            extra_user = {
                'can_rate': can_rate,
                'has_rated': Review.objects.valid().filter(**filters).exists()
            }

        extra_info = {
            'average': app.average_rating,
            'slug': app.app_slug,
            'total_reviews': app.total_reviews,
            'current_version': getattr(app.current_version, 'version', None)
        }

        return extra_user, extra_info

    @action(methods=['POST'], permission_classes=[AllowAny])
    def flag(self, request, pk=None):
        self.kwargs[self.lookup_field] = pk
        self.get_object()  # Will check that the Review instance is valid.
        request._request.CORS = RatingFlagViewSet.cors_allowed_methods
        view = RatingFlagViewSet.as_view({'post': 'create'})
        return view(request, *self.args, **{'review': pk})