Пример #1
0
 def setUp(self):
     self.permission = GroupPermission('Drinkers', 'Beer')
     self.obj = Mock()
     self.profile = UserProfile.objects.get(pk=2519)
     self.anonymous = AnonymousUser()
     self.request = RequestFactory().get('/')
     self.request.user = self.anonymous
Пример #2
0
class VersionViewSet(CORSMixin, mixins.RetrieveModelMixin,
                     mixins.UpdateModelMixin, viewsets.GenericViewSet):
    queryset = Version.objects.exclude(addon__status=mkt.STATUS_DELETED)
    serializer_class = VersionSerializer
    authentication_classes = [
        RestOAuthAuthentication, RestSharedSecretAuthentication,
        RestAnonymousAuthentication
    ]
    permission_classes = [
        AnyOf(AllowReadOnlyIfPublic, AllowRelatedAppOwner,
              GroupPermission('Apps', 'Review'), GroupPermission('Admin', '%'))
    ]
    cors_allowed_methods = ['get', 'patch', 'put']

    def update(self, request, *args, **kwargs):
        """
        Allow a version's features to be updated.
        """
        obj = self.get_object()

        # Update features if they are provided.
        if 'features' in request.DATA:

            # Raise an exception if any invalid features are passed.
            invalid = [
                f for f in request.DATA['features']
                if f.upper() not in APP_FEATURES.keys()
            ]
            if any(invalid):
                raise ParseError('Invalid feature(s): %s' % ', '.join(invalid))

            # Update the value of each feature (note: a feature not present in
            # the form data is assumed to be False)
            data = {}
            for key, name in APP_FEATURES.items():
                field_name = 'has_' + key.lower()
                data[field_name] = key.lower() in request.DATA['features']
            obj.features.update(**data)

            del request.DATA['features']

        return super(VersionViewSet, self).update(request, *args, **kwargs)

    @action(methods=['PATCH'],
            cors_allowed_methods=VersionStatusViewSet.cors_allowed_methods)
    def status(self, request, *args, **kwargs):
        self.queryset = Version.with_deleted.all()
        kwargs['version'] = self.get_object()
        view = VersionStatusViewSet.as_view({'patch': 'update'})
        return view(request, *args, **kwargs)
Пример #3
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()
Пример #4
0
class FeedAppViewSet(CORSMixin, MarketplaceView, SlugOrIdMixin,
                     ImageURLUploadMixin):
    """
    A viewset for the FeedApp class, which highlights a single app and some
    additional metadata (e.g. a review or a screenshot).
    """
    authentication_classes = [
        RestOAuthAuthentication, RestSharedSecretAuthentication,
        RestAnonymousAuthentication
    ]
    permission_classes = [
        AnyOf(AllowReadOnly, GroupPermission('Feed', 'Curate'))
    ]
    filter_backends = (OrderingFilter, )
    queryset = FeedApp.objects.all()
    cors_allowed_methods = ('get', 'delete', 'post', 'put', 'patch')
    serializer_class = FeedAppSerializer

    image_fields = (('background_image_upload_url', 'image_hash', ''), )

    def list(self, request, *args, **kwargs):
        page = self.paginate_queryset(self.filter_queryset(
            self.get_queryset()))
        serializer = self.get_pagination_serializer(page)
        return response.Response(serializer.data)
Пример #5
0
class GlobalStats(CORSMixin, APIView):
    authentication_classes = (RestOAuthAuthentication,
                              RestSharedSecretAuthentication)
    cors_allowed_methods = ['get']
    permission_classes = [GroupPermission('Stats', 'View')]

    def get(self, request, metric):
        if metric not in STATS:
            raise http.Http404('No metric by that name.')

        stat = STATS[metric]

        # Perform form validation.
        form = StatsForm(request.GET)
        if not form.is_valid():
            raise ParseError(dict(form.errors.items()))

        qs = form.cleaned_data

        dimensions = {}
        if 'dimensions' in stat:
            for key, default in stat['dimensions'].items():
                val = request.GET.get(key, default)
                if val is not None:
                    # Avoid passing kwargs to the monolith client when the
                    # dimension is None to avoid facet filters being applied.
                    dimensions[key] = request.GET.get(key, default)

        return Response(
            _get_monolith_data(stat, qs.get('start'), qs.get('end'),
                               qs.get('interval'), dimensions))
