예제 #1
0
 def update(self, *args, **kwargs):
     try:
         super().update(*args, **kwargs)
     except ConnectionError as e:
         sentry.log_info(e)
     except Exception as e:
         sentry.log_info(e)
예제 #2
0
    def build_hit(self, request, data):
        hit = {}
        event_data = data.copy()

        user_id = data.get('user_id', request.user.id)
        ip, is_routable = get_client_ip(request)

        hit['api_key'] = self.api_key

        if user_id:
            user = User.objects.get(id=user_id)
            user_email = user.email

            invited_by = user.invited_by
            if invited_by:
                invited_by_id = invited_by.id
            else:
                invited_by_id = None

            user_properties = {
                'email': user_email,
                'first_name': user.first_name,
                'last_name': user.last_name,
                'reputation': user.reputation,
                'is_suspended': user.is_suspended,
                'probable_spammer': user.probable_spammer,
                'invited_by_id': invited_by_id
            }
            user_id = f'{user_email}_{user_id}'

            if len(user_id) < 5:
                user_id += '_____'
            event_data['user_id'] = user_id
        else:
            user_properties = {
                'email': '',
                'first_name': 'Anonymous',
                'reputation': 0,
                'is_suspended': False,
                'probable_spammer': False
            }
            event_data['user_id'] = '_Anonymous_'

        event_data['user_properties'] = user_properties

        if ip is not None:
            try:
                geo_info = geo.city(ip)
                event_data['ip'] = ip
                event_data['country'] = geo_info['country_name']
                event_data['city'] = geo_info['city']
                event_data['region'] = geo_info['region']
                event_data['dma'] = geo_info['dma_code']
                event_data['location_lat'] = geo_info['latitude']
                event_data['location_lng'] = geo_info['longitude']
            except Exception as e:
                log_info(e)

        hit['events'] = [event_data]
        self.hit = json.dumps(hit)
예제 #3
0
    def sift_check_user_content(self, request):
        # https://sift.com/developers/docs/python/decisions-api/decision-webhooks/authentication

        # Let's check whether this webhook actually came from Sift!
        # First let's grab the signature from the postback's headers
        postback_signature = request.headers.get("X-Sift-Science-Signature")

        # Next, let's try to assemble the signature on our side to verify
        key = SIFT_WEBHOOK_SECRET_KEY.encode("utf-8")
        postback_body = request.body

        h = hmac.new(key, postback_body, sha1)
        verification_signature = "sha1={}".format(h.hexdigest())

        if verification_signature == postback_signature:
            decision_id = request.data["decision"]["id"]
            user_id = request.data["entity"]["id"]
            user = User.objects.get(id=user_id)

            if not user.moderator or user.email not in EMAIL_WHITELIST:
                if "mark_as_probable_spammer_content_abuse" in decision_id:
                    log_info(
                        f"Possible Spammer - {user.id}: {user.first_name} {user.last_name} - {decision_id}"
                    )
                    user.set_probable_spammer()
                elif "suspend_user_content_abuse" in decision_id:
                    log_info(
                        f"Suspending User - {user.id}: {user.first_name} {user.last_name} - {decision_id}"
                    )
                    user.set_suspended(is_manual=False)
            serialized = UserSerializer(user)
            return Response(serialized.data, status=200)
        else:
            raise Exception("Sift verification signature mismatch")
예제 #4
0
    def process_response(self, request, response):
        if 'api' in request.path:
            try:
                self.prof.disable()

                out = StringIO()
                old_stdout = sys.stdout
                sys.stdout = out
                stats = pstats.Stats(self.prof, stream=out)

                stats.print_stats()

                sys.stdout = old_stdout
                stats_str = out.getvalue()
                total_time = str(stats.total_tt)
            except Exception as e:
                sentry.log_error(e)
                sentry.log_info(self.prof.stats, error=e)
                sentry.log_info(self.prof.getstats(), error=e)
                return response

            log_traceback.apply_async(
                (self.data, total_time, stats_str),
                priority=1,
            )

        return response
