Пример #1
0
class NotificationViewSet(
    viewsets.GenericViewSet,
    viewsets.mixins.ListModelMixin,
):
    serializer_class = NotificationSerializer
    permission_classes = (IsAuthenticated,)
    filterset_fields = ('unread',)

    def get_queryset(self):
        return self.request.user.notifications.all()

    @action(methods=['GET'], detail=False, url_path='unread-count')
    @method_decorator(ratelimit(key='user', rate='3/s', method='GET', block=True))
    def unread_count(self, request, *args, **kwargs):
        count = self.get_queryset().filter(unread=True).count()
        return Response({
            'unread_count': count
        }, status=status.HTTP_200_OK)

    @action(methods=['POST'], detail=False, url_path='mark-all-as-read')
    @method_decorator(ratelimit(key='user', rate='3/s', method='POST', block=True))
    def mark_all_as_read(self, request, *args, **kwargs):
        updated_count = self.get_queryset().update(unread=False)
        return Response({
            'marked_count': updated_count
        }, status=status.HTTP_200_OK)

    @required_params(method='POST', params=['unread'])
    @method_decorator(ratelimit(key='user', rate='3/s', method='POST', block=True))
    def update(self, request, *args, **kwargs):
        """
        user can mark a notification as read/unread, this an update for notification
        so override update,
        another method use action
            @action(method=['POST], detail=True, url_path='mark-as-read')
            def mark_as_read(self, request, *args, **kwargs)
                ...
            @action(method=['POST], detail=True, url_path='mark-as-unread')
            def mark_as_unread(self, request, *args, **kwargs)
                ...
        both are OK. override update is more restful, mark as read/unread can share
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        serializer = NotificationSerializerForUpdate(
            instance=self.get_object(),
            data=request.data,
        )
        if not serializer.is_valid():
            return Response({
                'message': "Please check input",
                'errors': serializer.errors,
            }, status=status.HTTP_400_BAD_REQUEST)
        notification = serializer.save()
        return Response(
            NotificationSerializer(notification).data,
            status=status.HTTP_200_OK
        )
Пример #2
0
class TweetViewSet(viewsets.GenericViewSet, viewsets.mixins.CreateModelMixin,
                   viewsets.mixins.ListModelMixin):
    serializer_class = TweetSerializerForCreate
    pagination_class = EndlessPagination
    queryset = Tweet.objects.all()

    def get_permissions(self):
        if self.action in ['list', 'retrieve']:
            return [permissions.AllowAny()]
        return [permissions.IsAuthenticated()]

    @method_decorator(
        ratelimit(key='user_or_ip', rate='5/s', method='GET', block=True))
    def retrieve(self, request, *args, **kwargs):
        serializer = TweetSerializerForDetail(self.get_object(),
                                              context={'request': request})
        return Response(serializer.data)

    @required_params(params=['user_id'])
    @method_decorator(
        ratelimit(key='user_or_ip', rate='5/s', method='GET', block=True))
    def list(self, request, *args, **kwargs):
        user_id = request.query_params['user_id']
        cached_tweets = TweetService.get_cached_tweets(user_id)
        page = self.paginator.paginate_cached_list(cached_tweets, request)
        if page is None:
            queryset = Tweet.objects.filter(
                user_id=user_id).order_by('-created_at')
            page = self.paginate_queryset(queryset)
        serializer = TweetSerializer(
            page,
            context={'request': request},
            many=True,
        )
        return self.get_paginated_response(serializer.data)

    @method_decorator(
        ratelimit(key='user', rate='1/s', method='POST', block=True))
    @method_decorator(
        ratelimit(key='user', rate='5/m', method='POST', block=True))
    def create(self, request, *args, **kwargs):
        serializer = TweetSerializerForCreate(data=request.data,
                                              context={'request': request})
        if not serializer.is_valid():
            return Response(
                {
                    'success': False,
                    'message': 'Please check input',
                    'errors': serializer.errors
                },
                status=400)

        tweet = serializer.save()
        NewsfeedService.fanout_to_followers(tweet)
        serializer = TweetSerializer(
            tweet,
            context={'request': request},
        )
        return Response(serializer.data, status=201)
Пример #3
0
class NotificationViewSet(
        viewsets.GenericViewSet,
        viewsets.mixins.ListModelMixin,
):
    serializer_class = NotificationSerializer
    permission_classes = (IsAuthenticated, )
    filterset_fields = ('unread', )

    def get_queryset(self):
        return self.request.user.notifications.all()

    @action(methods=['GET'], detail=False, url_path='unread-count')
    @method_decorator(
        ratelimit(key='user', rate='3/s', method='GET', block=True))
    def unread_count(self, request, *args, **kwargs):
        count = self.get_queryset().filter(unread=True).count()
        return Response({'unread_count': count}, status=status.HTTP_200_OK)

    @action(methods=['POST'], detail=False, url_path='mark-all-as-read')
    @method_decorator(
        ratelimit(key='user', rate='3/s', method='POST', block=True))
    def mark_all_as_read(self, request, *args, **kwargs):
        updated_count = self.get_queryset().update(unread=False)
        return Response({'marked_count': updated_count},
                        status=status.HTTP_200_OK)

    @required_params(method='POST', params=['unread'])
    @method_decorator(
        ratelimit(key='user', rate='3/s', method='POST', block=True))
    def update(self, request, *args, **kwargs):
        """
        用户可以标记一个 notification 为已读或者未读。标记已读和未读都是对notification
        的更新操作,所以直接重载update方法来实现,这种实现方法更加rest。另外一种方法是用一个
        专属的action然后用两个方法实现如下
        @action(methods=['POST], detail=True, url_path='mark-as-read')
        def mark_as_read(self, request, *args, **kwargs):
            ...

        @action(methods=['POST], detail=True, url_path='mark-as-unread')
        def mark_as_unread(self, request, *args, **kwargs):
            ...
        但是这种方法无法封装这两个方法公用的逻辑。
        """
        serializer = NotificationSerializerForUpdate(
            instance=self.get_object(),
            data=request.data,
        )
        if not serializer.is_valid():
            return Response(
                {
                    'message': 'Please check input',
                    'errors': serializer.errors,
                },
                status=status.HTTP_400_BAD_REQUEST)
        notification = serializer.save()
        return Response(
            NotificationSerializer(notification).data,
            status=status.HTTP_200_OK,
        )
Пример #4
0
class ProgramSaveForLaterApiView(APIView):
    """
    API VIEW
    """
    @transaction.atomic
    @method_decorator(
        ratelimit(key=POST_EMAIL_KEY,
                  rate=settings.SAVE_FOR_LATER_EMAIL_RATE_LIMIT,
                  method='POST'))
    @method_decorator(
        ratelimit(key=REAL_IP_KEY,
                  rate=settings.SAVE_FOR_LATER_IP_RATE_LIMIT,
                  method='POST'))
    def post(self, request):
        """
        **Use Case**

            * Send favorite program through email to user for later learning.

        **Example Request for program**

            POST /api/v1/save/program/

        **Example POST Request for program**

            {
                "email": "*****@*****.**",
                "uuid": "587f6abe-bfa4-4125-9fbe-4789bf3f97f1"

            }
        """
        user = request.user
        data = request.data
        program_uuid = data.get('uuid')
        email = data.get('email')

        if getattr(request, 'limited', False):
            return Response({'error_code': 'rate-limited'}, status=403)

        if get_email_validation_error(email):
            return Response({'error_code': 'incorrect-email'}, status=400)

        program = get_programs(uuid=program_uuid)
        SavedProgram.objects.update_or_create(
            user_id=user.id,
            email=email,
            program_uuid=program_uuid,
        )
        if program:
            program_data = {
                'program': program,
                'type': 'program',
            }
            if send_email(request, email, program_data):
                return Response({'result': 'success'}, status=200)
            else:
                return Response({'error_code': 'email-not-send'}, status=400)

        return Response({'error_code': 'program-not-found'}, status=404)
Пример #5
0
        class TestView(View):
            @method_decorator(ratelimit(key='ip', rate='1/m', method='GET'))
            def get(self, request):
                return request.limited

            @method_decorator(ratelimit(key='ip', rate='1/m', method='POST'))
            def post(self, request):
                return request.limited
Пример #6
0
class LikeViewSet(viewsets.GenericViewSet):
    queryset = Like.objects.all()
    serializer_class = LikeSerializerForCreate

    def get_permissions(self):
        if self.action in ['create', 'cancel']:
            return [
                IsAuthenticated(),
            ]
        return [
            AllowAny(),
        ]

    @require_params(require_attrs='data', params=['content_type', 'object_id'])
    @method_decorator(
        ratelimit(key='user', rate='10/s', method='POST', block=True))
    def create(self, request):
        serializer = LikeSerializerForCreate(
            data=request.data,
            context={'request': request},
        )
        if not serializer.is_valid():
            return Response({
                'success': False,
                'error': serializer.errors,
            },
                            status=status.HTTP_400_BAD_REQUEST)
        like, created = serializer.get_or_create()
        # raise notifications in creation, do not dispatch notifications if liked
        if created:
            NotificationService.send_like_notification(like)
        return Response({
            'success': True,
            'like': LikeSerializer(like).data
        },
                        status=status.HTTP_200_OK)

    @action(methods=['POST'], detail=False)
    @require_params(require_attrs='data', params=['content_type', 'object_id'])
    @method_decorator(
        ratelimit(key='user', rate='10/s', method='POST', block=True))
    def cancel(self, request):
        serializer = LikeSerializerForCancel(
            data=request.data,
            context={'request': request},
        )
        if not serializer.is_valid():
            return Response({
                'success': False,
                'error': serializer.errors,
            },
                            status=status.HTTP_400_BAD_REQUEST)
        deleted = serializer.cancel()
        return Response({
            'success': True,
            'deleted': deleted,
        },
                        status=status.HTTP_200_OK)
Пример #7
0
class TweetViewSet(viewsets.GenericViewSet):
    serializer_class = TweetSerializerForCreate
    queryset = Tweet.objects.all()
    pagination_class = EndlessPagination

    def get_permissions(self):
        if self.action in ['list', 'retrieve']:
            return [AllowAny()]
        return [IsAuthenticated()]

    @required_params(params=['user_id'])
    def list(self, request, *args, **kwargs):
        user_id = request.query_params['user_id']
        tweets = Tweet.objects.filter(user_id=user_id).prefetch_related('user')

        cached_tweets = TweetService.get_cached_tweets(user_id)
        page = self.paginator.paginate_cached_list(cached_tweets, request)
        if page is None:
            # select * from twitter_tweets
            # WHERE user_id = xxx
            # order by created_at desc
            # this sql query use user and created_at index together
            # user only index can't execute this query
            queryset = Tweet.objects.filter(user_id=user_id).order_by('-created_at')
            page = self.paginate_queryset(queryset)
        serializer = TweetSerializer(
                page,
                context={'request': request},
                many=True,
        )
        return self.get_paginated_response(serializer.data)

    @method_decorator(ratelimit(key='user_or_ip', rate='5/s', method='GET', block=True))
    def retrieve(self, request, *args, **kwargs):
        tweet = self.get_object()
        serializer = TweetSerializerForDetail(
            tweet,
            context={'request': request},
        )
        return Response(serializer.data)

    @method_decorator(ratelimit(key='user', rate='1/s', method='POST', block=True))
    @method_decorator(ratelimit(key='user', rate='5/m', method='POST', block=True))
    def create(self, request):
        serializer = TweetSerializerForCreate(
            data = request.data,
            context={'request': request}
        )

        if not serializer.is_valid():
            return Response({
                'success': False,
                'message': 'Please check input.',
                'errors': serializer.errors,
            }, status=400 )
        tweet = serializer.save()
        NewsFeedService.fanout_to_followers(tweet)
        return Response(TweetSerializer(tweet, context={'request':request}).data , status=201)
class TweetViewSet(viewsets.GenericViewSet):
    serializer_class = TweetSerializerForCreate
    queryset = Tweet.objects.all()
    pagination_class = EndlessPagination

    def get_permissions(self):
        if self.action in ['list', 'retrieve']:
            return [AllowAny()]
        return [IsAuthenticated()]

    @required_params(params=['user_id'])
    def list(self, request):
        user_id = request.query_params['user_id']
        tweets = Tweet.objects.filter(user_id=user_id).prefetch_related('user')

        cached_tweets = TweetService.get_cached_tweets(user_id)
        page = self.paginator.paginate_cached_list(cached_tweets, request)
        if page is None:
            queryset = Tweet.objects.filter(user_id=user_id).order_by('-created_at')
            page = self.paginate_queryset(queryset)
        serializer = TweetSerializer(
            page,
            context={'request': request},
            many=True,
        )
        return self.get_paginated_response(serializer.data)

    @method_decorator(ratelimit(key='user_or_ip', rate='5/s', method='GET', block=True))
    def retrieve(self, request, *args, **kwargs):
        tweet = self.get_object()
        serializer = TweetSerializerForDetail(
            tweet,
            context={'request': request},
        )
        return Response(serializer.data)

    @method_decorator(ratelimit(key='user', rate='1/s', method='POST', block=True))
    @method_decorator(ratelimit(key='user', rate='5/m', method='POST', block=True))
    def create(self, request):
        serializer = TweetSerializerForCreate(
            data=request.data,
            context={'request': request},
        )
        if not serializer.is_valid():
            return Response({
                "success": False,
                "message": "Input is invalid. Please check it again!",
                "errors": serializer.errors,
            }, status=400)
        # save will call create method in TweetSerializerForCreate
        tweet = serializer.save()
        NewsFeedService.fanout_to_followers(tweet)
        return Response(
            TweetSerializer(tweet, context={'request': request}).data,
            status=201,
        )
Пример #9
0
class NotificationViewSet(
    viewsets.GenericViewSet,
    viewsets.mixins.ListModelMixin,
):
    serializer_class = NotificationSerializer
    permission_classes = (IsAuthenticated,)
    filterset_fields = ('unread',)

    def get_queryset(self):
        return self.request.user.notifications.all()

    # detail is false becaus the current logined user doesn't need to show up in the url
    @action(methods=['GET'], detail=False, url_path='unread-count')
    @method_decorator(ratelimit(key='user', rate='3/s', method='GET', block=True))
    def unread_count(self, request, *args, **kwargs):
        # /api/notifications/unread_count/
        count = self.get_queryset().filter(unread=True).count()
        return Response({'unread_count': count}, status=status.HTTP_200_OK)

    @action(methods=['POST'], detail=False, url_path='mark-all-as-read')
    @method_decorator(ratelimit(key='user', rate='3/s', method='POST', block=True))
    def mark_all_as_read(self, request, *args, **kwargs):
        updated_count = self.get_queryset().update(unread=False)
        return Response({'marked_count': updated_count}, status=status.HTTP_200_OK)

    @required_params(method='POST', params=['unread'])
    @method_decorator(ratelimit(key='user', rate='3/s', method='POST', block=True))
    def update(self, request, *args, **kwargs):
        """
        用户可以标记一个 notification 为已读或者未读。标记已读和未读都是对 notification
        的一次更新操作,所以直接重载 update 的方法来实现。另外一种实现方法是用一个专属的 action:
            @action(methods=['POST'], detail=True, url_path='mark-as-read')
            def mark_as_read(self, request, *args, **kwargs):
                ...
            @action(methods=['POST'], detail=True, url_path='mark-as-unread')
            def mark_as_unread(self, request, *args, **kwargs):
                ...
        两种方法都可以,我更偏好重载 update,因为更通用更 rest 一些, 而且 mark as unread 和
        mark as read 可以公用一套逻辑。
        """
        serializer = NotificationSerializerForUpdate(
            instance=self.get_object(),
            data=request.data,
        )
        if not serializer.is_valid():
            return Response({
                'message': "Please check input",
                'errors': serializer.errors,
            }, status=status.HTTP_400_BAD_REQUEST)
        notification = serializer.save()
        return Response(
            NotificationSerializer(notification).data,
            status=status.HTTP_200_OK,
        )
Пример #10
0
class RateLimitedAdmin(admin.AdminSite):
    @method_decorator(never_cache)
    @method_decorator(
        ratelimit(key='ip', rate='3/m', method=ratelimit.UNSAFE, block=True))
    @method_decorator(
        ratelimit(key='post:username',
                  rate='3/m',
                  method=ratelimit.UNSAFE,
                  block=True))
    def login(self, request, extra_context=None):
        return super(RateLimitedAdmin, self).login(request, extra_context)
Пример #11
0
class LikeViewSet(viewsets.GenericViewSet):
    queryset = Like.objects.all()
    permission_classes = [IsAuthenticated]
    serializer_class = LikeSerializerForCreate

    @required_params(method='POST', params=['content_type', 'object_id'])
    @method_decorator(
        ratelimit(key='user', rate='10/s', method='POST', block=True))
    def create(self, request, *args, **kwargs):
        serializer = LikeSerializerForCreate(
            data=request.data,
            context={'request': request},
        )
        if not serializer.is_valid():
            return Response(
                {
                    'message': 'Please check input.',
                    'errors': serializer.errors,
                },
                status=status.HTTP_400_BAD_REQUEST)
        instance, created = serializer.get_or_create()
        # 发送 like notification
        if created:
            NotificationService.send_like_notification(instance)
        return Response(
            LikeSerializer(instance).data,
            status=status.HTTP_201_CREATED,
        )

    @action(methods=['POST'], detail=False)
    @required_params(method='POST', params=['content_type', 'object_id'])
    @method_decorator(
        ratelimit(key='user', rate='10/s', method='POST', block=True))
    def cancel(self, request, *args, **kwargs):
        serializer = LikeSerializerForCancel(
            data=request.data,
            context={'request': request},
        )
        if not serializer.is_valid():
            return Response(
                {
                    'message': 'Please check input.',
                    'errors': serializer.errors,
                },
                status=status.HTTP_400_BAD_REQUEST)
        deleted_count = serializer.cancel()
        return Response({
            'success': True,
            'deleted': deleted_count,
        },
                        status=status.HTTP_200_OK)
Пример #12
0
class LikeViewSet(viewsets.GenericViewSet):
    queryset = Like.objects.all()
    permission_classes = [IsAuthenticated]
    serializer_class = LikeSerializerForCreate

    @required_params(method='POST', params=['content_type', 'object_id'])
    @method_decorator(
        ratelimit(key='user', rate='10/s', method='POST', block=True))
    def create(self, request, *args, **kwargs):
        serializer = LikeSerializerForCreate(
            data=request.data,
            context={'request': request},
        )
        if not serializer.is_valid():
            return Response(
                {
                    'message': 'Please check input',
                    'errors': serializer.errors,
                },
                status=status.HTTP_400_BAD_REQUEST)
        instance, created = serializer.get_or_create()
        if created:
            NotificationService.send_like_notification(instance)
        return Response(
            LikeSerializer(instance).data,
            status=status.HTTP_201_CREATED,
        )

    # 设计取消like的api时候不要用 httpdelete /api/likes/{like_object_id}
    # 因为如果在前端一个用户点击了like,又很快的在点击一下取消like,如果用上述的delete的方式,url需要知道like的object id
    # 这就意味着如果用户取消点赞必须要等到得到了这个点赞的id,这种体验不好 视频 第二十章 like 1_2_3的43:19
    # 所以取消点赞的api是 POST /api/likes/cancle
    @action(methods=['POST'], detail=False)
    @required_params(method='POST', params=['content_type', 'object_id'])
    @method_decorator(
        ratelimit(key='user', rate='10/s', method='POST', block=True))
    def cancel(self, request, *args, **kwargs):
        serializer = LikeSerializerForCancel(
            data=request.data,
            context={'request': request},
        )
        if not serializer.is_valid():
            return Response(
                {
                    'message': 'Please check input',
                    'errors': serializer.errors,
                },
                status=status.HTTP_400_BAD_REQUEST)
        serializer.cancel()
        return Response({'success': True}, status=status.HTTP_200_OK)
Пример #13
0
class NotificationViewSet(
        viewsets.GenericViewSet,
        viewsets.mixins.ListModelMixin,
):
    serializer_class = NotificationSerializer
    permission_classes = (IsAuthenticated, )

    def get_queryset(self):
        return Notification.objects.filter(recipient=self.request.user)

    @action(methods=['GET'], detail=False, url_path='unread-count')
    @method_decorator(
        ratelimit(key='user', rate='3/s', method='GET', block=True))
    def unread_count(self, request, *args, **kwargs):
        # GET /api/notifications/unread-count/
        count = self.get_queryset().filter(unread=True).count()
        return Response({'unread_count': count}, status=status.HTTP_200_OK)

    @action(methods=['POST'], detail=False, url_path='mark-all-as-read')
    @method_decorator(
        ratelimit(key='user', rate='3/s', method='POST', block=True))
    def mark_all_as_read(self, request, *args, **kwargs):
        updated_count = self.get_queryset().update(unread=False)
        return Response({'marked_count': updated_count},
                        status=status.HTTP_200_OK)

    @required_params(method='POST', params=['unread'])
    @method_decorator(
        ratelimit(key='user', rate='3/s', method='POST', block=True))
    def update(self, request, *args, **kwargs):
        # PUT /api/notifications/1  (request data unread is True or False)
        serializer = NotificationSerializerForUpdate(
            instance=self.get_object(),
            data=request.data,
        )

        if not serializer.is_valid():
            return Response(
                {
                    'message': 'Please check input',
                    'error': serializer.errors,
                },
                status=status.HTTP_400_BAD_REQUEST)
        notification = serializer.save()
        return Response(
            NotificationSerializer(notification).data,
            status=status.HTTP_200_OK,
        )
Пример #14
0
class UserConsentActionView(DetailView):
    """
    An abstract view

    Validates that a token is valid for consent ID + email_hash
    """

    model = models.UserConsent
    context_object_name = "consent"
    token_salt = consent_settings.UNSUBSCRIBE_SALT

    @method_decorator(ratelimit(key="ip", rate=RATELIMIT))
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

    def action(self, consent):
        raise NotImplementedError("blah")

    def get_object(self, queryset=None):
        consent = super().get_object(queryset)
        token = self.kwargs.get("token")

        if utils.validate_token(token, consent, salt=self.token_salt):
            self.action(consent)
            return consent
        else:
            raise Http404("This does not work")

    def get_context_data(self, **kwargs):
        c = super().get_context_data(**kwargs)
        c["token"] = utils.get_consent_token(c["consent"],
                                             salt=self.token_salt)
        return c
Пример #15
0
 def view_with_rate_limit(request, *args, **kwargs):
     # cannot use @decorator syntax because reading from settings during import time
     # prevents django.test.override_settings from working as expected.
     # that's why I'm manually decorating the view in this custom view
     return ratelimit(key=ratelimit_key, rate=settings.RATELIMIT_RATE, block=settings.RATELIMIT_ENABLE)(view)(
         request, *args, **kwargs
     )
Пример #16
0
class RegistrationAccepted(UpdateView):
    template_name = "registration/accepted.html"
    model = models.Registration
    context_object_name = "registration"

    @method_decorator(ratelimit(key="ip", rate="100/h"))
    def dispatch(self, request, *args, **kwargs):
        self.access_code = kwargs["access_code"]
        return UpdateView.dispatch(self, request, *args, **kwargs)

    def get_object(self):
        return UpdateView.get_queryset(self).get(
            access_code=self.access_code,
            accepted=True,
        )

    def get_success_url(self):
        return reverse(
            "registration:confirmation-done",
            kwargs={"access_code": self.access_code},
        )

    def get_form_class(self):
        if self.object.scholarship and not self.object.scholarship_confirmed:
            return forms.RegistrationAcceptedScholarshipForm
        if self.object.scholarship_confirmed:
            return forms.RegistrationAcceptedScholarshipConfirmedForm
        if self.object.confirmed:
            return forms.RegistrationAcceptedConfirmedForm
        return forms.RegistrationAcceptedForm
Пример #17
0
class NewsFeedViewSet(viewsets.GenericViewSet):
    pagination_class = EndlessPagination

    def get_permissions(self):
        if self.action == 'list':
            return [
                IsAuthenticated(),
            ]
        return [
            AllowAny(),
        ]

    @method_decorator(
        ratelimit(key='user', rate='5/s', method='GET', block=True))
    def list(self, request):
        cached_newsfeeds = NewsFeedService.load_newsfeeds_through_cache(
            user_id=request.user.id)
        page = self.paginator.paginate_cached_list(cached_newsfeeds, request)
        # cache not enough, access the db directly for extra
        if not page:
            newsfeeds = NewsFeed.objects.filter(user_id=request.user.id)
            page = self.paginate_queryset(newsfeeds)
        serializer = NewsFeedSerializer(
            page,
            context={'request': request},
            many=True,
        )
        return self.get_paginated_response(serializer.data)
Пример #18
0
class NewsFeedViewSet(viewsets.GenericViewSet):
    permission_classes = [IsAuthenticated]
    pagination_class = EndlessPagination

    def get_queryset(self):
        # 自定义 queryset,因为 newsfeed 的查看是有权限的
        # 只能看 user=当前登录用户的 newsfeed
        # 也可以是 self.request.user.newsfeed_set.all()
        # 但是一般最好还是按照 NewsFeed.objects.filter 的方式写,更清晰直观
        return NewsFeed.objects.filter(user=self.request.user)

    @method_decorator(
        ratelimit(key='user', rate='5/s', method='GET', block=True))
    def list(self, request):
        cached_newsfeeds = NewsFeedService.get_cached_newsfeeds(
            request.user.id)
        page = self.paginator.paginate_cached_list(cached_newsfeeds, request)
        if page is None:
            queryset = NewsFeed.objects.filter(user=request.user)
            page = self.paginate_queryset(queryset)
        serializer = NewsFeedSerializer(
            page,
            context={'request': request},
            many=True,
        )
        return self.get_paginated_response(serializer.data)
Пример #19
0
class NewsFeedViewSet(viewsets.GenericViewSet):
    permission_classes = [IsAuthenticated]
    pagination_class = EndlessPagination

    @method_decorator(
        ratelimit(key='user', rate='5/s', method='GET', block=True))
    def list(self, request):
        cached_newsfeeds = NewsFeedService.get_cached_newsfeeds(
            request.user.id)
        page = self.paginator.paginate_cached_list(cached_newsfeeds, request)
        if page is None:
            if GateKeeper.is_switch_on('switch_newsfeed_to_hbase'):
                page = self.paginator.paginate_hbase(HBaseNewsFeed,
                                                     (request.user.id, ),
                                                     request)
            else:
                queryset = NewsFeed.objects.filter(user=request.user)
                page = self.paginate_queryset(queryset)

        serializer = NewsFeedSerializer(
            page,
            context={'request': request},
            many=True,
        )
        return self.get_paginated_response(serializer.data)
Пример #20
0
    def test_wrap_view(self):
        class TestView(View):
            def get(self, request):
                return request.limited

        view = TestView.as_view()
        wrapped = ratelimit(key='ip', rate='1/m', block=False)(view)

        assert not wrapped(rf.get('/'))
        assert wrapped(rf.get('/'))
Пример #21
0
    def test_wrap_view(self):
        class TestView(View):
            def get(self, request):
                return request.limited

        view = TestView.as_view()
        wrapped = ratelimit(key='ip', rate='1/m', block=False)(view)

        assert not wrapped(rf.get('/'))
        assert wrapped(rf.get('/'))
Пример #22
0
    def test_class_decorator(self):
        @method_decorator(ratelimit(key='ip', rate='1/m', block=False),
                          name='get')
        class TestView(View):
            def get(self, request):
                return request.limited

        view = TestView.as_view()

        assert not view(rf.get('/'))
        assert view(rf.get('/'))
Пример #23
0
    def test_class_decorator(self):
        @method_decorator(ratelimit(key='ip', rate='1/m', block=False),
                          name='get')
        class TestView(View):
            def get(self, request):
                return request.limited

        view = TestView.as_view()

        assert not view(rf.get('/'))
        assert view(rf.get('/'))
Пример #24
0
class RegistrationWeek1AcceptedConfirm(DetailView):
    template_name = "registration/week1_accepted_confirmed.html"
    model = models.RegistrationWeek1
    context_object_name = "registration"

    @method_decorator(ratelimit(key="ip", rate="100/h"))
    def dispatch(self, request, *args, **kwargs):
        self.access_code = kwargs["access_code"]
        return DetailView.dispatch(self, request, *args, **kwargs)

    def get_object(self):
        return UpdateView.get_queryset(self).get(access_code=self.access_code)
Пример #25
0
 def test_ratelimit_no_method_match(self, mock_count):
     #we're testing to make sure that the method type doesn't match
     #the default POST
     self.request.method = "GET"
     #we dont need any parameters since
     #we're only testing that the rate limited doesn't get called
     wrapper = decorators.ratelimit()
     mock_view = Mock()
     mock_view.__name__ = "my mock view"
     wrapped_mock = wrapper(mock_view)
     wrapped_mock(self.request)
     self.assertFalse(mock_count.called)
Пример #26
0
class VerifyVerificationCodeAPIView(GenericAPIView):
    serializer_class = serializers.CheckVerificationCodeSerializer
    permission_classes = [SignatureCheckPermission]
    authentication_classes = []

    def get_email(self, uidb64, token):
        try:
            uid = force_text(urlsafe_base64_decode(uidb64))
            user = User.objects.get(pk=uid)
        except (TypeError, ValueError, OverflowError, User.DoesNotExist):
            user = None

        if user is not None and helpers.verification_token.check_token(user, token):
            return user.email

    def get_object(self):
        uidb64 = self.request.data.get('uidb64')
        token = self.request.data.get('token')

        if uidb64 and token:
            email = self.get_email(uidb64, token)
        else:
            email = self.request.data.get('email')

        return get_object_or_404(models.VerificationCode.objects.all(), user__email__iexact=email)

    @method_decorator(ratelimit(key='post:email', rate='12/m', method='POST', block=True))
    def post(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data)

        serializer.is_valid(raise_exception=True)
        serializer.save(date_verified=now())

        EmailAddress.objects.update_or_create(
            user=instance.user, email=instance.user.email, defaults={'verified': True, 'primary': True}
        )

        login(
            request=self.request,
            user=instance.user,
            backend='django.contrib.auth.backends.ModelBackend',
        )
        return Response({'email': instance.user.email})
Пример #27
0
class ConsentCreateView(CreateView):
    """
    The view isn't part of urls.py but you can add it to your own project's
    url configuration if you want to use it.

    To mount it, add a source_id kwarg, for instance::

        path("signup/<int:source_id>/", SignupView.as_view()),
    """

    model = models.UserConsent
    form_class = forms.EmailConsentForm
    template_name = "consent/user/create.html"

    @method_decorator(ratelimit(key="ip", rate=RATELIMIT))
    def dispatch(self, request, *args, **kwargs):
        self.consent_source = get_object_or_404(models.ConsentSource,
                                                id=kwargs["source_id"])
        return super().dispatch(request, *args, **kwargs)

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs["consent_source"] = self.consent_source
        return kwargs

    def get_context_data(self, **kwargs):
        c = super().get_context_data(**kwargs)
        c["consent_source"] = self.consent_source
        return c

    def form_valid(self, form):
        ret_value = super().form_valid(form)
        consent = self.object
        consent.email_confirmation(request=self.request)
        return ret_value

    def get_success_url(self):
        """
        This requires a project with a urlconf specifying a view with the name
        'signup_confirmation'.
        """
        return reverse("signup_confirmation",
                       kwargs={"source_id": self.consent_source.id})
Пример #28
0
class CommentCreate(LoginRequiredMixin, CreateView):
    model = Comment
    context_object_name = 'comments'
    http_method_names = ['post']
    template_name = 'products/includes/comments.html'
    fields = ['content']

    def post(self, request, *args, **kwargs):
        if request.is_ajax():
            text = request.POST['content']
            sku_id = int(self.kwargs['sku_id'])

            self.create_comment(text, sku_id)

            html = render_to_string(self.template_name,
                                    self.get_context_data())
            return HttpResponse(html)

        raise Http404()

    def create_comment(self, text, sku_id):
        user_profile = UserProfile.objects.only('id').get(
            user__id=self.request.user.id)
        self.sku = SKU.objects.get(id=sku_id)
        self.sku.comments.create(owner=user_profile, content=text)

    def get_context_data(self, **kwargs):
        context = {self.context_object_name: self.get_queryset()}
        return context

    def get_queryset(self):
        only_fields = ['owner__user__username']
        queryset = self.sku.comments.select_related().only(
            *only_fields).order_by('-time')
        return queryset

    def http_method_not_allowed(self, request, *args, **kwargs):
        raise Http404()

    @method_decorator(ratelimit(key='user', rate='5/m', block=True))
    def dispatch(self, request, *args, **kwargs):
        return super(CommentCreate, self).dispatch(request, *args, **kwargs)
Пример #29
0
class NewsfeedViewSet(viewsets.GenericViewSet):
    permission_classes = [IsAuthenticated]
    pagination_class = EndlessPagination

    @method_decorator(
        ratelimit(key='user', rate='5/s', method='GET', block=True))
    def list(self, request):
        newsfeeds = NewsfeedService.get_cached_newsfeeds(request.user.id)
        page = self.paginator.paginate_cached_list(newsfeeds, request)

        # The data is not in the cache when the data is not recent.
        # Only cache the recent REDIS_LIST_LENGTH_LIMIT objects in cache.
        if page is None:
            queryset = Newsfeed.objects.filter(
                user_id=request.user.id).order_by('-created_at')
            page = self.paginate_queryset(queryset)

        serializer = NewsfeedSerializer(page,
                                        context={'request': request},
                                        many=True)
        return self.get_paginated_response(serializer.data)
Пример #30
0
    def test_ratelimit_decorator(self):
        #this tests the normal behavior of the ratelimit
        #where we call it twice and one gets limited
        wrapper = decorators.ratelimit(
            limit_by=decorators.ATTR,
            field='oauth_consumer_key',
            #we'll pick a low rate so we can test
            rate="1/h"
            )
        mock_view = Mock()
        #this is need for the functools methods
        mock_view.__name__ = "my mock view"
        wrapped_mock = wrapper(mock_view)

        #call the wrapped mock view for the first time
        wrapped_mock(self.request)
        self.assertFalse(self.request.limited)
        self.assertEqual(self.request.rate_limit_count, 1)
        #call the wrapped mock view for the second time should
        #result in the request being limited
        wrapped_mock(self.request)
        self.assertTrue(self.request.limited)
        self.assertEqual(self.request.rate_limit_count, 2)
Пример #31
0
class RegistrationWeek1Accepted(UpdateView):
    template_name = "registration/week1_accepted.html"
    model = models.RegistrationWeek1
    context_object_name = "registration"

    @method_decorator(ratelimit(key="ip", rate="100/h"))
    def dispatch(self, request, *args, **kwargs):
        self.access_code = kwargs["access_code"]
        return UpdateView.dispatch(self, request, *args, **kwargs)

    def get_object(self):
        return UpdateView.get_queryset(self).get(
            access_code=self.access_code,
            accepted=True,
        )

    def get_success_url(self):
        return reverse(
            "registration:week1-confirmation-done",
            kwargs={"access_code": self.access_code},
        )

    def get_form_class(self):
        return forms.RegistrationWeek1AcceptedForm
Пример #32
0
class NewsFeedViewSet(viewsets.GenericViewSet):
    permission_classes = [IsAuthenticated]
    pagination_class = EndlessPagination

    def get_queryset(self):
        # instead of using queryset() directly, we need to customize the method
        # since NewsFeed is only available to authenticated users
        return NewsFeed.objects.filter(user=self.request.user)

    @method_decorator(
        ratelimit(key='user', rate='5/s', method='GET', block=True))
    def list(self, request):
        cached_newsfeeds = NewsFeedService.get_cached_newsfeeds(
            request.user.id)
        page = self.paginator.paginate_cached_list(cached_newsfeeds, request)
        if page is None:
            queryset = NewsFeed.objects.filter(user=request.user)
            page = self.paginate_queryset(queryset)
        serializer = NewsFeedSerializer(
            page,
            context={'request': request},
            many=True,
        )
        return self.get_paginated_response(serializer.data)
Пример #33
0
 def dispatch(self, *args, **kwargs):
     return ratelimit(
         **self.get_ratelimit_config()
     )(super(RateLimitMixin, self).dispatch)(*args, **kwargs)
Пример #34
0
    elif not window.started():
        template_name = 'ctflex/board/waiting.html'
    elif window.ended():
        template_name = 'ctflex/board/ended.html'
    else:
        template_name = 'ctflex/board/current.html'

    return render(request, template_name, context)


# endregion

# region Auth

password_change = limited_http_methods('GET', 'POST')(
    ratelimit(key='user', method='POST', rate='4/m', block=True)(
        auth_views.password_change))

password_reset = limited_http_methods('GET', 'POST')(
    ratelimit(method='POST', key='ip', rate='4/m', block=True)(
        auth_views.password_reset))


@limited_http_methods('GET')
def logout_done(request, *,
                message="You have been logged out.",
                redirect_url='ctflex:index'):
    messages.success(request, message)
    return redirect(redirect_url)


@limited_http_methods('GET')
Пример #35
0
from ratelimit.decorators import ratelimit
from ratelimit.exceptions import Ratelimited


# See: https://www.bing.com/webmaster/help/how-to-verify-bingbot-3905dc26
# and: https://support.google.com/webmasters/answer/80553?hl=en
APPROVED_DOMAINS = [
    'google.com',
    'googlebot.com',
    'search.msn.com',
    'localhost',  # For dev.
]


ratelimiter = ratelimit(key='ip', rate='250/h', block=True)


def ratelimit_if_not_whitelisted(view):
    """A wrapper for the ratelimit function that adds a whitelist for approved
    crawlers.
    """
    ratelimited_view = ratelimiter(view)

    @functools.wraps(view)
    def wrapper(request, *args, **kwargs):
        try:
            return ratelimited_view(request, *args, **kwargs)
        except Ratelimited as e:
            if is_whitelisted(request):
                return view(request, *args, **kwargs)
Пример #36
0
Файл: urls.py Проект: AASHE/hub
django_admin_blocks.autodiscover()

urlpatterns = [

    url(r'^$', HomeView.as_view(), name='home'),
    url(r'^browse/', include('hub.apps.browse.urls', namespace='browse')),
    url(r'^submit-resource/',
        include('hub.apps.submit.urls', namespace='submit')),

    url(r'^api/v1/', include('hub.apps.api.urls', namespace='api')),

    # url(r'^login/$', login, name='login'),
    url(r'^login/$',
        ratelimit(
            key='post:username',
            rate=settings.LOGIN_RATE_LIMIT,
            block=True)(login),
        name='login'),
    url(r'^logout/$', logout, name='logout'),
    url(r'^_ad/', include(admin.site.urls)),

    # Link checking url pattern
    url(r'^_ad/linkcheck/', include('linkcheck.urls')),
    url(r'^s3direct/', include('s3direct.urls')),

    # Let's Encrypt (django-acme-challenge)
    url(r'^.well-known/acme-challenge/', include('acme_challenge.urls')),

    url(r'^', include('django.contrib.flatpages.urls')),
]
Пример #37
0
 def dispatch(self, *args, **kwargs):
     return ratelimit(rate='5/m')(super(ImageCreateView, self).dispatch)(*args, **kwargs)
Пример #38
0
        grouped_filters = []
        for group_options, filters in filter_groups.items():
            group_name, group_slug, group_order = group_options
            sorted_filters = sorted(filters, key=attrgetter('name'))
            grouped_filters.append(FilterGroup(name=group_name,
                                               slug=group_slug,
                                               order=group_order,
                                               options=sorted_filters))
        sorted_filters = sorted(grouped_filters,
                                key=attrgetter('order'),
                                reverse=True)
        return FacetedFilterSerializer(sorted_filters, many=True).data


# Since the search endpoint accepts user input (via query parameters) and its
# response is compressed, use rate limiting to mitigate the BREACH attack
# (see http://breachattack.com/). It still needs to allow a user to click
# the filter switches (bug 1426968).
# Alternate: forbid gzip by setting Content-Encoding: identity
search = never_cache(ratelimit(key='user_or_ip', rate='25/m', block=True)(
    SearchView.as_view()
))


@shared_cache_control(s_maxage=60 * 60 * 24 * 7)
def plugin(request):
    """Render an OpenSearch Plugin."""
    return render(request, 'search/plugin.html', {
        'locale': request.LANGUAGE_CODE
    }, content_type='application/opensearchdescription+xml')
Пример #39
0
logger = logging.getLogger("posts.views")


def ratelimited(request, *args, **kwargs):
    logger.info("Rate limit reached for request", request.META["CLIENT_IP"])
    return HttpResponse(status=429,
                        content="Too many requests, please slow down!")


GLOBAL_REQUEST_GROUP = "global"

limit = ratelimit(
    key="header:client-ip",
    method="GET",
    rate="5/s",
    block=True,
    group=GLOBAL_REQUEST_GROUP,
)


class Index(View):
    @method_decorator(limit)
    def get(self, request, **kwargs):
        previous_id = request.GET.get("p")
        post = self._random_post(previous_id=previous_id)
        if post is None:
            logger.debug("Exhausted all posts")
            messages.error(request, "No more posts to view, submit your own!")
            return redirect("submit")
        context = {"post": post}
Пример #40
0
from oauth2_provider.models import get_application_model
from oauth2_provider.exceptions import OAuthToolkitError
from oauth2_provider.http import HttpResponseUriRedirect
from ratelimit.decorators import ratelimit
from ..forms import AllowForm, SimpleAllowForm
from ..models import ExpiresIn
from oauth2_provider.forms import AllowForm as DotAllowForm
from django.conf import settings

logger = logging.getLogger('hhs_server.%s' % __name__)

__author__ = "Alan Viars"


@method_decorator(ratelimit(key='user_or_ip',
                            rate='5/m',
                            method=['GET', 'POST'],
                            block=True),
                  name='dispatch')
class AuthorizationView(DotAuthorizationView):
    """
    Override the base authorization view from dot to
    use the custom AllowForm.
    """
    form_class = SimpleAllowForm
    login_url = '/accounts/oauth2-login'
    template_name = getattr(settings, 'AUTHORIZATION_TEMPLATE_NAME',
                            "design_system/authorize.html")


@method_decorator(ratelimit(key='user_or_ip',
                            rate='5/m',
Пример #41
0
"""Route URLs to apps"""

from django.conf import settings
from django.conf.urls import include, url
from django.contrib import admin

from ratelimit.decorators import ratelimit

admin.autodiscover()
admin.site.login = (
    ratelimit(key='ip', rate='1/s', method='POST', block=True)(
        ratelimit(key='ip', rate='10/h', method='POST', block=True)(
            admin.site.login)))

urlpatterns = [
    url(r'^{}/'.format(settings.ADMIN_URL_PATH), include(admin.site.urls), name='admin'),
    url(r'', include('ctflex.urls')),
    url(r'', include('pactf_web.urls')),
]