Пример #6
0
 def setUp(self):
     self.permission = GroupPermission('Drinkers', 'Beer')
     self.obj = Mock()
     self.profile = UserProfile.objects.get(pk=2519)
     self.anonymous = AnonymousUser()
     self.request = RequestFactory().get('/')
     self.request.user = self.anonymous
Пример #7
0
class ReviewersSearchView(SearchView):
    cors_allowed_methods = ['get']
    authentication_classes = [
        RestSharedSecretAuthentication, RestOAuthAuthentication
    ]
    permission_classes = [GroupPermission('Apps', 'Review')]
    form_class = ApiReviewersSearchForm
    serializer_class = ReviewersESAppSerializer

    def search(self, request):
        # Parse form.
        form = self.form_class(request.GET if request else None)
        if not form.is_valid():
            raise form_errors(form)
        form_data = form.cleaned_data

        # Status filter.
        data = search_form_to_es_fields(form_data)
        if form_data.get('status') != 'any':
            data.update(status=form_data.get('status'))

        # Do filter.
        sq = apply_reviewer_filters(request,
                                    WebappIndexer.search(),
                                    data=form_data)
        sq = WebappIndexer.get_app_filter(request, data, sq=sq, no_filter=True)

        page = self.paginate_queryset(sq)
        return self.get_pagination_serializer(page), request.GET.get('q', '')
Пример #8
0
class GlobalStatsTotal(CORSMixin, APIView, StatsTotalBase):
    authentication_classes = (RestOAuthAuthentication,
                              RestSharedSecretAuthentication)
    cors_allowed_methods = ['get']
    permission_classes = [GroupPermission('Stats', 'View')]
    slug_field = 'app_slug'

    def get(self, request):
        client = self.get_client()

        # Note: We have to do this as separate requests so that if one fails
        # the rest can still be returned.
        data = {}
        for metric, stat in STATS_TOTAL.items():
            data[metric] = {}
            query = self.get_query(metric, stat['metric'])

            try:
                resp = client.raw(query)
            except ValueError as e:
                log.info('Received value error from monolith client: %s' % e)
                continue

            self.process_response(resp, data)

        return Response(data)
Пример #9
0
class AppStatsTotal(CORSMixin, SlugOrIdMixin, ListAPIView, StatsTotalBase):
    authentication_classes = (RestOAuthAuthentication,
                              RestSharedSecretAuthentication)
    cors_allowed_methods = ['get']
    permission_classes = [
        AnyOf(AllowAppOwner, GroupPermission('Stats', 'View'))
    ]
    queryset = Webapp.objects.all()
    slug_field = 'app_slug'

    def get(self, request, pk):
        app = self.get_object()
        client = self.get_client()

        # Note: We have to do this as separate requests so that if one fails
        # the rest can still be returned.
        data = {}
        for metric, stat in APP_STATS_TOTAL.items():
            data[metric] = {}
            query = self.get_query(metric, stat['metric'], app.id)

            try:
                resp = client.raw(query)
            except ValueError as e:
                log.info('Received value error from monolith client: %s' % e)
                continue

            self.process_response(resp, data)

        return Response(data)
Пример #10
0
class MonolithViewSet(CORSMixin, mixins.DestroyModelMixin,
                      mixins.ListModelMixin, mixins.RetrieveModelMixin,
                      viewsets.GenericViewSet):
    cors_allowed_methods = ('get', 'delete')
    permission_classes = [GroupPermission('Monolith', 'API')]
    authentication_classes = [RestOAuthAuthentication]
    serializer_class = MonolithSerializer

    def get_queryset(self):
        form = MonolithForm(self.request.QUERY_PARAMS)
        if not form.is_valid():
            return Response(form.errors, status=status.HTTP_400_BAD_REQUEST)

        key = form.cleaned_data['key']
        start = form.cleaned_data['start']
        end = form.cleaned_data['end']

        qs = MonolithRecord.objects.all()
        if key:
            qs = qs.filter(key=key)
        if start is not None:
            qs = qs.filter(recorded__gte=start)
        if end is not None:
            qs = qs.filter(recorded__lt=end)
        return qs

    @transaction.commit_on_success
    def delete(self, request, *args, **kwargs):
        qs = self.filter_queryset(self.get_queryset())
        logger.info('Deleting %d monolith resources' % qs.count())
        qs.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
