def post(self) -> Any: if self.ip_limiter.exceeded(self.request.remote_addr): return HTTPTooManyRequests() email = User.normalize_email(self.request.params.get("email")) if self.email_limiter.exceeded(email): return HTTPTooManyRequests() # Will usually be prevented by the browser (required) if not email: return self.invalid_email(email=email, reason="missing_email") # Will usually be prevented by the browser (type=email) if not User.email_is_well_formed(email): return self.invalid_email(email=email, reason="incorrect_email") # Will NOT be prevented by the browser (pattern=... is clumsy) if not User.email_is_allowed(email): return self.invalid_email(email=email, reason="incorrect_domain") token = self.create_auth_token(email) self.send_auth_token_email(token=token, email=email) self.log_successful_token_request(email) return HTTPFound( location=self.request.route_url("email_sent", _query={"email": email}) )
def failed_logins(exc, request): resp = HTTPTooManyRequests( "There have been too many unsuccessful login attempts. " "Try again later.", retry_after=exc.resets_in.total_seconds(), ) # TODO: This is kind of gross, but we need it for as long as the legacy # upload API exists and is supported. Once we get rid of that we can # get rid of this as well. resp.status = "{} {}".format(resp.status_code, "Too Many Failed Login Attempts") return resp
def failed_logins(exc, request): resp = HTTPTooManyRequests( "There have been too many unsuccessful login attempts. " "Try again later.", retry_after=exc.resets_in.total_seconds(), ) # TODO: This is kind of gross, but we need it for as long as the legacy # upload API exists and is supported. Once we get rid of that we can # get rid of this as well. resp.status = "{} {}".format(resp.status_code, "Too Many Failed Login Attempts") return resp
def unverified_emails(exc, request): return HTTPTooManyRequests( request. _("Too many emails have been added to this account without verifying " "them. Check your inbox and follow the verification links."), retry_after=exc.resets_in.total_seconds(), )
def wrapper(*args: Any, **kwargs: Any) -> Any: request = args[0] result = request.check_rate_limit(action_name) if not result.is_allowed: raise result.add_headers_to_response(HTTPTooManyRequests()) return func(*args, **kwargs)
def incomplete_password_resets(exc, request): return HTTPTooManyRequests( request._( "Too many password resets have been requested for this account without " "completing them. Check your inbox and follow the verification links. " "(IP: ${ip})", mapping={"ip": request.remote_addr}, ), retry_after=exc.resets_in.total_seconds(), )
def get_departures(self): try: results = self.ot_api.get_departures(self.id, self.limit) except opentransapi.OpenTransRateLimitException as e: raise HTTPTooManyRequests(str(e)) # limit API exceeded except opentransapi.OpenTransNoStationException as e: raise HTTPNotFound(str(e)) # no station for this request except (RequestException, opentransapi.OpenTransException) as e: raise HTTPServiceUnavailable(str(e)) return results
def tween(request): log.debug('RATE LIMITING FOR METHOD ' + request.method) # Only write requests are considered for rate limiting. if request.method not in ['POST', 'PUT', 'DELETE']: return handler(request) if request.authorization is None: # See comment of similar block in jwt_database_validation tween return handler(request) user = DBSession.query(User).get(request.authenticated_userid) if user is None: return http_error_handler(HTTPBadRequest('Unknown user'), request) now = datetime.datetime.now(pytz.utc) if user.ratelimit_reset is None or user.ratelimit_reset < now: # No window exists or it is expired: create a new one. span = int(registry.settings.get('rate_limiting.window_span')) limit = int( registry.settings.get( 'rate_limiting.limit_robot' if user. robot else 'rate_limiting.limit_moderator' if user. moderator else 'rate_limiting.limit')) user.ratelimit_reset = now + datetime.timedelta(seconds=span) user.ratelimit_remaining = limit - 1 elif user.ratelimit_remaining: user.ratelimit_remaining -= 1 else: # User is rate limited # Count how many windows the user has been rate limited # and block them is too many. current_window = user.ratelimit_reset if user.ratelimit_last_blocked_window != current_window: user.ratelimit_last_blocked_window = current_window user.ratelimit_times += 1 max_times = int( registry.settings.get('rate_limiting.max_times')) if user.ratelimit_times > max_times: user.blocked = True # An alert message is sent to the moderators email_service = get_email_service(request) email_service.send_rate_limiting_alert(user) return http_error_handler( HTTPTooManyRequests('Rate limit reached'), request) return handler(request)
def rate_limiting(request, resource, section, to_increment=1): tsample = datetime.datetime.utcnow().replace(second=0, microsecond=0) key = REDIS_KEYS["rate_limits"][section].format(tsample, resource.resource_id) redis_pipeline = request.registry.redis_conn.pipeline() redis_pipeline.incr(key, to_increment) redis_pipeline.expire(key, 3600 * 24) results = redis_pipeline.execute() current_count = results[0] config = ConfigService.by_key_and_section(section, "global") limit = config.value if config else 1000 if current_count > int(limit): log.info("RATE LIMITING: {}: {}, {}".format(section, resource, current_count)) abort_msg = "Rate limits are in effect for this application" raise HTTPTooManyRequests(abort_msg, headers={"X-AppEnlight": abort_msg})
def get(self) -> Any: if self.ip_limiter.exceeded(self.request.remote_addr): return HTTPTooManyRequests() if self.request.unauthenticated_userid: return HTTPFound(location=self.next_url) token = self.request.params.get("token") auth = repository.get_auth_token_data(token) if auth is None: self.log_failed_login_attempt(token) self.request.session.flash( Message( cls="error", text="Le lien est invalide ou a expiré. Merci de renouveler votre demande.", # noqa ) ) raise HTTPFound(location=self.request.route_url("login")) # Delete token from repository after it's been used successfully repository.delete_auth_token(token) email = auth["email"] user, created = get_one_or_create(User, email=email) if created: DBSession.flush() # so that the DB assigns a value to user.pk self.log_successful_login_attempt(email) user.last_login_at = datetime.utcnow() next_url = self.next_url if not user.name: next_url = self.request.route_url("welcome", _query={"source": next_url}) # Compute response headers for the session cookie headers = remember(self.request, user.pk) app_name = self.request.registry.settings["zam.app_name"] self.request.session.flash( Message(cls="success", text=f"Bienvenue dans {app_name} !") ) return HTTPFound(location=next_url, headers=headers)
def wrapped(context, request): ratelimiter = request.find_service(IRateLimiter, name="xmlrpc.client", context=None) metrics = request.find_service(IMetricsService, context=None) ratelimiter.hit(request.remote_addr) if not ratelimiter.test(request.remote_addr): metrics.increment("warehouse.xmlrpc.ratelimiter.exceeded", tags=[]) message = ( "The action could not be performed because there were too " "many requests by the client.") _resets_in = ratelimiter.resets_in(request.remote_addr) if _resets_in is not None: _resets_in = max(1, int(_resets_in.total_seconds())) message += f" Limit may reset in {_resets_in} seconds." raise XMLRPCWrappedError(HTTPTooManyRequests(message)) metrics.increment("warehouse.xmlrpc.ratelimiter.hit", tags=[]) return f(context, request)
def tween(request): log.debug('RATE LIMITING FOR METHOD ' + request.method) # Only write requests are considered for rate limiting. if request.method not in ['POST', 'PUT', 'DELETE']: return handler(request) if request.authorization is None: # See comment of similar block in jwt_database_validation tween return handler(request) user = DBSession.query(User).get(request.authenticated_userid) if user is None: return http_error_handler(HTTPBadRequest('Unknown user'), request) now = datetime.datetime.now(pytz.utc) if user.ratelimit_reset is None or user.ratelimit_reset < now: # No window exists or it is expired: create a new one. span = int(registry.settings.get('rate_limiting.window_span')) limit = int( registry.settings.get( 'rate_limiting.limit_robot' if user. robot else 'rate_limiting.limit_moderator' if user. moderator else 'rate_limiting.limit')) user.ratelimit_reset = now + datetime.timedelta(seconds=span) user.ratelimit_remaining = limit - 1 log.debug('RATE LIMITING, CREATE WINDOW SPAN : {}'.format( user.ratelimit_reset)) elif user.ratelimit_remaining: user.ratelimit_remaining -= 1 log.info('RATE LIMITING, REQUESTS REMAINING FOR {} : {}'.format( user.id, user.ratelimit_remaining)) else: # User is rate limited log.warning('RATE LIMIT REACHED FOR USER {}'.format(user.id)) # Count how many windows the user has been rate limited # and block them if too many. current_window = user.ratelimit_reset if user.ratelimit_last_blocked_window != current_window: user.ratelimit_last_blocked_window = current_window user.ratelimit_times += 1 max_times = int( registry.settings.get('rate_limiting.max_times')) if user.ratelimit_times > max_times: log.warning('RATE LIMIT BLOCK USER {}'.format(user.id)) user.blocked = True # An alert message is sent to the moderators email_service = get_email_service(request) try: email_service.send_rate_limiting_alert(user) except SMTPAuthenticationError: log.error('RATE LIMIT ALERT MAIL : AUTHENTICATION ERROR') return http_error_handler( HTTPTooManyRequests('Rate limit reached'), request) return handler(request)
def apply_rate_limit(request: Request, action_name: str) -> None: """Check the rate limit for an action, and raise HTTP 429 if it's exceeded.""" result = request.check_rate_limit(action_name) if not result.is_allowed: raise result.add_headers_to_response(HTTPTooManyRequests())