Skip to content

Commit 212a097

Browse files
committed
Add AttackDefTeam
Added new model with two new routes under '/ctf/round2/list' and '/team/round2/powerups' to list a team's ctfs and used powerups respectively. Also added test rows in admin init for new models.
1 parent f5c32d6 commit 212a097

File tree

6 files changed

+160
-9
lines changed

6 files changed

+160
-9
lines changed

src/pwncore/config.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import os
22
from dataclasses import dataclass
3+
from enum import Enum
4+
from datetime import timedelta
35

46
"""
57
Sample messages:
@@ -40,8 +42,14 @@
4042
"insufficient_coins": 22,
4143
"user_or_email_exists": 23,
4244
"users_not_found": 24,
45+
"attack_def_team_not_found": 25
4346
}
4447

48+
class PowerUpType(str, Enum):
49+
SHIELD = "SHIELD"
50+
POINT_SIPHON = "POINT_SIPHON"
51+
SABOTAGE = "SABOTAGE"
52+
UPGRADE = "UPGRADE"
4553

4654
@dataclass
4755
class Config:
@@ -55,7 +63,7 @@ class Config:
5563
jwt_valid_duration: int
5664
hint_penalty: int
5765
max_members_per_team: int
58-
66+
powerups: dict[PowerUpType, dict[str, any]]
5967

6068
config = Config(
6169
development=True,
@@ -71,4 +79,21 @@ class Config:
7179
msg_codes=msg_codes,
7280
hint_penalty=50,
7381
max_members_per_team=3,
82+
powerups={
83+
PowerUpType.UPGRADE: {
84+
"cost": 100,
85+
},
86+
PowerUpType.SHIELD: {
87+
"duration": timedelta(seconds=10),
88+
"cost": 200,
89+
},
90+
PowerUpType.POINT_SIPHON: {
91+
"duration": timedelta(seconds=15),
92+
"cost": 150,
93+
},
94+
PowerUpType.SABOTAGE: {
95+
"duration": timedelta(seconds=5),
96+
"cost": 500,
97+
},
98+
}
7499
)

src/pwncore/models/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@
2525
Team_Pydantic,
2626
User_Pydantic,
2727
)
28+
from pwncore.models.round2 import (
29+
AttackDefProblem,
30+
AttackDefTeam,
31+
ActivatedPowerups,
32+
ActivatedPowerups_Pydantic
33+
)
2834
from pwncore.models.pre_event import (
2935
PreEventProblem,
3036
PreEventSolvedProblem,
@@ -52,6 +58,10 @@
5258
"PreEventProblem_Pydantic",
5359
"Problem_Pydantic",
5460
"BaseProblem",
61+
"AttackDefProblem",
62+
"AttackDefTeam",
63+
"ActivatedPowerups",
64+
"ActivatedPowerups_Pydantic"
5565
)
5666

5767

src/pwncore/models/round2.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from __future__ import annotations
2+
from datetime import datetime, timezone
3+
from tortoise import fields
4+
from tortoise.models import Model
5+
from tortoise.contrib.pydantic import pydantic_model_creator
6+
from pwncore.models import (
7+
Problem
8+
)
9+
from pwncore.config import config, PowerUpType
10+
11+
__all__ = (
12+
"AttackDefProblem",
13+
"AttackDefTeam",
14+
"ActivatedPowerups"
15+
)
16+
#TODO: Actually implement this.
17+
# For now this is a dummy class for testing AttackDefTeam.
18+
class AttackDefProblem(Model):
19+
problem: fields.OneToOneRelation[Problem] = fields.OneToOneField(
20+
"models.Problem"
21+
)
22+
attack_def_team: fields.ForeignKeyRelation[AttackDefTeam] = fields.ForeignKeyField(
23+
"models.AttackDefTeam", related_name="assigned_attack_def_problem"
24+
)
25+
26+
class AttackDefTeam(Model):
27+
team: fields.OneToOneRelation[Team] = fields.OneToOneField(
28+
"models.Team", null=False
29+
)
30+
assigned_attack_def_problem: fields.ReverseRelation(AttackDefProblem)
31+
32+
33+
class ActivatedPowerups(Model):
34+
id = fields.IntField(pk=True)
35+
used_by: fields.OneToOneRelation[AttackDefTeam] = fields.OneToOneField(
36+
"models.AttackDefTeam", related_name="powerups_used", null=False
37+
)
38+
used_on: fields.OneToOneNullableRelation[AttackDefTeam] = fields.OneToOneField(
39+
"models.AttackDefTeam", related_name="powerups_defended"
40+
)
41+
powerup_type = fields.data.CharEnumField(enum_type=PowerUpType, null=False)
42+
started_at = fields.data.DatetimeField(auto_now_add=True)
43+
44+
def is_active(self) -> bool:
45+
duration = config.powerups[self.powerup_type]["duration"]
46+
if ((self.started_at + duration) < datetime.now(timezone.utc)):
47+
return False
48+
49+
return True
50+
51+
class PydanticMeta:
52+
arbitrary_types_allowed = True
53+
computed = ("is_active",)
54+
55+
ActivatedPowerups_Pydantic = pydantic_model_creator(ActivatedPowerups)

src/pwncore/routes/admin.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@
66
from tortoise.transactions import atomic, in_transaction
77

88
import pwncore.containerASD as containerASD
9-
from pwncore.config import config
9+
from pwncore.config import config, PowerUpType
1010
from pwncore.models import (
1111
Hint,
1212
PreEventProblem,
1313
PreEventSolvedProblem,
1414
Problem,
1515
Team,
1616
User,
17+
AttackDefTeam,
18+
AttackDefProblem,
19+
ActivatedPowerups
1720
)
1821
from pwncore.models.ctf import SolvedProblem
1922
from pwncore.models.pre_event import PreEventUser
@@ -146,10 +149,37 @@ async def init_db(
146149
image_name="reg.lugvitc.net/key:latest",
147150
image_config={"PortBindings": {"22/tcp": [{}]}},
148151
)
152+
await Problem.create(
153+
name="GitGood2",
154+
description="How to master the art of solving CTFs? Git good nub.",
155+
author="Aadivishnu and Shoubhit",
156+
points=300,
157+
image_name="reg.lugvitc.net/key:latest",
158+
image_config={"PortBindings": {"22/tcp": [{}]}},
159+
)
149160
await Team.create(name="CID Squad", secret_hash=bcrypt.hash("veryverysecret"))
150-
await Team.create(
161+
triple_a_battery = await Team.create(
151162
name="Triple A battery", secret_hash=bcrypt.hash("chotiwali"), coins=20
152163
)
164+
triple_b_battery = await Team.create(
165+
name="Triple B battery", secret_hash=bcrypt.hash("chotiwali2"), coins=20
166+
)
167+
await AttackDefTeam.create(
168+
team_id=(await Team.get(name="Triple A battery")).id
169+
)
170+
await AttackDefTeam.create(
171+
team_id=(await Team.get(name="Triple B battery")).id
172+
)
173+
await AttackDefProblem.create(
174+
problem_id=(await Problem.get(name="GitGood2")).id,
175+
attack_def_team_id=(await AttackDefTeam.get(team_id=triple_b_battery.id)).id
176+
)
177+
await ActivatedPowerups.create(
178+
used_by_id=(await AttackDefTeam.get(team_id=triple_b_battery.id)).id,
179+
used_on_id = (await AttackDefTeam.get(team_id=triple_a_battery.id)).id,
180+
powerup_type = PowerUpType.SABOTAGE
181+
)
182+
153183
await PreEventUser.create(tag="23BCE1000", email="[email protected]")
154184
await PreEventUser.create(tag="23BRS1000", email="[email protected]")
155185
await PreEventSolvedProblem.create(user_id="23BCE1000", problem_id="1")

src/pwncore/routes/ctf/__init__.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
SolvedProblem,
1919
Team,
2020
ViewedHint,
21+
AttackDefProblem,
22+
AttackDefTeam
2123
)
2224
from pwncore.models.ctf import Problem_Pydantic
2325
from pwncore.routes.auth import RequireJwt
@@ -62,11 +64,7 @@ async def completed_problem_get(jwt: RequireJwt):
6264
)
6365
return problems
6466

65-
66-
@router.get("/list")
67-
async def ctf_list(jwt: RequireJwt):
68-
team_id = jwt["team_id"]
69-
problems = await Problem_Pydantic.from_queryset(Problem.filter(visible=True))
67+
async def calculate_problem_points(problems: [Problem_Pydantic], team_id: int) -> [Problem_Pydantic]:
7068
acc: dict[int, float] = defaultdict(lambda: 1.0)
7169
for k, v in map(
7270
lambda x: (x.hint.problem_id, HINTPENALTY[x.hint.order]), # type: ignore[attr-defined]
@@ -79,6 +77,20 @@ async def ctf_list(jwt: RequireJwt):
7977
i.points = int(acc[i.id] * i.points) # type: ignore[attr-defined]
8078
return problems
8179

80+
@router.get("/list")
81+
async def ctf_list(jwt: RequireJwt):
82+
team_id = jwt["team_id"]
83+
problems = await Problem_Pydantic.from_queryset(Problem.filter(visible=True))
84+
return await calculate_problem_points(problems, team_id)
85+
86+
@router.get("/round2/list")
87+
async def ctf_list(jwt: RequireJwt):
88+
team_id = jwt["team_id"]
89+
attack_def_team = await AttackDefTeam.get(team_id=team_id)
90+
if (attack_def_team is None):
91+
return {"msg_code": config.msg_codes["attack_def_team_not_found"]}
92+
problems = await Problem_Pydantic.from_queryset(Problem.filter(attackdefproblem__attack_def_team__id=attack_def_team.id))
93+
return await calculate_problem_points(problems, team_id)
8294

8395
async def update_points(req: Request, ctf_id: int):
8496
try:

src/pwncore/routes/team.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,16 @@
55
from tortoise.transactions import atomic
66

77
from pwncore.config import config
8-
from pwncore.models import Team, User, Team_Pydantic, User_Pydantic, Container
8+
from pwncore.models import (
9+
Team,
10+
User,
11+
Team_Pydantic,
12+
User_Pydantic,
13+
Container,
14+
ActivatedPowerups,
15+
ActivatedPowerups_Pydantic,
16+
AttackDefTeam
17+
)
918
from pwncore.routes.auth import RequireJwt
1019

1120
# from pwncore.routes.leaderboard import gcache
@@ -116,3 +125,13 @@ async def get_team_containers(response: Response, jwt: RequireJwt):
116125
)
117126

118127
return result
128+
129+
@router.get("/round2/powerups")
130+
async def get_team_powerups(response: Response, jwt:RequireJwt):
131+
team_id = jwt["team_id"]
132+
team = await Team.get(id=team_id)
133+
attack_def_team = await AttackDefTeam.get(team_id=team_id)
134+
if (attack_def_team is None):
135+
return {"msg_code": config.msg_codes["attack_def_team_not_found"]}
136+
activated_powerups = await ActivatedPowerups_Pydantic.from_queryset(ActivatedPowerups.filter(used_by_id=attack_def_team.id))
137+
return activated_powerups

0 commit comments

Comments
 (0)