예제 #5
0
    def forward_event(self):
        headers = {'Content-Type': 'application/json', 'Accept': '*/*'}
        request = requests.post(self.api_url, data=self.hit, headers=headers)
        res = request.json()
        if request.status_code != 200:
            log_info(res)

        return res
 def _add_references(self, paper):
     try:
         if not TESTING:
             add_references.apply_async((paper.id, ),
                                        priority=5,
                                        countdown=30)
         else:
             add_references(paper.id)
     except Exception as e:
         sentry.log_info(e)
 def _add_orcid_authors(self, paper):
     try:
         if not TESTING:
             add_orcid_authors.apply_async((paper.id, ),
                                           priority=5,
                                           countdown=10)
         else:
             add_orcid_authors(paper.id)
     except Exception as e:
         sentry.log_info(e)
예제 #8
0
 def _delete_user_account(self, user, error=None):
     user_email = user.email
     email_exists = email_address_exists(user_email)
     if email_exists:
         user = User.objects.get(email=user_email)
         social_account_exists = SocialAccount.objects.filter(
             user=user).exists()
         if not social_account_exists:
             deletion_info = user.delete()
             sentry.log_info(deletion_info, error=error)
             return True
     return False
예제 #9
0
def email_notifications(request):
    """Handles AWS SNS email notifications."""

    data = request.data
    if type(request.data) is not dict:
        data = json.loads(request.data)

    data_type = None
    try:
        data_type = data['Type']
    except KeyError:
        raise ParseError(f'Did not find key `Type` in {data}')

    if data_type == 'SubscriptionConfirmation':
        url = data['SubscribeURL']
        resp = http_request('GET', url)
        if resp.status_code != 200:
            message = 'Failed to subscribe to SNS'
            log_request_error(resp, message)

    elif data_type == 'Notification':
        data_message = json.loads(data['Message'])
        if data_message['notificationType'] == 'Bounce':
            bounced_recipients = data_message['bounce']['bouncedRecipients']

            for b_r in bounced_recipients:
                email_address = b_r['emailAddress']
                # TODO: Sanitize email address before putting it in the db
                try:
                    recipient, created = EmailRecipient.objects.get_or_create(
                        email=email_address)
                    recipient.bounced()
                except Exception as e:
                    message = (
                        f'Failed handling bounced recipient: {email_address}')
                    error = EmailNotificationError(e, message)
                    print(error)
                    log_error(error, base_error=e)
    elif data_type == 'Complaint':
        message = (f'`email_notifications` received {data_type}')
        log_info(message)
    else:
        message = (
            f'`email_notifications` received unsupported type {data_type}')
        print(message)

    return Response({})
예제 #10
0
    def to_representation(self, value):
        """
        Serialize tagged objects to a simple textual representation.
        """
        if isinstance(value, Distribution):
            return DistributionSerializer(value,
                                          context={
                                              'exclude_stats': True
                                          }).data
        elif isinstance(value, Purchase):
            return PurchaseSerializer(value, context={
                'exclude_stats': True
            }).data
        elif isinstance(value, Withdrawal):
            return WithdrawalSerializer(value, context={
                'exclude_stats': True
            }).data

        sentry.log_info('No representation for ' + value)
        return None
예제 #11
0
def notify_editor_inactivity():
    User = apps.get_model('user.User')

    last_week = timezone.now() - timedelta(days=7)
    editors = User.objects.editors()
    inactive_contributors = editors.annotate(
        paper_count=Count(
            'id',
            filter=Q(
                contributions__contribution_type=Contribution.SUBMITTER,
                contributions__created_date__gte=last_week
            )
        ),
        comment_count=Count(
            'id',
            filter=Q(
                contributions__contribution_type=Contribution.COMMENTER,
                contributions__created_date__gte=last_week
            )
        ),
        total_contributions=F('paper_count') + F('comment_count')
    ).filter(
        total_contributions__lt=3
    )

    logging = []
    for inactive_contributor in inactive_contributors.iterator():
        paper_count = inactive_contributor.paper_count
        comment_count = inactive_contributor.comment_count
        logging.append(
            (
                inactive_contributor.email,
                f'Paper count: {paper_count}',
                f'Comment count: {comment_count}'
            )
        )
        inactive_contributor.notify_inactivity(
            paper_count,
            comment_count
        )
    log_info(logging)
