コード例 #1
0
        def _inner(obj):
            func = key_func or self._key_func
            is_route = not isinstance(obj, Blueprint)
            name = "%s.%s" % (
                obj.__module__, obj.__name__
            ) if is_route else obj.name
            dynamic_limit, static_limits = None, []
            if callable(limit_value):
                dynamic_limit = LimitGroup(
                    limit_value, func, _scope, per_method, methods,
                    error_message, exempt_when, override_defaults,
                    deduct_when
                )
            else:
                try:
                    static_limits = list(
                        LimitGroup(
                            limit_value, func, _scope, per_method, methods,
                            error_message, exempt_when, override_defaults,
                            deduct_when
                        )
                    )
                except ValueError as e:
                    self.logger.error(
                        "failed to configure %s %s (%s)", "view function"
                        if is_route else "blueprint", name, e
                    )
            if isinstance(obj, Blueprint):
                if dynamic_limit:
                    self._blueprint_dynamic_limits.setdefault(
                        name, []
                    ).append(dynamic_limit)
                else:
                    self._blueprint_limits.setdefault(
                        name, []
                    ).extend(static_limits)
            else:
                self.__marked_for_limiting.setdefault(name, []).append(obj)
                if dynamic_limit:
                    self._dynamic_route_limits.setdefault(
                        name, []
                    ).append(dynamic_limit)
                else:
                    self._route_limits.setdefault(
                        name, []
                    ).extend(static_limits)

                @wraps(obj)
                def __inner(*a, **k):
                    if (
                        self._auto_check
                        and not g.get("_rate_limiting_complete")
                    ):
                        self.__check_request_limit(False)
                        g._rate_limiting_complete = True
                    return obj(*a, **k)
                return __inner
コード例 #2
0
ファイル: extension.py プロジェクト: BigRLab/flask-limiter
        def _inner(obj):
            func = key_func or self._key_func
            is_route = not isinstance(obj, Blueprint)
            name = "%s.%s" % (obj.__module__,
                              obj.__name__) if is_route else obj.name
            dynamic_limit, static_limits = None, []
            if callable(limit_value):
                dynamic_limit = LimitGroup(limit_value, func, _scope,
                                           per_method, methods, error_message,
                                           exempt_when)
            else:
                try:
                    static_limits = list(
                        LimitGroup(limit_value, func, _scope, per_method,
                                   methods, error_message, exempt_when))
                except ValueError as e:
                    self.logger.error(
                        "failed to configure %s %s (%s)",
                        "view function" if is_route else "blueprint", name, e)
            if isinstance(obj, Blueprint):
                if dynamic_limit:
                    self._blueprint_dynamic_limits.setdefault(
                        name, []).append(dynamic_limit)
                else:
                    self._blueprint_limits.setdefault(name,
                                                      []).extend(static_limits)
            else:

                @wraps(obj)
                def __inner(*a, **k):
                    return obj(*a, **k)

                if dynamic_limit:
                    self._dynamic_route_limits.setdefault(
                        name, []).append(dynamic_limit)
                else:
                    self._route_limits.setdefault(name,
                                                  []).extend(static_limits)
                return __inner
コード例 #3
0
    def __configure_fallbacks(self, app, strategy):
        config = app.config
        fallback_enabled = config.get(C.IN_MEMORY_FALLBACK_ENABLED, False)
        fallback_limits = config.get(C.IN_MEMORY_FALLBACK, None)
        if not self._in_memory_fallback and fallback_limits:
            self._in_memory_fallback = [
                LimitGroup(fallback_limits, self._key_func, None, False, None,
                           None, None, None, None)
            ]
        if not self._in_memory_fallback_enabled:
            self._in_memory_fallback_enabled = (
                fallback_enabled or len(self._in_memory_fallback) > 0)

        if self._in_memory_fallback_enabled:
            self._fallback_storage = MemoryStorage()
            self._fallback_limiter = STRATEGIES[strategy](
                self._fallback_storage)
