SRNI-2005/ctf
0
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 