예제 #12
0
    def update(self,
               thing,
               refresh=None,
               action='index',
               parallel=False,
               **kwargs):

        if refresh is not None:
            kwargs['refresh'] = refresh
        elif self.django.auto_refresh:
            kwargs['refresh'] = self.django.auto_refresh

        if isinstance(thing, models.Model):
            object_list = [thing]
        else:
            object_list = thing

        objects_to_remove = []
        objects_to_index = []
        for obj in object_list:
            if self.should_remove_from_index(obj):
                objects_to_remove.append(obj)
            else:
                objects_to_index.append(obj)

        try:
            self._bulk(self._get_actions(objects_to_index, action='index'),
                       parallel=parallel,
                       **kwargs)
            self._bulk(self._get_actions(objects_to_remove, action='delete'),
                       parallel=parallel,
                       **kwargs)
        except ConnectionError as e:
            sentry.log_info(e)
        except Exception as e:
            # The likely scenario is the result of removing objects
            # that do not exist in elastic search - 404s
            pass
예제 #13
0
def execute_editor_daily_payout_task():
    log_info(f"{APP_ENV}-running payout")
    result = editor_daily_payout_task()
    log_info(result)
예제 #14
0
    def subscribed_hub_papers(self, request):
        feed_type = "subscribed"
        user = request.user
        hubs = user.subscribed_hubs.all()
        page_number = int(request.GET["page"])
        start_date = datetime.datetime.fromtimestamp(
            int(request.GET.get("start_date__gte", 0)), datetime.timezone.utc)
        end_date = datetime.datetime.fromtimestamp(
            int(request.GET.get("end_date__lte", 0)), datetime.timezone.utc)
        ordering = self._set_hub_paper_ordering(request)

        if ordering == "-hot_score" and page_number == 1:
            papers = {}
            for hub in hubs.iterator():
                hub_name = hub.slug
                cache_key = get_cache_key("papers", hub_name)
                cache_hit = cache.get(cache_key)
                if cache_hit:
                    for hit in cache_hit:
                        paper_id = hit["id"]
                        abstract = hit.get("abstract", None)
                        if paper_id not in papers and abstract:
                            papers[paper_id] = hit
            papers = list(papers.values())

            if len(papers) < 1:
                qs = self.get_queryset(
                    include_autopull=True).order_by("-hot_score")
                papers = qs.filter(hubs__in=hubs).distinct()
            else:
                papers = sorted(papers, key=lambda paper: -paper["hot_score"])
                papers = papers[:UNIFIED_DOC_PAGE_SIZE]
                next_page = request.build_absolute_uri()
                if len(papers) < UNIFIED_DOC_PAGE_SIZE:
                    next_page = None
                else:
                    next_page = replace_query_param(next_page, "page", 2)
                res = {
                    "count": len(papers),
                    "next": next_page,
                    "results": {
                        "data": papers,
                        "no_results": False,
                        "feed_type": feed_type,
                    },
                }
                return Response(res, status=status.HTTP_200_OK)

        else:
            qs = self.get_queryset(
                include_autopull=True).order_by("-hot_score")
            papers = qs.filter(hubs__in=hubs).distinct()

        if papers.count() < 1:
            log_info(f"""
                    No hub papers found, retrieiving trending papers.
                    Page: {page_number}
                """)
            trending_pk = "0_-hot_score_today"
            cache_key_hub = get_cache_key("hub", trending_pk)
            cache_hit = cache.get(cache_key_hub)

            if cache_hit and page_number == 1:
                return Response(cache_hit)

            feed_type = "all"
            papers = self.get_queryset().order_by("-hot_score")

        context = self.get_serializer_context()
        context["user_no_balance"] = True
        context["exclude_promoted_score"] = True
        context["include_wallet"] = False

        order_papers = self.calculate_paper_ordering(papers, ordering,
                                                     start_date, end_date)

        page = self.paginate_queryset(order_papers)
        serializer = HubPaperSerializer(page, many=True, context=context)
        serializer_data = serializer.data

        return self.get_paginated_response({
            "data": serializer_data,
            "no_results": False,
            "feed_type": feed_type
        })
