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)
class ReviewerExtensionViewSet(CORSMixin, SlugOrIdMixin, MarketplaceView, ListModelMixin, RetrieveModelMixin, GenericViewSet): authentication_classes = [ RestOAuthAuthentication, RestSharedSecretAuthentication, RestAnonymousAuthentication ] cors_allowed_methods = ('get', 'post') permission_classes = (ByHttpMethod({ 'options': AllowAny, 'post': GroupPermission('ContentTools', 'AddonReview'), 'get': GroupPermission('ContentTools', 'AddonReview'), }), ) queryset = Extension.objects.without_deleted().pending() serializer_class = ExtensionSerializer @list_route(queryset=Extension.objects.without_deleted().public(). pending_with_versions()) def updates(self, *args, **kwargs): qs = self.get_queryset() page = self.paginate_queryset(qs) serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data)
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()
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)
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()
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 if not app.is_public() and not check_addon_ownership( self.request, app): # App owners and admin can see the app even if it's not public. # Regular users or anonymous users can't. raise PermissionDenied('The app requested is not public') 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})