Пример #11
0
class CannedResponseViewSet(CORSMixin, MarketplaceView, viewsets.ModelViewSet):
    authentication_classes = (RestOAuthAuthentication,
                              RestSharedSecretAuthentication)
    permission_classes = [GroupPermission('Admin', 'ReviewerTools')]
    model = CannedResponse
    serializer_class = CannedResponseSerializer
    cors_allowed_methods = ['get', 'post', 'patch', 'put', 'delete']
Пример #12
0
class FailureNotificationView(MarketplaceView, GenericAPIView):
    authentication_classes = [
        RestOAuthAuthentication, RestSharedSecretAuthentication
    ]
    permission_classes = [GroupPermission('Transaction', 'NotifyFailure')]
    queryset = Contribution.objects.filter(uuid__isnull=False)

    def patch(self, request, *args, **kwargs):
        form = FailureForm(request.DATA)
        if not form.is_valid():
            return Response(form.errors, status=status.HTTP_400_BAD_REQUEST)

        obj = self.get_object()
        data = {
            'transaction_id':
            obj,
            'transaction_url':
            absolutify(
                urlparams(reverse('mkt.developers.transactions'),
                          transaction_id=obj.uuid)),
            'url':
            form.cleaned_data['url'],
            'retries':
            form.cleaned_data['attempts']
        }
        owners = obj.addon.authors.values_list('email', flat=True)
        send_mail_jinja('Payment notification failure.',
                        'webpay/failure.txt',
                        data,
                        recipient_list=owners)
        return Response(status=status.HTTP_202_ACCEPTED)
Пример #13
0
class ReviewersWebsiteSearchView(WebsiteSearchView):
    permission_classes = [GroupPermission('Apps', 'Review')]
    filter_backends = [
        SearchQueryFilter, ReviewerWebsiteSearchFormFilter, SortingFilter
    ]
    serializer_class = ReviewerESWebsiteSerializer
    form_class = ReviewersWebsiteSearchForm
Пример #14
0
class PriceTierViewSet(generics.CreateAPIView,
                       generics.RetrieveUpdateDestroyAPIView,
                       ModelViewSet):
    permission_classes = [GroupPermission('Prices', 'Edit')]
    authentication_classes = [RestOAuthAuthentication]
    serializer_class = PriceTierSerializer
    model = Price
Пример #15
0
class ProductIconViewSet(CORSMixin, MarketplaceView, ListModelMixin,
                         RetrieveModelMixin, GenericViewSet):
    authentication_classes = [
        RestOAuthAuthentication, RestSharedSecretAuthentication,
        RestAnonymousAuthentication
    ]
    permission_classes = [
        AnyOf(AllowReadOnly, GroupPermission('ProductIcon', 'Create'))
    ]
    queryset = ProductIcon.objects.all()
    serializer_class = ProductIconSerializer
    cors_allowed_methods = ['get', 'post']
    filter_fields = ('ext_url', 'ext_size', 'size')

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.DATA)
        if serializer.is_valid():
            log.info('Resizing product icon %s @ %s to %s for webpay' %
                     (serializer.data['ext_url'], serializer.data['ext_size'],
                      serializer.data['size']))
            tasks.fetch_product_icon.delay(serializer.data['ext_url'],
                                           serializer.data['ext_size'],
                                           serializer.data['size'])
            return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Пример #16
