diff --git a/.gitignore b/.gitignore index 68bc17f..b2c4db2 100644 --- a/.gitignore +++ b/.gitignore @@ -158,3 +158,5 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ + +src/pwncore/utils.py \ No newline at end of file diff --git a/src/pwncore/models/ctf.py b/src/pwncore/models/ctf.py index 8b5f0da..227d728 100644 --- a/src/pwncore/models/ctf.py +++ b/src/pwncore/models/ctf.py @@ -1,5 +1,7 @@ from __future__ import annotations +from math import tanh + from tortoise.models import Model from tortoise import fields from tortoise.contrib.pydantic import pydantic_model_creator @@ -30,14 +32,29 @@ class Problem(BaseProblem): null=True ) # type: ignore[assignment] + mi = fields.IntField(default=100) # Arbitrary meaning full defaults + ma = fields.IntField(default=600) + hints: fields.ReverseRelation[Hint] + class PydanticMeta: + exclude = ["image_name", "image_config"] + + async def _solves(self) -> int: + return await SolvedProblem.filter(problem=self).count() + + async def update_points(self) -> None: + self.points = round( + self.mi + (self.ma - self.mi) * (1 - tanh((await self._solves()) / 25)) + ) + await self.save() + class Hint(Model): id = fields.IntField(pk=True) order = fields.SmallIntField() # 0, 1, 2 problem: fields.ForeignKeyRelation[Problem] = fields.ForeignKeyField( - "models.Problem" + "models.Problem", related_name="hints" ) text = fields.TextField() @@ -59,7 +76,9 @@ class Meta: class ViewedHint(Model): - team: fields.ForeignKeyRelation[Team] = fields.ForeignKeyField("models.Team") + team: fields.ForeignKeyRelation[Team] = fields.ForeignKeyField( + "models.Team", related_name="viewedhints" + ) hint: fields.ForeignKeyRelation[Hint] = fields.ForeignKeyField( "models.Hint", ) diff --git a/src/pwncore/routes/admin.py b/src/pwncore/routes/admin.py index 27cf541..b5fb468 100644 --- a/src/pwncore/routes/admin.py +++ b/src/pwncore/routes/admin.py @@ -57,6 +57,8 @@ async def init_db(): points=300, image_name="key:latest", image_config={"PortBindings": {"22/tcp": [{}]}}, + mi=200, + ma=400, ) await PreEventProblem.create( name="Static_test", @@ -81,6 +83,8 @@ async def init_db(): points=300, image_name="key:latest", image_config={"PortBindings": {"22/tcp": [{}]}}, + mi=200, + ma=400, ) await Problem.create( name="GitGood", diff --git a/src/pwncore/routes/ctf/__init__.py b/src/pwncore/routes/ctf/__init__.py index 4603c51..aa74c88 100644 --- a/src/pwncore/routes/ctf/__init__.py +++ b/src/pwncore/routes/ctf/__init__.py @@ -1,5 +1,8 @@ from __future__ import annotations +from asyncio import create_task +from logging import getLogger + from fastapi import APIRouter, Response from pydantic import BaseModel from tortoise.transactions import atomic @@ -30,6 +33,8 @@ router.include_router(start_router) router.include_router(pre_event_router) +logger = getLogger("routes.ctf") + class Flag(BaseModel): flag: str @@ -41,6 +46,14 @@ async def ctf_list(): return problems +async def update_points(ctf_id: int): + try: + p = await Problem.get(id=ctf_id) + await p.update_points() + except Exception: + logger.exception("An error occured while updating points") + + @atomic() @router.post("/{ctf_id}/flag") async def flag_post(ctf_id: int, flag: Flag, response: Response, jwt: RequireJwt): @@ -60,6 +73,7 @@ async def flag_post(ctf_id: int, flag: Flag, response: Response, jwt: RequireJwt ) if check_solved: await SolvedProblem.create(team_id=team_id, problem_id=ctf_id) + create_task(update_points(ctf_id)) return {"status": True} return {"status": False}