Exemplo n.º 1
0
Arquivo: auth.py Projeto: betagouv/zam
    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})
        )
Exemplo n.º 2
0
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
Exemplo n.º 3
0
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
Exemplo n.º 4
0
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(),
    )
Exemplo n.º 5
0
        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)
Exemplo n.º 6
0
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(),
    )
Exemplo n.º 7
0
 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
Exemplo n.º 8
0
    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)
Exemplo n.º 9
0
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})
Exemplo n.º 10
0
Arquivo: auth.py Projeto: betagouv/zam
    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)
Exemplo n.º 11
0
 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)
Exemplo n.º 12
0
    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)
Exemplo n.º 13
0
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())