0
class VersionStatusViewSet(mixins.UpdateModelMixin, viewsets.GenericViewSet):
    """Special API view used by senior reviewers and admins to modify a version
    (actually the corresponding File) status."""
    authentication_classes = [
        RestOAuthAuthentication, RestSharedSecretAuthentication,
        RestAnonymousAuthentication
    ]
    permission_classes = [GroupPermission('Admin', '%')]
    serializer_class = FileStatusSerializer
    cors_allowed_methods = ['patch']

    def get_object(self):
        # Since we are fetching a totally different object than the pk the
        # client is passing, we need to make sure to override the pk in
        # self.kwargs, DRF uses it as a precautionary measure in in pre_save().
        obj = self.kwargs['version'].all_files[0]
        self.kwargs[self.lookup_field] = obj.pk
        return obj

    def update(self, request, *args, **kwargs):
        # PUT is disallowed, only PATCH is accepted for this endpoint.
        if request.method == 'PUT':
            raise MethodNotAllowed('PUT')
        res = super(VersionStatusViewSet, self).update(request, *args,
                                                       **kwargs)
        app = self.object.version.addon
        res.data['app_status'] = mkt.STATUS_CHOICES_API[app.status]
        return res
Пример #17
0
class MonolithView(CORSMixin, MarketplaceView, ListAPIView):
    cors_allowed_methods = ['get']
    permission_classes = [GroupPermission('Monolith', 'API')]
    authentication_classes = [RestOAuthAuthentication]
    serializer_class = MonolithSerializer

    def get_queryset(self):
        form = MonolithForm(self.request.QUERY_PARAMS)
        if not form.is_valid():
            return Response(form.errors, status=status.HTTP_400_BAD_REQUEST)

        key = form.cleaned_data['key']
        start = form.cleaned_data['start']
        end = form.cleaned_data['end']

        log.info('[Monolith] Querying key:%s [%s:%s]' % (key, start, end))

        if key in STATS:
            return _get_query_result(key, start, end)

        else:
            qs = MonolithRecord.objects.all()
            if key:
                qs = qs.filter(key=key)
            if start is not None:
                qs = qs.filter(recorded__gte=start)
            if end is not None:
                qs = qs.filter(recorded__lt=end)

            return qs
Пример #18
0
class TransactionAPI(CORSMixin, APIView):
    """
    API to query by transaction ID.

    Note: This is intended for Monolith to be able to associate a Solitude
    transaction with an app and price tier amount in USD.

    """
    authentication_classes = (RestOAuthAuthentication,
                              RestSharedSecretAuthentication)
    cors_allowed_methods = ['get']
    permission_classes = [GroupPermission('RevenueStats', 'View')]

    def get(self, request, transaction_id):
        try:
            contrib = (Contribution.objects.select_related('price_tier').get(
                transaction_id=transaction_id))
        except Contribution.DoesNotExist:
            raise http.Http404('No transaction by that ID.')

        data = {
            'id': transaction_id,
            'app_id': contrib.addon_id,
            'amount_USD': contrib.price_tier.price,
            'type': amo.CONTRIB_TYPES[contrib.type],
        }

        return Response(data)
Пример #19
0
class ReviewingView(ListAPIView):
    authentication_classes = [RestOAuthAuthentication,
                              RestSharedSecretAuthentication]
    permission_classes = [GroupPermission('Apps', 'Review')]
    serializer_class = ReviewingSerializer

    def get_queryset(self):
        return [row['app'] for row in AppsReviewing(self.request).get_apps()]
Пример #20
0
class FeedItemViewSet(viewsets.ModelViewSet):
    authentication_classes = [RestOAuthAuthentication,
                              RestSharedSecretAuthentication,
                              RestAnonymousAuthentication]
    permission_classes = [AnyOf(AllowReadOnly,
                                GroupPermission('Feed', 'Curate'))]
    queryset = FeedItem.objects.all()
    serializer_class = FeedItemSerializer