コード例 #4
0
ファイル: extension.py プロジェクト: who0sy/flask-limiter
    def __init__(
        self,
        app=None,
        key_func=None,
        global_limits=[],
        default_limits=[],
        application_limits=[],
        headers_enabled=False,
        strategy=None,
        storage_uri=None,
        storage_options={},
        auto_check=True,
        swallow_errors=False,
        in_memory_fallback=[],
        retry_after=None,
        key_prefix="",
        enabled=True
    ):
        self.app = app
        self.logger = logging.getLogger("flask-limiter")

        self.enabled = enabled
        self._default_limits = []
        self._application_limits = []
        self._in_memory_fallback = []
        self._exempt_routes = set()
        self._request_filters = []
        self._headers_enabled = headers_enabled
        self._header_mapping = {}
        self._retry_after = retry_after
        self._strategy = strategy
        self._storage_uri = storage_uri
        self._storage_options = storage_options
        self._auto_check = auto_check
        self._swallow_errors = swallow_errors
        if not key_func:
            warnings.warn(
                "Use of the default `get_ipaddr` function is discouraged."
                " Please refer to https://flask-limiter.readthedocs.org/#rate-limit-domain"
                " for the recommended configuration", UserWarning
            )
        if global_limits:
            self.raise_global_limits_warning()

        self._key_func = key_func or get_ipaddr
        self._key_prefix = key_prefix

        for limit in set(global_limits + default_limits):
            self._default_limits.extend(
                [
                    LimitGroup(
                        limit, self._key_func, None, False, None, None, None
                    )
                ]
            )
        for limit in application_limits:
            self._application_limits.extend(
                [
                    LimitGroup(
                        limit, self._key_func, "global", False, None, None,
                        None
                    )
                ]
            )
        for limit in in_memory_fallback:
            self._in_memory_fallback.extend(
                [
                    LimitGroup(
                        limit, self._key_func, None, False, None, None, None
                    )
                ]
            )
        self._route_limits = {}
        self._dynamic_route_limits = {}
        self._blueprint_limits = {}
        self._blueprint_dynamic_limits = {}
        self._blueprint_exempt = set()
        self._storage = self._limiter = None
        self._storage_dead = False
        self._fallback_limiter = None
        self.__check_backend_count = 0
        self.__last_check_backend = time.time()
        self.__marked_for_limiting = {}

        class BlackHoleHandler(logging.StreamHandler):
            def emit(*_):
                return

        self.logger.addHandler(BlackHoleHandler())
        if app:
            self.init_app(app)
コード例 #5
0
ファイル: extension.py プロジェクト: who0sy/flask-limiter
    def init_app(self, app):
        """
        :param app: :class:`flask.Flask` instance to rate limit.
        """
        self.enabled = app.config.setdefault(C.ENABLED, self.enabled)
        self._swallow_errors = app.config.setdefault(
            C.SWALLOW_ERRORS, self._swallow_errors
        )
        self._headers_enabled = (
            self._headers_enabled
            or app.config.setdefault(C.HEADERS_ENABLED, False)
        )
        self._storage_options.update(app.config.get(C.STORAGE_OPTIONS, {}))
        self._storage = storage_from_string(
            self._storage_uri
            or app.config.setdefault(C.STORAGE_URL, 'memory://'),
            **self._storage_options
        )
        strategy = (
            self._strategy
            or app.config.setdefault(C.STRATEGY, 'fixed-window')
        )
        if strategy not in STRATEGIES:
            raise ConfigurationError(
                "Invalid rate limiting strategy %s" % strategy
            )
        self._limiter = STRATEGIES[strategy](self._storage)
        self._header_mapping.update(
            {
                HEADERS.RESET:
                self._header_mapping.get(HEADERS.RESET, None)
                or app.config.setdefault(C.HEADER_RESET, "X-RateLimit-Reset"),
                HEADERS.REMAINING:
                self._header_mapping.get(HEADERS.REMAINING, None)
                or app.config.setdefault(
                    C.HEADER_REMAINING, "X-RateLimit-Remaining"
                ),
                HEADERS.LIMIT:
                self._header_mapping.get(HEADERS.LIMIT, None)
                or app.config.setdefault(C.HEADER_LIMIT, "X-RateLimit-Limit"),
                HEADERS.RETRY_AFTER:
                self._header_mapping.get(HEADERS.RETRY_AFTER, None)
                or app.config.setdefault(C.HEADER_RETRY_AFTER, "Retry-After"),
            }
        )
        self._retry_after = (
            self._retry_after or app.config.get(C.HEADER_RETRY_AFTER_VALUE)
        )
        self._key_prefix = (self._key_prefix or app.config.get(C.KEY_PREFIX))
        app_limits = app.config.get(C.APPLICATION_LIMITS, None)
        if not self._application_limits and app_limits:
            self._application_limits = [
                LimitGroup(
                    app_limits, self._key_func, "global", False, None, None,
                    None
                )
            ]

        if app.config.get(C.GLOBAL_LIMITS, None):
            self.raise_global_limits_warning()
        conf_limits = app.config.get(
            C.GLOBAL_LIMITS, app.config.get(C.DEFAULT_LIMITS, None)
        )
        if not self._default_limits and conf_limits:
            self._default_limits = [
                LimitGroup(
                    conf_limits, self._key_func, None, False, None, None, None
                )
            ]
        fallback_limits = app.config.get(C.IN_MEMORY_FALLBACK, None)
        if not self._in_memory_fallback and fallback_limits:
            self._in_memory_fallback = [
                LimitGroup(
                    fallback_limits, self._key_func, None, False, None, None,
                    None
                )
            ]

        if self._in_memory_fallback:
            self._fallback_storage = MemoryStorage()
            self._fallback_limiter = STRATEGIES[strategy](
                self._fallback_storage
            )

        # purely for backward compatibility as stated in flask documentation
        if not hasattr(app, 'extensions'):
            app.extensions = {}  # pragma: no cover

        if not app.extensions.get('limiter'):
            if self._auto_check:
                app.before_request(self.__check_request_limit)
            app.after_request(self.__inject_headers)

        app.extensions['limiter'] = self
