CoolFace
Apppublic

SRNI-2005/ctf

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
__init__.py155 linesDownload Raw Back to dynamic_challenges
1from flask import Blueprint2 3from CTFd.exceptions.challenges import (4    ChallengeCreateException,5    ChallengeUpdateException,6)7from CTFd.models import Challenges, db8from CTFd.plugins import register_plugin_assets_directory9from CTFd.plugins.challenges import CHALLENGE_CLASSES, BaseChallenge10from CTFd.plugins.dynamic_challenges.decay import DECAY_FUNCTIONS, logarithmic11from CTFd.plugins.migrations import upgrade12 13 14class DynamicChallenge(Challenges):15    __mapper_args__ = {"polymorphic_identity": "dynamic"}16    id = db.Column(17        db.Integer, db.ForeignKey("challenges.id", ondelete="CASCADE"), primary_key=True18    )19    dynamic_initial = db.Column(db.Integer, default=0)20    dynamic_minimum = db.Column(db.Integer, default=0)21    dynamic_decay = db.Column(db.Integer, default=0)22    dynamic_function = db.Column(db.String(32), default="logarithmic")23 24    @property25    def initial(self):26        return self.dynamic_initial27 28    @initial.setter29    def initial(self, initial_value):30        self.dynamic_initial = initial_value31 32    @property33    def minimum(self):34        return self.dynamic_minimum35 36    @minimum.setter37    def minimum(self, minimum_value):38        self.dynamic_minimum = minimum_value39 40    @property41    def decay(self):42        return self.dynamic_decay43 44    @decay.setter45    def decay(self, decay_value):46        self.dynamic_decay = decay_value47 48    @property49    def function(self):50        return self.dynamic_function51 52    @function.setter53    def function(self, function_value):54        self.dynamic_function = function_value55 56    def __init__(self, *args, **kwargs):57        super(DynamicChallenge, self).__init__(**kwargs)58        try:59            self.value = kwargs["initial"]60        except KeyError:61            raise ChallengeCreateException("Missing initial value for challenge")62 63 64class DynamicValueChallenge(BaseChallenge):65    id = "dynamic"  # Unique identifier used to register challenges66    name = "dynamic"  # Name of a challenge type67    templates = (68        {  # Handlebars templates used for each aspect of challenge editing & viewing69            "create": "/plugins/dynamic_challenges/assets/create.html",70            "update": "/plugins/dynamic_challenges/assets/update.html",71            "view": "/plugins/dynamic_challenges/assets/view.html",72        }73    )74    scripts = {  # Scripts that are loaded when a template is loaded75        "create": "/plugins/dynamic_challenges/assets/create.js",76        "update": "/plugins/dynamic_challenges/assets/update.js",77        "view": "/plugins/dynamic_challenges/assets/view.js",78    }79    # Route at which files are accessible. This must be registered using register_plugin_assets_directory()80    route = "/plugins/dynamic_challenges/assets/"81    # Blueprint used to access the static_folder directory.82    blueprint = Blueprint(83        "dynamic_challenges",84        __name__,85        template_folder="templates",86        static_folder="assets",87    )88    challenge_model = DynamicChallenge89 90    @classmethod91    def calculate_value(cls, challenge):92        f = DECAY_FUNCTIONS.get(challenge.function, logarithmic)93        value = f(challenge)94 95        challenge.value = value96        db.session.commit()97        return challenge98 99    @classmethod100    def read(cls, challenge):101        """102        This method is in used to access the data of a challenge in a format processable by the front end.103 104        :param challenge:105        :return: Challenge object, data dictionary to be returned to the user106        """107        challenge = DynamicChallenge.query.filter_by(id=challenge.id).first()108        data = super().read(challenge)109        data.update(110            {111                "initial": challenge.initial,112                "decay": challenge.decay,113                "minimum": challenge.minimum,114                "function": challenge.function,115            }116        )117        return data118 119    @classmethod120    def update(cls, challenge, request):121        """122        This method is used to update the information associated with a challenge. This should be kept strictly to the123        Challenges table and any child tables.124 125        :param challenge:126        :param request:127        :return:128        """129        data = request.form or request.get_json()130 131        for attr, value in data.items():132            # We need to set these to floats so that the next operations don't operate on strings133            if attr in ("initial", "minimum", "decay"):134                try:135                    value = float(value)136                except (ValueError, TypeError):137                    raise ChallengeUpdateException(f"Invalid input for '{attr}'")138            setattr(challenge, attr, value)139 140        return DynamicValueChallenge.calculate_value(challenge)141 142    @classmethod143    def solve(cls, user, team, challenge, request):144        super().solve(user, team, challenge, request)145 146        DynamicValueChallenge.calculate_value(challenge)147 148 149def load(app):150    upgrade(plugin_name="dynamic_challenges")151    CHALLENGE_CLASSES["dynamic"] = DynamicValueChallenge152    register_plugin_assets_directory(153        app, base_path="/plugins/dynamic_challenges/assets/"154    )155