Пример #21
0
class FeedBuilderView(CORSMixin, APIView):
    authentication_classes = [
        RestOAuthAuthentication, RestSharedSecretAuthentication
    ]
    permission_classes = [GroupPermission('Feed', 'Curate')]
    cors_allowed_methods = ('put', )

    def put(self, request, *args, **kwargs):
        """
        For each region in the object:
        Deletes all of the (carrier-less) FeedItems in the region.
        Batch create all of the FeedItems in order for each region.

        -- feed - object of regions that point to a list of feed
                  element IDs (as well as their type) .
        {
            'us': [
                ['app', 36L],
                ['app', 42L],
                ['collection', 12L],
                ['brand', 12L]
            ]
        }
        """
        regions = [
            mkt.regions.REGIONS_DICT[region].id
            for region in request.DATA.keys()
        ]
        FeedItem.objects.filter(carrier=None, region__in=regions).delete()

        feed_items = []
        for region, feed_elements in request.DATA.items():
            for order, feed_element in enumerate(feed_elements):
                try:
                    item_type, item_id = feed_element
                except ValueError:
                    return response.Response(
                        'Expected two-element arrays.',
                        status=status.HTTP_400_BAD_REQUEST)
                feed_item = {
                    'region': mkt.regions.REGIONS_DICT[region].id,
                    'order': order,
                    'item_type': item_type,
                }
                feed_item[item_type + '_id'] = item_id
                feed_items.append(FeedItem(**feed_item))

        FeedItem.objects.bulk_create(feed_items)

        # Index the feed items created. bulk_create doesn't call save or
        # post_save so get the IDs manually.
        feed_item_ids = list(
            FeedItem.objects.filter(region__in=regions).values_list('id',
                                                                    flat=True))
        FeedItem.get_indexer().index_ids(feed_item_ids, no_delay=True)

        return response.Response(status=status.HTTP_201_CREATED)
Пример #22
0
class CollectionImageViewSet(CORSMixin, SlugOrIdMixin, MarketplaceView,
                             generics.GenericAPIView, viewsets.ViewSet):
    permission_classes = [
        AnyOf(AllowReadOnly, GroupPermission('Feed', 'Curate'))
    ]
    authentication_classes = [
        RestOAuthAuthentication, RestSharedSecretAuthentication,
        RestAnonymousAuthentication
    ]
    cors_allowed_methods = ('get', 'put', 'delete')

    hash_field = 'image_hash'
    image_suffix = ''

    # Dummy serializer to keep DRF happy when it's answering to OPTIONS.
    serializer_class = Serializer

    def perform_content_negotiation(self, request, force=False):
        """
        Force DRF's content negociation to not raise an error - It wants to use
        the format passed to the URL, but we don't care since we only deal with
        "raw" content: we don't even use the renderers.
        """
        return super(CollectionImageViewSet,
                     self).perform_content_negotiation(request, force=True)

    @cache_control(max_age=60 * 60 * 24 * 365)
    def retrieve(self, request, *args, **kwargs):
        obj = self.get_object()
        if not getattr(obj, 'image_hash', None):
            raise Http404
        return HttpResponseSendFile(request,
                                    obj.image_path(self.image_suffix),
                                    content_type='image/png')

    def update(self, request, *args, **kwargs):
        obj = self.get_object()
        try:
            img, hash_ = DataURLImageField().from_native(request.read())
        except ValidationError:
            return Response(status=status.HTTP_400_BAD_REQUEST)
        i = Image.open(img)
        with public_storage.open(obj.image_path(self.image_suffix), 'wb') as f:
            i.save(f, 'png')
        # Store the hash of the original image data sent.
        obj.update(**{self.hash_field: hash_})

        pngcrush_image.delay(obj.image_path(self.image_suffix))
        return Response(status=status.HTTP_204_NO_CONTENT)

    def destroy(self, request, *args, **kwargs):
        obj = self.get_object()
        if getattr(obj, 'image_hash', None):
            public_storage.delete(obj.image_path(self.image_suffix))
            obj.update(**{self.hash_field: None})
        return Response(status=status.HTTP_204_NO_CONTENT)
