Ejemplo n.º 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
Ejemplo n.º 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)
Ejemplo n.º 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()
Ejemplo n.º 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)
Ejemplo n.º 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))
Ejemplo n.º 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
Ejemplo n.º 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', '')
Ejemplo n.º 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)
Ejemplo n.º 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)
Ejemplo n.º 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)
Ejemplo n.º 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']
Ejemplo n.º 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)
Ejemplo n.º 13
0
class ReviewersWebsiteSearchView(WebsiteSearchView):
    permission_classes = [GroupPermission('Apps', 'Review')]
    filter_backends = [
        SearchQueryFilter, ReviewerWebsiteSearchFormFilter, SortingFilter
    ]
    serializer_class = ReviewerESWebsiteSerializer
    form_class = ReviewersWebsiteSearchForm
Ejemplo n.º 14
0
class PriceTierViewSet(generics.CreateAPIView,
                       generics.RetrieveUpdateDestroyAPIView,
                       ModelViewSet):
    permission_classes = [GroupPermission('Prices', 'Edit')]
    authentication_classes = [RestOAuthAuthentication]
    serializer_class = PriceTierSerializer
    model = Price
Ejemplo n.º 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)
Ejemplo n.º 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
Ejemplo n.º 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
Ejemplo n.º 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)
Ejemplo n.º 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()]
Ejemplo n.º 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
Ejemplo n.º 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)
Ejemplo n.º 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)
Ejemplo n.º 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)
Ejemplo n.º 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)
Ejemplo n.º 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
Ejemplo n.º 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)
Ejemplo n.º 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)
Ejemplo n.º 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)
Ejemplo n.º 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
    ]
Ejemplo n.º 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)
Ejemplo n.º 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)
Ejemplo n.º 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)
Ejemplo n.º 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)