コード例 #6
0
    def init_app(self, app):
        """
        :param app: :class:`flask.Flask` instance to rate limit.
        """
        config = app.config
        self.enabled = config.setdefault(C.ENABLED, self.enabled)
        if not self.enabled:
            return

        self._default_limits_per_method = config.setdefault(
            C.DEFAULT_LIMITS_PER_METHOD, self._default_limits_per_method
        )
        self._default_limits_exempt_when = config.setdefault(
            C.DEFAULT_LIMITS_EXEMPT_WHEN, self._default_limits_exempt_when
        )
        self._default_limits_deduct_when = config.setdefault(
            C.DEFAULT_LIMITS_DEDUCT_WHEN, self._default_limits_deduct_when
        )
        self._swallow_errors = config.setdefault(
            C.SWALLOW_ERRORS, self._swallow_errors
        )
        self._headers_enabled = (
            self._headers_enabled
            or config.setdefault(C.HEADERS_ENABLED, False)
        )
        self._storage_options.update(config.get(C.STORAGE_OPTIONS, {}))
        self._storage = storage_from_string(
            self._storage_uri
            or config.setdefault(C.STORAGE_URL, 'memory://'),
            **self._storage_options
        )
        strategy = (
            self._strategy
            or config.setdefault(C.STRATEGY, 'fixed-window')
        )
        if strategy not in STRATEGIES:
            raise ConfigurationError(
                "Invalid rate limiting strategy %s" % strategy
            )
        self._limiter = STRATEGIES[strategy](self._storage)

        # TODO: this should be made consistent with the rest of the
        #  configuration
        self._header_mapping = {
            HEADERS.RESET: self._header_mapping.get(
                HEADERS.RESET, config.get(
                    C.HEADER_RESET, "X-RateLimit-Reset"
                )
            ),
            HEADERS.REMAINING: self._header_mapping.get(
                HEADERS.REMAINING, config.get(
                    C.HEADER_REMAINING, "X-RateLimit-Remaining"
                )
            ),
            HEADERS.LIMIT: self._header_mapping.get(
                HEADERS.LIMIT, config.get(
                    C.HEADER_LIMIT, "X-RateLimit-Limit"
                )
            ),
            HEADERS.RETRY_AFTER: self._header_mapping.get(
                HEADERS.RETRY_AFTER, config.get(
                    C.HEADER_RETRY_AFTER, "Retry-After"
                )
            ),
        }
        self._retry_after = (
            self._retry_after or config.get(C.HEADER_RETRY_AFTER_VALUE)
        )

        self._key_prefix = (self._key_prefix or config.get(C.KEY_PREFIX))

        app_limits = config.get(C.APPLICATION_LIMITS, None)
        if not self._application_limits and app_limits:
            self._application_limits = [
                LimitGroup(
                    app_limits, self._key_func, "global", False, None, None,
                    None, None, None
                )
            ]

        if config.get(C.GLOBAL_LIMITS, None):
            self.__raise_global_limits_warning()

        conf_limits = config.get(
            C.GLOBAL_LIMITS, config.get(C.DEFAULT_LIMITS, None)
        )
        if not self._default_limits and conf_limits:
            self._default_limits = [
                LimitGroup(
                    conf_limits, self._key_func, None, False, None, None,
                    None, None, None
                )
            ]
        for limit in self._default_limits:
            limit.per_method = self._default_limits_per_method
            limit.exempt_when = self._default_limits_exempt_when
            limit.deduct_when = self._default_limits_deduct_when

        self.__configure_fallbacks(app, strategy)

        # purely for backward compatibility as stated in flask documentation
        if not hasattr(app, 'extensions'):
            app.extensions = {}  # pragma: no cover

        if not app.extensions.get('limiter'):
            if self._auto_check:
                app.before_request(self.__check_request_limit)
            app.after_request(self.__inject_headers)

        app.extensions['limiter'] = self
        self.initialized = True