예제 #15
0
    def validate(self, attrs, retry=0):
        view = self.context.get('view')
        request = self._get_request()

        if not view:
            error = serializers.ValidationError(
                _("View is not defined, pass it as a context variable"))
            sentry.log_error(error)
            raise error

        adapter_class = getattr(view, 'adapter_class', None)
        if not adapter_class:
            error = serializers.ValidationError(
                _("Define adapter_class in view"))
            sentry.log_error(error)
            raise error

        adapter = adapter_class(request)
        app = adapter.get_provider().get_app(request)

        # More info on code vs access_token
        # http://stackoverflow.com/questions/8666316/facebook-oauth-2-0-code-and-token

        credential = attrs.get('credential')

        # Case 1: We received the access_token
        if attrs.get('access_token') or credential:
            access_token = attrs.get('access_token')
            if credential:
                access_token = credential

        # Case 2: We received the authorization code
        elif attrs.get('code'):
            self.callback_url = getattr(view, 'callback_url', None)
            self.client_class = getattr(view, 'client_class', None)

            if not self.callback_url:
                error = serializers.ValidationError(
                    _("Define callback_url in view"))
                sentry.log_error(error)
                raise error
            if not self.client_class:
                error = serializers.ValidationError(
                    _("Define client_class in view"))
                sentry.log_error(error)
                raise error

            code = attrs.get('code')

            provider = adapter.get_provider()
            scope = provider.get_scope(request)
            client = self.client_class(
                request,
                app.client_id,
                app.secret,
                adapter.access_token_method,
                adapter.access_token_url,
                'postmessage',  # This is the callback url
                scope)
            token = client.get_access_token(code)
            access_token = token['access_token']

        else:
            error = serializers.ValidationError(
                _("Incorrect input. access_token or code is required."))
            sentry.log_error(error)
            raise serializers.ValidationError(
                _("Incorrect input. access_token or code is required."))

        social_token = adapter.parse_token({'access_token': access_token})
        social_token.app = app

        login = None
        try:
            login = self.get_social_login(adapter, app, social_token,
                                          access_token)
            complete_social_login(request, login)
        except ConnectionTimeout:
            pass
        except NoReverseMatch as e:
            if 'account_inactive' in str(e):
                login_user = login.account.user
                tracked_login = events_api.track_login(login_user, '$failure',
                                                       request)
                update_user_risk_score(login_user, tracked_login)
                raise LoginError(None, 'Account is suspended')
        except Exception as e:
            error = LoginError(e, 'Login failed')
            sentry.log_info(error, error=e)
            if login:
                deleted = self._delete_user_account(login.user, error=e)
                if deleted and retry < 3:
                    return self.validate(attrs, retry=retry + 1)
            sentry.log_error(error, base_error=e)
            raise serializers.ValidationError(_("Incorrect value"))

        if login and not login.is_existing:
            # We have an account already signed up in a different flow
            # with the same email address: raise an exception.
            # This needs to be handled in the frontend. We can not just
            # link up the accounts due to security constraints
            if app_settings.UNIQUE_EMAIL:
                # Do we have an account already with this email address?
                account_exists = get_user_model().objects.filter(
                    email=login.user.email, ).exists()
                if account_exists:
                    sentry.log_info('User already registered with this e-mail')
                    deleted = self._delete_user_account(login.user)
                    if deleted and retry < 3:
                        return self.validate(attrs, retry=retry + 1)
                    raise serializers.ValidationError(
                        _("User already registered with this e-mail address."))

            login.lookup()
            login.save(request, connect=True)

        login_user = login.account.user
        attrs['user'] = login_user
        tracked_login = events_api.track_login(login_user, '$success', request)
        update_user_risk_score(login_user, tracked_login)

        try:
            visits = WebsiteVisits.objects.get(uuid=attrs['uuid'])
            visits.user = attrs['user']
            visits.save()
        except Exception as e:
            print(e)
            pass

        try:
            referral_code = attrs.get('referral_code')
            if referral_code:
                referral_user = User.objects.get(referral_code=referral_code)
                user = attrs['user']
                if referral_code and not user.invited_by and referral_user.id != user.id:
                    user.invited_by = referral_user
                    user.save()
        except Exception as e:
            print(e)
            sentry.log_error(e)
            pass

        request = self._get_request()
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')

        user = attrs['user']
        check_user_risk(user)

        if user.is_authenticated and not user.probable_spammer:
            try:
                country = geo.country(ip)
                user.country_code = country.get('country_code')
                user.save()
            except Exception as e:
                print(e)
                sentry.log_error(e)

        return attrs