Пример #23
0
class FeedElementSearchView(BaseFeedESView):
    """
    Search view for the Curation Tools.

    Returns an object keyed by feed element type
    ('apps', 'brands', 'collections').
    """
    authentication_classes = [
        RestOAuthAuthentication, RestSharedSecretAuthentication
    ]
    permission_classes = [GroupPermission('Feed', 'Curate')]
    cors_allowed_methods = ('get', )

    def _phrase(self, q):
        return {
            'query': q,
            'type': 'phrase',
            'slop': 2,
        }

    def get(self, request, *args, **kwargs):
        q = request.GET.get('q')

        # Make search.
        queries = [
            query.Q('match', slug=self._phrase(q)),  # Slug.
            query.Q('match', type=self._phrase(q)),  # Type.
            query.Q('match', search_names=self._phrase(q)),  # Name.
            query.Q('prefix', carrier=q),  # Shelf carrier.
            query.Q('term', region=q)  # Shelf region.
        ]
        sq = query.Bool(should=queries)

        # Search.
        res = {'apps': [], 'brands': [], 'collections': [], 'shelves': []}
        es = Search(using=FeedItemIndexer.get_es(),
                    index=self.get_feed_element_index())
        feed_elements = es.query(sq).execute().hits
        if not feed_elements:
            return response.Response(res, status=status.HTTP_404_NOT_FOUND)

        # Deserialize.
        ctx = {
            'app_map': self.get_apps(request,
                                     self.get_app_ids_all(feed_elements)),
            'request': request
        }
        for feed_element in feed_elements:
            item_type = feed_element.item_type
            serializer = self.SERIALIZERS[item_type]
            data = serializer(feed_element, context=ctx).data
            res[self.PLURAL_TYPES[item_type]].append(data)

        # Return.
        return response.Response(res, status=status.HTTP_200_OK)
Пример #24
0
class FeedShelfPublishView(CORSMixin, APIView):
    """
    put -- creates a FeedItem for a FeedShelf with respective carrier/region
        pair.  Deletes any currently existing FeedItems with the carrier/region
        pair to effectively "unpublish" it since only one shelf can be toggled
        at a time for a carrier/region.

    delete -- deletes the FeedItem for a FeedShelf with respective
        carrier/region.
    """
    authentication_classes = [RestOAuthAuthentication,
                              RestSharedSecretAuthentication]
    permission_classes = [GroupPermission('Feed', 'Curate')]
    cors_allowed_methods = ('delete', 'put',)

    def get_object(self, pk):
        if pk.isdigit():
            return FeedShelf.objects.get(pk=pk)
        else:
            return FeedShelf.objects.get(slug=pk)

    def put(self, request, *args, **kwargs):
        try:
            shelf = self.get_object(self.kwargs['pk'])
        except FeedShelf.DoesNotExist:
            return response.Response(status=status.HTTP_404_NOT_FOUND)

        feed_item_kwargs = {
            'item_type': feed.FEED_TYPE_SHELF,
            'carrier': shelf.carrier,
            'region': shelf.region
        }
        FeedItem.objects.filter(**feed_item_kwargs).delete()
        feed_item = FeedItem.objects.create(shelf_id=shelf.id,
                                            **feed_item_kwargs)

        # Return.
        return response.Response(FeedItemSerializer(feed_item).data,
                                 status=status.HTTP_201_CREATED)

    def delete(self, request, *args, **kwargs):
        try:
            shelf = self.get_object(self.kwargs['pk'])
        except FeedShelf.DoesNotExist:
            return response.Response(status=status.HTTP_404_NOT_FOUND)

        feed_item_kwargs = {
            'item_type': feed.FEED_TYPE_SHELF,
            'carrier': shelf.carrier,
            'region': shelf.region
        }
        FeedItem.objects.filter(**feed_item_kwargs).delete()

        # Return.
        return response.Response(status=status.HTTP_204_NO_CONTENT)
Пример #25
0
class FeedAppViewSet(CORSMixin, viewsets.ModelViewSet):
    authentication_classes = [
        RestOAuthAuthentication, RestSharedSecretAuthentication,
        RestAnonymousAuthentication
    ]
    permission_classes = [
        AnyOf(AllowReadOnly, GroupPermission('Feed', 'Curate'))
    ]
    queryset = FeedApp.objects.all()
    cors_allowed_methods = ('get', 'post')
    serializer_class = FeedAppSerializer
Пример #26
0
class WebsiteSubmissionViewSet(CORSMixin, MarketplaceView,
                               viewsets.ModelViewSet):
    cors_allowed_methods = ['get', 'post']
    authentication_classes = [
        RestSharedSecretAuthentication, RestOAuthAuthentication
    ]
    queryset = WebsiteSubmission.objects.all()
    permission_classes = [GroupPermission('Websites', 'Submit')]
    serializer_class = PublicWebsiteSubmissionSerializer

    def pre_save(self, obj):
        setattr(obj, 'submitter', self.request.user)
