def form_valid(self, form): user = form.save() messages.success(self.request, mark_safe( "Successfully registered, you are now logged in! <a href='%s'>View your profile</a>" % reverse('user-detail', kwargs={'pk': user.id}))) user = authenticate(username=form.cleaned_data['username'], password=form.cleaned_data['password1']) login(self.request, user) is_ratelimited(request=self.request, group=self.ratelimit_group, key=self.ratelimit_key, rate=self.ratelimit_rate, increment=True) return super(RegistrationView, self).form_valid(form)
def process_request(self, request): encoding = request.META.get('HTTP_CONTENT_ENCODING', None) if encoding == 'gzip': if is_ratelimited(request, group='gunzip_request_middleware', key='ip', rate='300/1m', increment=True): return http.HttpResponse( 'Rate limit exceeded: too many gzipped request bodies', status=429) data = request._stream.read() if len(data) > settings.GUNZIP_MAX_COMPRESSED_SIZE: logger.warning('Compressed request body is too large: %s', request.path, extra={ 'status_code': 400, 'request': request, }) return http.HttpResponseBadRequest( 'Compressed request body is too large') try: zfile = GzipFile(mode='rb', fileobj=StringIO(data)) uncompressed = zfile.read() request._stream = LimitedStream(StringIO(uncompressed), len(uncompressed)) del request.META['HTTP_CONTENT_ENCODING'] except IOError: return http.HttpResponseBadRequest( 'Invalid content-encoding, could not gunzip') return None
def form_valid(self, form): user = form.save() messages.success( self.request, mark_safe( "Successfully registered, you are now logged in! <a href='%s'>View your profile</a>" % reverse('user-detail', kwargs={'pk': user.id}))) user = authenticate(username=form.cleaned_data['username'], password=form.cleaned_data['password1']) login(self.request, user) is_ratelimited(request=self.request, group=self.ratelimit_group, key=self.ratelimit_key, rate=self.ratelimit_rate, increment=True) return super(RegistrationView, self).form_valid(form)
def subscribe_main(request): """Subscription view for use with client side JS""" form = SubscribeForm(request.POST) if form.is_valid(): data = form.cleaned_data if email_is_blocked(data['email']): statsd.incr('news.views.subscribe_main.email_blocked') # don't let on there's a problem return respond_ok(request, data) data['format'] = data.pop('fmt') or 'H' if data['lang']: if not language_code_is_valid(data['lang']): data['lang'] = 'en' # if lang not provided get the best one from the accept-language header else: lang = get_best_request_lang(request) if lang: data['lang'] = lang else: del data['lang'] # now ensure that if we do have a lang that it's a supported one if 'lang' in data: data['lang'] = get_best_supported_lang(data['lang']) # if source_url not provided we should store the referrer header # NOTE this is not a typo; Referrer is misspelled in the HTTP spec # https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.36 if not data['source_url'] and request.META.get('HTTP_REFERER'): referrer = request.META['HTTP_REFERER'] if SOURCE_URL_RE.match(referrer): statsd.incr('news.views.subscribe_main.use_referrer') data['source_url'] = referrer if is_ratelimited(request, group='basket.news.views.subscribe_main', key=lambda x, y: '%s-%s' % (':'.join(data['newsletters']), data['email']), rate=EMAIL_SUBSCRIBE_RATE_LIMIT, increment=True): statsd.incr('subscribe.ratelimited') return respond_error(request, form, 'Rate limit reached', 429) try: upsert_user.delay(SUBSCRIBE, data, start_time=time()) except Exception: return respond_error(request, form, 'Unknown error', 500) return respond_ok(request, data) else: # form is invalid if request.is_ajax(): return HttpResponseJSON({ 'status': 'error', 'errors': format_form_errors(form.errors), 'errors_by_field': form.errors, }, 400) else: return render(request, 'news/formerror.html', {'form': form}, status=400)
def do_increment(request): return is_ratelimited(request, increment=True, method=is_ratelimited.ALL, key=get_key, rate='1/m', group='a')
def dispatch(self, request, *args, **kwargs): operation_name, is_mutation = get_gql_operation(request) if is_mutation: rate = MUTATION_RATELIMITS.get( operation_name, DEFAULT_MUTATION_RATE, ) else: rate = QUERY_RATELIMITS.get( operation_name, DEFAULT_QUERY_RATE, ) client_ip = get_client_ip(request) if is_ratelimited( request, group='', key=lambda group, request, client_ip=client_ip: client_ip, rate=rate, increment=True, ): logger.error( f'{get_client_ip(request)} exceeded rate limit threshold', ) return HttpResponseTooManyRequests() return super().dispatch(request, *args, **kwargs)
def confirm(request, token): if is_ratelimited(request, group='basket.news.views.confirm', key=lambda x, y: token, rate=EMAIL_SUBSCRIBE_RATE_LIMIT, increment=True): raise Ratelimited() confirm_user.delay(token, start_time=time()) return HttpResponseJSON({'status': 'ok'})
def dispatch(self, *args, **kwargs): self.setup_edx_ratelimit_config() request = args[0] request.limited = getattr(request, 'limited', False) rate = self.get_ratelimit_rate(request) ratelimited = is_ratelimited(request=request, group=self.ratelimit_group, fn=super(EdxRateLimitMixin, self).dispatch, key=self.ratelimit_key, rate=rate, method=self.ratelimit_methods, increment=True) if ratelimited and self.ratelimit_block: # add log to splunk logging.exception( 'Too many request: Rate limit of {} exceeded'.format( self.ratelimit_rate)) # add log to NR return JsonResponse(data={ 'message': 'Too many request: Rate limit of {} exceeded'.format( self.ratelimit_rate), 'status': False }, status=429) return super(EdxRateLimitMixin, self).dispatch(*args, **kwargs)
def process_request(self, request): encoding = request.META.get('HTTP_CONTENT_ENCODING', None) if encoding == 'gzip': if is_ratelimited(request, group='gunzip_request_middleware', key='ip', rate='300/1m', increment=True): return http.HttpResponse('Rate limit exceeded: too many gzipped request bodies', status=429) data = request._stream.read() if len(data) > settings.GUNZIP_MAX_COMPRESSED_SIZE: logger.warning('Compressed request body is too large: %s', request.path, extra = { 'status_code': 400, 'request': request, } ) return http.HttpResponseBadRequest('Compressed request body is too large') try: zfile = GzipFile(mode='rb', fileobj=StringIO(data)) uncompressed = zfile.read() request._stream = LimitedStream(StringIO(uncompressed), len(uncompressed)) del request.META['HTTP_CONTENT_ENCODING'] except IOError: return http.HttpResponseBadRequest('Invalid content-encoding, could not gunzip') return None
def _wrapped(root, info, **kw): request = info.context old_limited = getattr(request, "limited", False) if key and key.startswith("gql:"): _key = key.split("gql:")[1] value = kw.get(_key, None) if not value: raise ValueError(f"Cannot get key: {key}") request.gql_rl_field = value new_key = GQLRatelimitKey else: new_key = key ratelimited = is_ratelimited( request=request, group=group, fn=fn, key=new_key, rate=rate, method=method, increment=True, ) request.limited = ratelimited or old_limited if ratelimited and block: logger.warn("url:<%s> is denied for <%s> in Ratelimit" % (request.path, request.META["REMOTE_ADDR"])) raise Ratelimited("rate_limited") return fn(root, info, **kw)
def ratelimit(request, group="", keys=[None], increment=True): checkcaptcha = False for key in keys: if key == "ip": group = group key = "ip" else: group = group + "-{}".format(key) key = settings.GETKEY if is_ratelimited( request, group=group, key=key, rate=settings.DJANGO_RATE_LIMIT, increment=True, ): checkcaptcha = True if checkcaptcha: if not validatecaptcha(request): return True else: return False return False
def api_clusters(request): if is_ratelimited(request, key="ip", rate=API_REQUEST_LIMIT, group="api", increment=True): return HttpResponse(status=429) clusters = models.ClusterModel.objects.all() data = {} data["type"] = "FeatureCollection" data["features"] = [] for cluster in clusters: tmp = {} tmp["type"] = "Feature" tmp["geometry"] = {} tmp["geometry"]["type"] = "Point" tmp["geometry"]["coordinates"] = [cluster.geo_long, cluster.geo_lat] tmp["properties"] = {} data["features"].append(tmp) return HttpResponse(status=200, content_type="application/json", content=JSONRenderer().render(data))
def not_increment(request, delta): return is_ratelimited(request, method=is_ratelimited.ALL, group='a', key="ip", rate='10/m', increment=False, delta=delta)
def dispatch(self, request, *args, **kwargs): try: auth = request.META['HTTP_AUTHORIZATION'].split() except KeyError: logger.info('Missing auth header') return HttpResponseBadRequest() if len(auth) != 2: logger.info('Bad auth format') return HttpResponseBadRequest() if auth[0] != 'Bearer': logger.info('Wrong auth type') return HttpResponseBadRequest() try: unverified_payload = jwt.decode(auth[1], verify=False) except Exception as e: logger.info(f'{type(e)}: {str(e)}') return HttpResponseBadRequest() try: unverified_uid = str(unverified_payload['user']) except KeyError: logger.info('Missing user id from payload') return HttpResponseBadRequest() if is_ratelimited( request, group='', key=lambda group, request, uid=unverified_uid: uid, rate='88/m', increment=True, ): logger.error( f'{get_client_ip(request)} exceeded rate limit threshold for ' f'user {unverified_uid}', ) return HttpResponseTooManyRequests() try: payload = jwt.decode(auth[1]) except Exception as e: logger.info(f'{type(e)}: {str(e)}') return HttpResponseUnauthorized() try: user = User.objects.get(id=payload['user']) except User.DoesNotExist: logger.info('User does not exist; probably deleted account') return HttpResponseForbidden() except ValidationError as e: logger.info(f'{type(e)}: {str(e)}') return HttpResponseForbidden() request.user = user return super().dispatch(request, *args, **kwargs)
def dispatch(self, *args, **kwargs): ratelimited = is_ratelimited(request=self.request, group=self.ratelimit_group, key=self.ratelimit_key, rate=self.ratelimit_rate, increment=False) if ratelimited and self.ratelimit_block: raise Ratelimited() return super(RateLimitedFormView, self).dispatch(*args, **kwargs)
def subscribe_sms(request): mobile = request.POST.get("mobile_number") if not mobile: return HttpResponseJSON( { "status": "error", "desc": "mobile_number is missing", "code": errors.BASKET_USAGE_ERROR, }, 400, ) country = request.POST.get("country", "us") language = request.POST.get("lang", "en-US") msg_name = request.POST.get("msg_name", "SMS_Android") vendor_id = get_sms_vendor_id(msg_name, country, language) if not vendor_id: if language != "en-US": # if not available in the requested language, try the default language = "en-US" vendor_id = get_sms_vendor_id(msg_name, country, language) if not vendor_id: return HttpResponseJSON( { "status": "error", "desc": "Invalid msg_name + country + language", "code": errors.BASKET_USAGE_ERROR, }, 400, ) mobile = parse_phone_number(mobile, country) if not mobile: return HttpResponseJSON( { "status": "error", "desc": "mobile_number is invalid", "code": errors.BASKET_USAGE_ERROR, }, 400, ) # only rate limit numbers here so we don't rate limit errors. if is_ratelimited( request, group="basket.news.views.subscribe_sms", key=lambda x, y: "%s-%s" % (msg_name, mobile), rate=PHONE_NUMBER_RATE_LIMIT, increment=True, ): raise Ratelimited() optin = request.POST.get("optin", "N") == "Y" add_sms_user.delay(msg_name, mobile, optin, vendor_id=vendor_id) return HttpResponseJSON({"status": "ok"})
def get(self, request, *args, **kwargs): if 'kwd' in self.request.GET and is_ratelimited( request, key='header:x-forwarded-for', rate='20/h', increment=True, group=__name__ + '.KeywordSearchPage'): raise Ratelimited return super().get(request, *args, **kwargs)
def subscribe_main(request): """Subscription view for use with client side JS""" form = SubscribeForm(request.POST) if form.is_valid(): data = form.cleaned_data if email_is_blocked(data['email']): statsd.incr('news.views.subscribe_main.email_blocked') # don't let on there's a problem return respond_ok(request, data) data['format'] = data.pop('fmt') or 'H' if data['lang']: if not language_code_is_valid(data['lang']): data['lang'] = 'en' # if lang not provided get the best one from the accept-language header else: lang = get_best_request_lang(request) if lang: data['lang'] = lang else: del data['lang'] # if source_url not provided we should store the referrer header # NOTE this is not a typo; Referrer is misspelled in the HTTP spec # https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.36 if not data['source_url'] and request.META.get('HTTP_REFERER'): referrer = request.META['HTTP_REFERER'] if SOURCE_URL_RE.match(referrer): statsd.incr('news.views.subscribe_main.use_referrer') data['source_url'] = referrer if is_ratelimited(request, group='basket.news.views.subscribe_main', key=lambda x, y: '%s-%s' % (':'.join(data['newsletters']), data['email']), rate=EMAIL_SUBSCRIBE_RATE_LIMIT, increment=True): statsd.incr('subscribe.ratelimited') return respond_error(request, form, 'Rate limit reached', 429) try: upsert_user.delay(SUBSCRIBE, data, start_time=time()) except Exception: return respond_error(request, form, 'Unknown error', 500) return respond_ok(request, data) else: # form is invalid if request.is_ajax(): return HttpResponseJSON({ 'status': 'error', 'errors': format_form_errors(form.errors), 'errors_by_field': form.errors, }, 400) else: return render(request, 'news/formerror.html', {'form': form}, status=400)
def api_clusters_grid(request): if is_ratelimited(request, key="ip", rate=API_REQUEST_LIMIT, group="api", increment=True): return HttpResponse(status=429) data = {} data["type"] = "FeatureCollection" data["features"] = [] geo_lat = decimal.Decimal(-90) while True: tmp = {} tmp["type"] = "Feature" tmp["geometry"] = {} tmp["geometry"]["type"] = "LineString" tmp["geometry"]["coordinates"] = [[ -180 - models.CLUSTER_WIDTH_HALF, geo_lat - models.CLUSTER_HEIGHT_HALF ], [ 180 - models.CLUSTER_WIDTH_HALF, geo_lat - models.CLUSTER_HEIGHT_HALF ]] data["features"].append(tmp) geo_lat += models.CLUSTER_HEIGHT if geo_lat > 90: break geo_long = decimal.Decimal(-180) while True: tmp = {} tmp["type"] = "Feature" tmp["geometry"] = {} tmp["geometry"]["type"] = "LineString" tmp["geometry"]["coordinates"] = [[ geo_long - models.CLUSTER_WIDTH_HALF, -90 - models.CLUSTER_HEIGHT_HALF ], [ geo_long - models.CLUSTER_WIDTH_HALF, 90 - models.CLUSTER_HEIGHT_HALF ]] data["features"].append(tmp) geo_long += models.CLUSTER_WIDTH if geo_long > 180: break return HttpResponse(status=200, content_type="application/json", content=JSONRenderer().render(data))
def _login_view(request, event): errors = [] if request.method == "POST": key = request.POST.get("key", "") if len(key) == 0: errors.append(_("Access key must be given")) elif is_ratelimited(request, fn=_login_view, key=_ratelimit_key, rate=settings.KIRPPU_MOBILE_LOGIN_RATE_LIMIT, increment=True): errors.append(_("You are trying too much. Try again later.")) else: with transaction.atomic(): try: permit = TemporaryAccessPermit.objects.get(short_code=key) except TemporaryAccessPermit.DoesNotExist: errors.append(_("Invalid access key")) else: can_use = permit.state == TemporaryAccessPermit.STATE_UNUSED\ and permit.expiration_time >= timezone.now() TemporaryAccessPermitLog.objects.create( permit=permit, action=TemporaryAccessPermitLog.ACTION_USE if can_use else TemporaryAccessPermitLog.ACTION_TRY, address=shorten_text( get_ip(request) + "; " + request.META.get("REMOTE_HOST", ""), TemporaryAccessPermitLog._meta.get_field( "address").max_length, False), peer=shorten_text( request.META.get("HTTP_USER_AGENT", ""), TemporaryAccessPermitLog._meta.get_field( "peer").max_length, False)) if can_use: permit.state = TemporaryAccessPermit.STATE_IN_USE permit.save(update_fields=("state", )) request.session[_PERMIT_SESSION_KEY] = permit.pk return HttpResponseRedirect( reverse("kirppu:mobile", kwargs={"event_slug": event.slug})) else: errors.append(_("Invalid access key")) field = TemporaryAccessPermit._meta.get_field("short_code") min_length = first(field.validators, lambda v: v.code == "min_length") return render( request, "kirppu/vendor_status_login.html", { 'event': event, 'min_length': min_length.limit_value if min_length else 0, 'max_length': field.max_length, 'errors': errors, })
def _wrapped(request, *args, **kwargs): already_limited = getattr(request, 'limited', False) ratelimited = is_ratelimited( request=request, group=rulename, key=keyfun, rate=rate, method=['POST'], increment=True) if not already_limited and ratelimited: statsd.incr('throttled.' + rulename) return fn(request, *args, **kwargs)
def _wrapped(*args, **kwargs): request = args[0] if isinstance(args[0], HttpRequest) else args[1] request.limited = getattr(request, 'limited', False) if not getattr(settings, 'RATELIMIT_ENABLE', True): request.limited = False return fn(*args, **kwargs) rate_local = self.rate if callable(rate_local): rate_local = rate_local(self.group, request) if rate_local is None: """ This has been taken from the original ratelimit function. Ideally it should raise ImproperlyConfigured. """ return fn(*args, **kwargs) ban_duration = self._extract_ban_duration(self.ban, request) group_local = self.get_group(fn) key_value = self.get_key_value(group=group_local, request=request) ban_cache_key = self._make_ban_cache_key(group_local, rate_local, key_value, self.method, ban_duration) banned = self.is_banned(ban_cache_key) if banned and self.block: raise Ratelimited # Checks if the user is ratelimited. ratelimited = is_ratelimited(request=request, group=self.group, fn=fn, key=self.key, rate=self.rate, method=self.method, increment=True) if ratelimited: self.cache.add(ban_cache_key, ban_duration, ban_duration + self.EXPIRATION_FUDGE) if self.block: exception = Ratelimited() exception.banlimit_data = { "key": self.key, "key_value": key_value, "ban_duration": ban_duration } # Raise Ratelimited exception with details about banned entity. raise exception response = fn(*args, **kwargs) return response
def confirm(request, token): token = str(token) if is_ratelimited( request, group="basket.news.views.confirm", key=lambda x, y: token, rate=EMAIL_SUBSCRIBE_RATE_LIMIT, increment=True, ): raise Ratelimited() confirm_user.delay(token, start_time=time()) return HttpResponseJSON({"status": "ok"})
def _wrapped(*args, **kw): # Work as a CBV method decorator. if isinstance(args[0], HttpRequest): request = args[0] else: request = args[1] request.limited = getattr(request, 'limited', False) ratelimited = is_ratelimited(request=request, group=group, fn=fn, key=key, rate=rate, method=method, increment=True) if ratelimited and block: raise Ratelimited() return fn(*args, **kw)
def _wrapped(*args, **kw): # Work as a CBV method decorator. if isinstance(args[0], HttpRequest): request = args[0] else: request = args[1] request.limited = getattr(request, 'limited', False) ratelimited = is_ratelimited(request=request, group=group, fn=fn, key=key, rate=rate, method=method, increment=True, reset=reset) if ratelimited and block: raise Ratelimited() return fn(*args, **kw)
def _wrapped(request, *args, **kwargs): already_limited = getattr(request, 'limited', False) ratelimited = is_ratelimited(request=request, group=rulename, key=keyfun, rate=rate, method=['POST'], increment=True) if not already_limited and ratelimited: statsd.incr('throttled.' + rulename) return fn(request, *args, **kwargs)
def submit_flag(request, *, prob_id): """Grade a flag submission and return a JSON response""" # Define constants STATUS_FIELD = 'status' MESSAGE_FIELD = 'message' ALREADY_SOLVED_STATUS = -1 CORRECT_STATUS = 0 INCORRECT_STATUS = 1 ERROR_STATUS = 2 # Rate-limit if is_ratelimited(request, fn=submit_flag, key=queries.competitor_key, rate='2/s', increment=True): return JsonResponse({ STATUS_FIELD: ERROR_STATUS, MESSAGE_FIELD: "You are submitting flags too fast. Slow down!" }) # Process data from the request flag = request.POST.get('flag', '') competitor = request.user.competitor # Grade, catching errors try: correct, message = commands.submit_flag( prob_id=prob_id, competitor=competitor, flag=flag) except models.CtfProblem.DoesNotExist: raise Http404() except commands.ProblemAlreadySolvedException: status = ALREADY_SOLVED_STATUS message = "Your team has already solved this problem!" except commands.FlagAlreadyTriedException: status = ERROR_STATUS message = "Your team has already tried this flag!" except commands.FlagSubmissionNotAllowedException: status = ERROR_STATUS message = "Your timer must have expired; reload the page." except commands.EmptyFlagException: status = ERROR_STATUS message = "The flag was empty." except: status = ERROR_STATUS message = "Something went wrong; please report this to us if it persists." else: status = CORRECT_STATUS if correct else INCORRECT_STATUS return JsonResponse({ STATUS_FIELD: status, MESSAGE_FIELD: message })
def allow_request(self, request, view): already_limited = getattr(request, 'limited', False) ratelimited = is_ratelimited( request=request, group=self.rulename, key=self.keyfun, rate=self.rate, method=self.methods, increment=True) if ratelimited: if not already_limited: statsd.incr('throttled.' + self.rulename) return self.throttle_failure() # Did not trigger rate-limiting, so this request is allowed. return self.throttle_success()
def _wrapped(*args, **kw): # Work as a CBV method decorator. if isinstance(args[0], HttpRequest): request = args[0] else: request = args[1] request.limited = getattr(request, 'limited', False) ratelimited = is_ratelimited(request=request, group=group, fn=fn, key=key, rate=rate, method=method, increment=True) if ratelimited and block: print 'DEBUG - decorator' return HttpResponseForbidden() return fn(*args, **kw)
def _wrapped(*args, **kw): # Work as a CBV method decorator. if isinstance(args[0], HttpRequest): request = args[0] else: request = args[1] request.limited = getattr(request, 'limited', False) rate_limit_logging(request, *args, **kw) # rule-out exempt if check_bypassed_rules(request): #print("ruled-out") return fn(*args, **kw) # whitelist if check_bypassed_ip(request): #print("whitelisted") return fn(*args, **kw) if is_authenticated(request.user) is False \ and is_ratelimited(request=request, group=nlgroup, fn=fn, key="ip", rate=nlrate, method=method, increment=True): if redirecturl is None: # return redirect("/accounts/login/?next="+request.path) raise Ratelimited() else: return redirect("/accounts/login/?next=" + redirecturl) if is_authenticated(request.user) is True \ and is_ratelimited(request=request, group=group, fn=fn, key="user", rate=rate, method=method, increment=True): raise Ratelimited() return fn(*args, **kw)
def subscribe_sms(request): mobile = request.POST.get('mobile_number') if not mobile: return HttpResponseJSON( { 'status': 'error', 'desc': 'mobile_number is missing', 'code': errors.BASKET_USAGE_ERROR, }, 400) country = request.POST.get('country', 'us') language = request.POST.get('lang', 'en-US') msg_name = request.POST.get('msg_name', 'SMS_Android') vendor_id = get_sms_vendor_id(msg_name, country, language) if not vendor_id: if language != 'en-US': # if not available in the requested language, try the default language = 'en-US' vendor_id = get_sms_vendor_id(msg_name, country, language) if not vendor_id: return HttpResponseJSON( { 'status': 'error', 'desc': 'Invalid msg_name + country + language', 'code': errors.BASKET_USAGE_ERROR, }, 400) mobile = parse_phone_number(mobile, country) if not mobile: return HttpResponseJSON( { 'status': 'error', 'desc': 'mobile_number is invalid', 'code': errors.BASKET_USAGE_ERROR, }, 400) # only rate limit numbers here so we don't rate limit errors. if is_ratelimited(request, group='basket.news.views.subscribe_sms', key=lambda x, y: '%s-%s' % (msg_name, mobile), rate=PHONE_NUMBER_RATE_LIMIT, increment=True): raise Ratelimited() optin = request.POST.get('optin', 'N') == 'Y' add_sms_user.delay(msg_name, mobile, optin, vendor_id=vendor_id) return HttpResponseJSON({'status': 'ok'})
def check_rate(self,request, puzzle_id): request.puzzle = get_object_or_404(Puzzle, puzzle_id__iexact=puzzle_id) request.hunt = request.puzzle.episode.hunt request.team = request.hunt.team_from_user(request.user) limited = False if(request.team is not None): request.ratelimit_key = request.user.username limited = is_ratelimited(request, fn=PuzzleView.as_view(), key='user', rate='1/7s', method='POST', increment=True) #if (not limited and not request.hunt.is_public): # limited = is_ratelimited(request, fn=PuzzleView.as_view(), key=get_ratelimit_key, rate='5/m', method='POST', # increment=True) return limited or getattr(request, 'limited', False)
def allow_request(self, request, view): already_limited = getattr(request, 'limited', False) ratelimited = is_ratelimited(request=request, group=self.rulename, key=self.keyfun, rate=self.rate, method=self.methods, increment=True) if ratelimited: if not already_limited: statsd.incr('throttled.' + self.rulename) return self.throttle_failure() # Did not trigger rate-limiting, so this request is allowed. return self.throttle_success()
def is_click_ratelimited(request, ratelimits=None): """Returns ``True`` if this user is rate limited from clicking ads and ``False`` otherwise.""" if ratelimits is None: # Explicitly set the rate limits ONLY if the parameter is `None` # If it is an empty list, there's simply no rate limiting ratelimits = settings.ADSERVER_CLICK_RATELIMITS for rate in ratelimits: if is_ratelimited( request, group="ad.click", key=lambda _, req: get_client_ip(req), rate=rate, increment=True, ): return True return False
def subscribe_sms(request): mobile = request.POST.get('mobile_number') if not mobile: return HttpResponseJSON({ 'status': 'error', 'desc': 'mobile_number is missing', 'code': errors.BASKET_USAGE_ERROR, }, 400) country = request.POST.get('country', 'us') language = request.POST.get('lang', 'en-US') msg_name = request.POST.get('msg_name', 'SMS_Android') vendor_id = get_sms_vendor_id(msg_name, country, language) if not vendor_id: if language != 'en-US': # if not available in the requested language, try the default language = 'en-US' vendor_id = get_sms_vendor_id(msg_name, country, language) if not vendor_id: return HttpResponseJSON({ 'status': 'error', 'desc': 'Invalid msg_name + country + language', 'code': errors.BASKET_USAGE_ERROR, }, 400) mobile = parse_phone_number(mobile, country) if not mobile: return HttpResponseJSON({ 'status': 'error', 'desc': 'mobile_number is invalid', 'code': errors.BASKET_USAGE_ERROR, }, 400) # only rate limit numbers here so we don't rate limit errors. if is_ratelimited(request, group='basket.news.views.subscribe_sms', key=lambda x, y: '%s-%s' % (msg_name, mobile), rate=PHONE_NUMBER_RATE_LIMIT, increment=True): raise Ratelimited() optin = request.POST.get('optin', 'N') == 'Y' add_sms_user.delay(msg_name, mobile, optin, vendor_id=vendor_id) return HttpResponseJSON({'status': 'ok'})
def api_hotspot_content(request): if is_ratelimited(request, key="ip", rate=API_REQUEST_LIMIT, group="api", increment=True): return HttpResponse(status=429) if "uuid" in request.GET: uuid = request.GET["uuid"] else: return HttpResponse(status=404) hotspot = models.HotspotModel.objects.filter(uuid=uuid).first() if hotspot is None: return HttpResponse(status=404) return TemplateResponse(request, "details.html", {"hotspot": hotspot})
def post(self, request, *args, **kwargs): form = self.get_form() blocked = is_ratelimited( request, group=self.ratelimit_group, key=self.ratelimit_key, method=self.ratelimit_method, rate=self.ratelimit_rate, ) recent_captcha = (time.time() - request.session.get( "captcha_validation_time", 0)) < 86400 if blocked and not recent_captcha: form.fields["captcha"] = CaptchaField() if form.is_valid(): return self.form_valid(form) else: return self.form_invalid(form)
def api_hotspot(request): if is_ratelimited(request, key="ip", rate=API_REQUEST_LIMIT, group="api", increment=True): return HttpResponse(status=429) if "uuid" in request.GET: uuid = request.GET["uuid"] else: return HttpResponse(status=404) hotspot = models.HotspotModel.objects.filter(uuid=uuid).first() if hotspot is None: return HttpResponse(status=404) return HttpResponse(status=200, content_type="application/json", content=JSONRenderer().render( serializers.HotspotSerializer(hotspot).data))
def _wrapped(*args, **kw): # Work as a CBV method decorator. if isinstance(args[0], HttpRequest): request = args[0] else: request = args[1] request.limited = getattr(request, 'limited', False) conf = get_ratelimit_conf() if conf: ip_whitelist_rate = get_whitelist_rate(request, key, group, conf['ip_whitelist']) rate = ip_whitelist_rate if ip_whitelist_rate else conf['rate'] else: rate = '5/m' block = conf['block'] if conf['block'] else True method = conf['method'] if conf['method'] else ALL print rate ratelimited = is_ratelimited(request=request, group=group, fn=fn, key=key, rate=rate, method=method, increment=True) if ratelimited and block: logging.exception( 'Too many request: Rate limit of {} exceeded'.format(rate)) # add log to NR return JsonResponse(data={ 'message': 'Too many request: Rate limit of {} exceeded'.format(rate), 'status': False }, status=429) return fn(*args, **kw)
def subscribe_sms(request): if 'mobile_number' not in request.POST: return HttpResponseJSON({ 'status': 'error', 'desc': 'mobile_number is missing', 'code': errors.BASKET_USAGE_ERROR, }, 400) messages = get_sms_messages() msg_name = request.POST.get('msg_name', 'SMS_Android') if msg_name not in messages: return HttpResponseJSON({ 'status': 'error', 'desc': 'Invalid msg_name', 'code': errors.BASKET_USAGE_ERROR, }, 400) mobile = request.POST['mobile_number'] mobile = re.sub(r'\D+', '', mobile) if len(mobile) == 10: mobile = '1' + mobile elif len(mobile) != 11 or mobile[0] != '1': return HttpResponseJSON({ 'status': 'error', 'desc': 'mobile_number must be a US number', 'code': errors.BASKET_USAGE_ERROR, }, 400) # only rate limit numbers here so we don't rate limit errors. if is_ratelimited(request, group='news.views.subscribe_sms', key=lambda x, y: '%s-%s' % (msg_name, mobile), rate=PHONE_NUMBER_RATE_LIMIT, increment=True): raise Ratelimited() optin = request.POST.get('optin', 'N') == 'Y' add_sms_user.delay(msg_name, mobile, optin) return HttpResponseJSON({'status': 'ok'})
def not_increment(request): return is_ratelimited( request, increment=False, method=is_ratelimited.ALL, key=get_key, rate="1/m", group="a" )
def update_user_task(request, api_call_type, data=None, optin=False, sync=False): """Call the update_user task async with the right parameters. If sync==True, be sure to include the token in the response. Otherwise, basket can just do everything in the background. """ data = data or request.POST.dict() newsletters = parse_newsletters_csv(data.get('newsletters')) if newsletters: if api_call_type == SUBSCRIBE: all_newsletters = newsletter_and_group_slugs() + get_transactional_message_ids() else: all_newsletters = newsletter_slugs() private_newsletters = newsletter_private_slugs() for nl in newsletters: if nl not in all_newsletters: return HttpResponseJSON({ 'status': 'error', 'desc': 'invalid newsletter', 'code': errors.BASKET_INVALID_NEWSLETTER, }, 400) if api_call_type != UNSUBSCRIBE and nl in private_newsletters: if not has_valid_api_key(request): return HttpResponseJSON({ 'status': 'error', 'desc': 'private newsletter subscription requires a valid API key', 'code': errors.BASKET_AUTH_ERROR, }, 401) if 'lang' in data: if not language_code_is_valid(data['lang']): data['lang'] = 'en' elif 'accept_lang' in data: lang = get_best_language(get_accept_languages(data['accept_lang'])) if lang: data['lang'] = lang del data['accept_lang'] else: data['lang'] = 'en' # if lang not provided get the best one from the accept-language header else: data['lang'] = get_best_request_lang(request) or 'en' email = data.get('email') token = data.get('token') if not (email or token): return HttpResponseJSON({ 'status': 'error', 'desc': MSG_EMAIL_OR_TOKEN_REQUIRED, 'code': errors.BASKET_USAGE_ERROR, }, 400) if optin: data['optin'] = True if api_call_type == SUBSCRIBE and email and data.get('newsletters'): # only rate limit here so we don't rate limit errors. if is_ratelimited(request, group='basket.news.views.update_user_task.subscribe', key=lambda x, y: '%s-%s' % (data['newsletters'], email), rate=EMAIL_SUBSCRIBE_RATE_LIMIT, increment=True): raise Ratelimited() if api_call_type == SET and token and data.get('newsletters'): # only rate limit here so we don't rate limit errors. if is_ratelimited(request, group='basket.news.views.update_user_task.set', key=lambda x, y: '%s-%s' % (data['newsletters'], token), rate=EMAIL_SUBSCRIBE_RATE_LIMIT, increment=True): raise Ratelimited() if sync: statsd.incr('news.views.subscribe.sync') if settings.MAINTENANCE_MODE and not settings.MAINTENANCE_READ_ONLY: # save what we can upsert_user.delay(api_call_type, data, start_time=time()) # have to error since we can't return a token return HttpResponseJSON({ 'status': 'error', 'desc': 'sync is not available in maintenance mode', 'code': errors.BASKET_NETWORK_FAILURE, }, 400) try: user_data = get_user_data(email=email, token=token) except NewsletterException as e: return newsletter_exception_response(e) if not user_data: if not email: # must have email to create a user return HttpResponseJSON({ 'status': 'error', 'desc': MSG_EMAIL_OR_TOKEN_REQUIRED, 'code': errors.BASKET_USAGE_ERROR, }, 400) token, created = upsert_contact(api_call_type, data, user_data) return HttpResponseJSON({ 'status': 'ok', 'token': token, 'created': created, }) else: upsert_user.delay(api_call_type, data, start_time=time()) return HttpResponseJSON({ 'status': 'ok', })