CoolFace
Apppublic

SRNI-2005/ctf

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
__init__.py254 linesDownload Raw Back to cache
1from functools import lru_cache, wraps2from hashlib import md53from time import monotonic_ns4 5from flask import current_app, request6from flask_caching import Cache, make_template_fragment_key7 8 9class CTFdCache(Cache):10    """11    This subclass exists to give flask-caching some additional features12    Ideally likely we should have our own isolated redis connection but that might introduce more issues13    """14 15    def inc(self, *args, **kwargs):16        """17        Support redis INCR in flask-caching18        Note that redis INCR does not expire by default19        https://github.com/pallets-eco/flask-caching/issues/41820        """21        inc = getattr(self.cache, "inc", None)22        if inc is not None and callable(inc):23            return inc(*args, **kwargs)24        raise NotImplementedError25 26    def expire(self, key, timeout):27        """28        Support redis EXPIRE in flask-caching29        """30        if current_app.config["CACHE_TYPE"] == "redis":31            return self.cache._write_client.expire(32                f"{self.cache.key_prefix}{key}", timeout33            )34        else:35            # Generic alternative that leverages flask-caching built-ins to do expiration36            if timeout <= 0:37                self.cache.delete(key)38            value = self.get(key)39            if value:40                self.set(key=key, value=value, timeout=timeout)41                return True42            return False43 44 45cache = CTFdCache()46 47 48def timed_lru_cache(timeout: int = 300, maxsize: int = 64, typed: bool = False):49    """50    lru_cache implementation that includes a time based expiry51 52    Parameters:53    seconds (int): Timeout in seconds to clear the WHOLE cache, default = 5 minutes54    maxsize (int): Maximum Size of the Cache55    typed (bool): Same value of different type will be a different entry56 57    Implmentation from https://gist.github.com/Morreski/c1d08a3afa4040815eafd3891e16b945?permalink_comment_id=3437689#gistcomment-343768958    """59 60    def wrapper_cache(func):61        func = lru_cache(maxsize=maxsize, typed=typed)(func)62        func.delta = timeout * 10**963        func.expiration = monotonic_ns() + func.delta64 65        @wraps(func)66        def wrapped_func(*args, **kwargs):67            if monotonic_ns() >= func.expiration:68                func.cache_clear()69                func.expiration = monotonic_ns() + func.delta70            return func(*args, **kwargs)71 72        wrapped_func.cache_info = func.cache_info73        wrapped_func.cache_clear = func.cache_clear74        return wrapped_func75 76    return wrapper_cache77 78 79def make_cache_key(path=None, key_prefix="view/%s"):80    """81    This function mostly emulates Flask-Caching's `make_cache_key` function so we can delete cached api responses.82    Over time this function may be replaced with a cleaner custom cache implementation.83    :param path:84    :param key_prefix:85    :return:86    """87    if path is None:88        path = request.endpoint89    cache_key = key_prefix % path90    return cache_key91 92 93def make_cache_key_with_query_string(allowed_params=None, query_string_hash=None):94    if allowed_params is None:95        allowed_params = []96 97    def _make_cache_key_with_query_string(path=None, key_prefix="view/%s/%s"):98        if path is None:99            path = request.endpoint100 101        if query_string_hash:102            args_hash = query_string_hash103        else:104            args_hash = calculate_param_hash(105                params=tuple(request.args.items(multi=True)),106                allowed_params=allowed_params,107            )108        cache_key = key_prefix % (path, args_hash)109        return cache_key110 111    return _make_cache_key_with_query_string112 113 114def calculate_param_hash(params, allowed_params=None):115    # Copied from Flask-Caching but modified to allow only accepted parameters116    if allowed_params:117        args_as_sorted_tuple = tuple(118            sorted(pair for pair in params if pair[0] in allowed_params)119        )120    else:121        args_as_sorted_tuple = tuple(sorted(pair for pair in params))122    args_hash = md5(str(args_as_sorted_tuple).encode()).hexdigest()  # nosec B303 B324123    return args_hash124 125 126def clear_config():127    from CTFd.utils import _get_config, get_app_config128 129    cache.delete_memoized(_get_config)130    cache.delete_memoized(get_app_config)131 132 133def clear_standings():134    from CTFd.api import api135    from CTFd.api.v1.scoreboard import ScoreboardDetail, ScoreboardList136    from CTFd.constants.static import CacheKeys137    from CTFd.models import Teams, Users  # noqa: I001138    from CTFd.utils.scoreboard import get_scoreboard_detail139    from CTFd.utils.scores import get_standings, get_team_standings, get_user_standings140    from CTFd.utils.user import (141        get_team_place,142        get_team_score,143        get_user_place,144        get_user_score,145    )146 147    # Clear out the bulk standings functions148    cache.delete_memoized(get_standings)149    cache.delete_memoized(get_team_standings)150    cache.delete_memoized(get_user_standings)151    cache.delete_memoized(get_scoreboard_detail)152 153    # Clear out the individual helpers for accessing score via the model154    cache.delete_memoized(Users.get_score)155    cache.delete_memoized(Users.get_place)156    cache.delete_memoized(Teams.get_score)157    cache.delete_memoized(Teams.get_place)158 159    # Clear the Jinja Attrs constants160    cache.delete_memoized(get_user_score)161    cache.delete_memoized(get_user_place)162    cache.delete_memoized(get_team_score)163    cache.delete_memoized(get_team_place)164 165    # Clear out HTTP request responses166    cache.delete(make_cache_key(path=api.name + "." + ScoreboardList.endpoint))167    cache.delete(make_cache_key(path=api.name + "." + ScoreboardDetail.endpoint))168    cache.delete_memoized(ScoreboardList.get)169    cache.delete_memoized(ScoreboardDetail.get)170 171    # Clear out scoreboard templates172    cache.delete(make_template_fragment_key(CacheKeys.PUBLIC_SCOREBOARD_TABLE))173 174 175def clear_challenges():176    from CTFd.utils.challenges import get_all_challenges  # noqa: I001177    from CTFd.utils.challenges import (178        get_rating_average_for_challenge_id,179        get_solve_counts_for_challenges,180        get_solve_ids_for_user_id,181        get_solves_for_challenge_id,182        get_submissions_for_user_id_for_challenge_id,183    )184 185    cache.delete_memoized(get_all_challenges)186    cache.delete_memoized(get_solves_for_challenge_id)187    cache.delete_memoized(get_submissions_for_user_id_for_challenge_id)188    cache.delete_memoized(get_solve_ids_for_user_id)189    cache.delete_memoized(get_solve_counts_for_challenges)190    cache.delete_memoized(get_rating_average_for_challenge_id)191 192 193def clear_ratings():194    from CTFd.utils.challenges import get_rating_average_for_challenge_id195 196    cache.delete_memoized(get_rating_average_for_challenge_id)197 198 199def clear_pages():200    from CTFd.utils.config.pages import get_page, get_pages201 202    cache.delete_memoized(get_pages)203    cache.delete_memoized(get_page)204 205 206def clear_user_recent_ips(user_id):207    from CTFd.utils.user import get_user_recent_ips208 209    cache.delete_memoized(get_user_recent_ips, user_id=user_id)210 211 212def clear_user_session(user_id):213    from CTFd.utils.user import (  # noqa: I001214        get_user_attrs,215        get_user_place,216        get_user_recent_ips,217        get_user_score,218    )219 220    cache.delete_memoized(get_user_attrs, user_id=user_id)221    cache.delete_memoized(get_user_place, user_id=user_id)222    cache.delete_memoized(get_user_score, user_id=user_id)223    cache.delete_memoized(get_user_recent_ips, user_id=user_id)224 225 226def clear_all_user_sessions():227    from CTFd.utils.user import (  # noqa: I001228        get_user_attrs,229        get_user_place,230        get_user_recent_ips,231        get_user_score,232    )233 234    cache.delete_memoized(get_user_attrs)235    cache.delete_memoized(get_user_place)236    cache.delete_memoized(get_user_score)237    cache.delete_memoized(get_user_recent_ips)238 239 240def clear_team_session(team_id):241    from CTFd.utils.user import get_team_attrs, get_team_place, get_team_score242 243    cache.delete_memoized(get_team_attrs, team_id=team_id)244    cache.delete_memoized(get_team_place, team_id=team_id)245    cache.delete_memoized(get_team_score, team_id=team_id)246 247 248def clear_all_team_sessions():249    from CTFd.utils.user import get_team_attrs, get_team_place, get_team_score250 251    cache.delete_memoized(get_team_attrs)252    cache.delete_memoized(get_team_place)253    cache.delete_memoized(get_team_score)254