Пример #27
0
class ReviewerScoreViewSet(CORSMixin, MarketplaceView, viewsets.ModelViewSet):
    authentication_classes = (RestOAuthAuthentication,
                              RestSharedSecretAuthentication)
    permission_classes = [GroupPermission('Admin', 'ReviewerTools')]
    serializer_class = ReviewerScoreSerializer
    cors_allowed_methods = ['get', 'post', 'patch', 'put', 'delete']

    # amo.REVIEWED_MANUAL is the default so we don't need to set it on the
    # instance when we are creating a new one, but we do need to set it on
    # queryset to prevent instances with other note_key values from ever being
    # returned.
    queryset = ReviewerScore.objects.filter(note_key=amo.REVIEWED_MANUAL)
Пример #28
0
class WebsiteLookupSearchView(WebsiteSearchView):
    permission_classes = [GroupPermission('WebsiteLookup', 'View')]
    filter_backends = [SearchQueryFilter]
    serializer_class = WebsiteLookupSerializer
    paginate_by = lkp.SEARCH_LIMIT
    max_paginate_by = lkp.MAX_RESULTS

    def get_paginate_by(self, *args, **kwargs):
        if self.request.GET.get(self.paginate_by_param) == 'max':
            return self.max_paginate_by
        else:
            return super(WebsiteLookupSearchView,
                         self).get_paginate_by(*args, **kwargs)
Пример #29
0
class NoRegionSearchView(SearchView):
    """
    A search view that allows searching for public apps regardless of region
    exclusions, protected behind a permission class.

    A special class is needed because when RegionFilter is included, as it is
    in the default SearchView, it will always use whatever region was set on
    the request, and we default to setting restofworld when no region is
    passed.

    """
    authentication_classes = [
        RestSharedSecretAuthentication, RestOAuthAuthentication
    ]
    permission_classes = [
        AnyOf(GroupPermission('Feed', 'Curate'),
              GroupPermission('OperatorDashboard', '*'), IsOperatorPermission)
    ]
    filter_backends = [
        SearchQueryFilter, PublicSearchFormFilter, PublicAppsFilter,
        DeviceTypeFilter, ProfileFilter, SortingFilter
    ]
Пример #30
0
class FeedElementListView(BaseFeedESView, MarketplaceView,
                          generics.GenericAPIView):
    """
    Fetches the five most recent of a feed element type for Curation Tools.
    With pagination.
    """
    authentication_classes = [
        RestOAuthAuthentication, RestSharedSecretAuthentication
    ]
    permission_classes = [GroupPermission('Feed', 'Curate')]
    cors_allowed_methods = ('get', )
    paginator_class = ESPaginator

    def get_recent_feed_elements(self, sq):
        """Matches all sorted by recent."""
        return sq.sort('-created').query(query.MatchAll())

    def get(self, request, item_type, **kwargs):
        item_type = self.ITEM_TYPES[item_type]

        # Hit ES.
        sq = self.get_recent_feed_elements(
            Search(using=FeedItemIndexer.get_es(),
                   index=self.INDICES[item_type]))
        feed_elements = self.paginate_queryset(sq)
        if not feed_elements:
            return response.Response({'objects': []},
                                     status=status.HTTP_404_NOT_FOUND)

        # Deserialize. Manually use pagination serializer because this view
        # uses multiple serializers.
        meta = mkt.api.paginator.CustomPaginationSerializer(feed_elements,
                                                            context={
                                                                'request':
                                                                request
                                                            }).data['meta']
        objects = self.SERIALIZERS[item_type](
            feed_elements,
            context={
                'app_map':
                self.get_apps(request, self.get_app_ids_all(feed_elements)),
                'request':
                request
            },
            many=True).data

        return response.Response({
            'meta': meta,
            'objects': objects
        },
                                 status=status.HTTP_200_OK)
Пример #31
0
class StatusViewSet(mixins.RetrieveModelMixin, mixins.UpdateModelMixin,
                    GenericViewSet):
    queryset = Webapp.objects.all()
    authentication_classes = [
        RestOAuthAuthentication, RestSharedSecretAuthentication
    ]
    permission_classes = [AnyOf(AllowAppOwner, GroupPermission('Admin', '%s'))]
    serializer_class = AppStatusSerializer

    def update(self, request, *args, **kwargs):
        # PUT is disallowed, only PATCH is accepted for this endpoint.
        if request.method == 'PUT':
            raise MethodNotAllowed('PUT')
        return super(StatusViewSet, self).update(request, *args, **kwargs)
Пример #32
0
class TestGroupPermission(TestCase):
    fixtures = fixture('user_2519')

    def setUp(self):
        self.permission = GroupPermission('Drinkers', 'Beer')
        self.obj = Mock()
        self.profile = UserProfile.objects.get(pk=2519)
        self.anonymous = AnonymousUser()
        self.request = RequestFactory().get('/')
        self.request.user = self.anonymous

    def test_has_permission_user_without(self):
        self.request.user = self.profile
        self.request.amo_user = self.profile
        self.request.groups = self.profile.groups.all()
        self.grant_permission(self.profile, 'Drinkers:Scotch')
        eq_(self.permission.has_permission(self.request, 'myview'), False)

    def test_has_permission_user_with(self):
        self.request.user = self.profile
        self.request.amo_user = self.profile
        self.request.groups = self.profile.groups.all()
        self.grant_permission(self.profile, 'Drinkers:Beer')
        eq_(self.permission.has_permission(self.request, 'myview'), True)

    def test_has_permission_anonymous(self):
        eq_(self.permission.has_permission(self.request, 'myview'), False)

    def test_has_object_permission_user_without(self):
        self.request.user = self.profile
        self.request.amo_user = self.profile
        self.request.groups = self.profile.groups.all()
        self.grant_permission(self.profile, 'Drinkers:Scotch')
        obj = Mock()
        eq_(self.permission.has_object_permission(self.request, 'myview', obj),
            False)

    def test_has_object_permission_user_with(self):
        self.request.user = self.profile
        self.request.amo_user = self.profile
        self.request.groups = self.profile.groups.all()
        self.grant_permission(self.profile, 'Drinkers:Beer')
        obj = Mock()
        eq_(self.permission.has_object_permission(self.request, 'myview', obj),
            True)

    def test_has_object_permission_anonymous(self):
        obj = Mock()
        eq_(self.permission.has_object_permission(self.request, 'myview', obj),
            False)
Пример #33
0
class TestGroupPermission(TestCase):
    fixtures = fixture("user_2519")

    def setUp(self):
        self.permission = GroupPermission("Drinkers", "Beer")
        self.obj = Mock()
        self.profile = UserProfile.objects.get(pk=2519)
        self.anonymous = AnonymousUser()
        self.request = RequestFactory().get("/")
        self.request.user = self.anonymous

    def test_has_permission_user_without(self):
        self.request.user = self.profile.user
        self.request.amo_user = self.profile
        self.request.groups = self.profile.groups.all()
        self.grant_permission(self.profile, "Drinkers:Scotch")
        eq_(self.permission.has_permission(self.request, "myview"), False)

    def test_has_permission_user_with(self):
        self.request.user = self.profile.user
        self.request.amo_user = self.profile
        self.request.groups = self.profile.groups.all()
        self.grant_permission(self.profile, "Drinkers:Beer")
        eq_(self.permission.has_permission(self.request, "myview"), True)

    def test_has_permission_anonymous(self):
        eq_(self.permission.has_permission(self.request, "myview"), False)

    def test_has_object_permission_user_without(self):
        self.request.user = self.profile.user
        self.request.amo_user = self.profile
        self.request.groups = self.profile.groups.all()
        self.grant_permission(self.profile, "Drinkers:Scotch")
        obj = Mock()
        eq_(self.permission.has_object_permission(self.request, "myview", obj), False)

    def test_has_object_permission_user_with(self):
        self.request.user = self.profile.user
        self.request.amo_user = self.profile
        self.request.groups = self.profile.groups.all()
        self.grant_permission(self.profile, "Drinkers:Beer")
        obj = Mock()
        eq_(self.permission.has_object_permission(self.request, "myview", obj), True)

    def test_has_object_permission_anonymous(self):
        obj = Mock()
        eq_(self.permission.has_object_permission(self.request, "myview", obj), False)