From 8410cc33da4037350811001f6eba6f260507b0ac Mon Sep 17 00:00:00 2001 From: faraphel Date: Sun, 26 Jan 2025 11:26:35 +0100 Subject: [PATCH] added models and the flag page --- source/apps/ctf/admin.py | 16 ++- source/apps/ctf/apps.py | 2 +- source/apps/ctf/forms.py | 9 ++ source/apps/ctf/models.py | 64 ++++++++++- source/apps/ctf/templates/ctf/base/base.html | 1 + source/apps/ctf/templates/ctf/flag_list.html | 105 ++++++++++++++++++ .../apps/ctf/templates/ctf/flag_submit.html | 11 ++ .../apps/ctf/templates/ctf/team_create.html | 11 ++ source/apps/ctf/templates/ctf/team_list.html | 13 +++ source/apps/ctf/urls.py | 6 +- source/apps/ctf/views.py | 42 ++++++- 11 files changed, 274 insertions(+), 6 deletions(-) create mode 100644 source/apps/ctf/forms.py create mode 100644 source/apps/ctf/templates/ctf/flag_list.html create mode 100644 source/apps/ctf/templates/ctf/flag_submit.html create mode 100644 source/apps/ctf/templates/ctf/team_create.html create mode 100644 source/apps/ctf/templates/ctf/team_list.html diff --git a/source/apps/ctf/admin.py b/source/apps/ctf/admin.py index 8c38f3f..5a74873 100644 --- a/source/apps/ctf/admin.py +++ b/source/apps/ctf/admin.py @@ -1,3 +1,17 @@ from django.contrib import admin -# Register your models here. +from source.apps.ctf import models + + +@admin.register(models.Team) +class TeamAdmin(admin.ModelAdmin): + list_display = ("id", "name") + +@admin.register(models.Flag) +class FlagAdmin(admin.ModelAdmin): + list_display = ("id", "name", "type") + + +@admin.register(models.Hint) +class HintAdmin(admin.ModelAdmin): + list_display = ("id", "flag") diff --git a/source/apps/ctf/apps.py b/source/apps/ctf/apps.py index 72da749..a24299d 100644 --- a/source/apps/ctf/apps.py +++ b/source/apps/ctf/apps.py @@ -3,4 +3,4 @@ from django.apps import AppConfig class CtfConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' - name = 'apps.ctf' + name = 'source.apps.ctf' diff --git a/source/apps/ctf/forms.py b/source/apps/ctf/forms.py new file mode 100644 index 0000000..b059663 --- /dev/null +++ b/source/apps/ctf/forms.py @@ -0,0 +1,9 @@ +from django import forms + + +class TeamForm(forms.Form): + name = forms.CharField(label="Team's name", max_length=32) + + +class FlagForm(forms.Form): + name = forms.UUIDField(label="Identifier", max_length=32) diff --git a/source/apps/ctf/models.py b/source/apps/ctf/models.py index 71a8362..9c0c276 100644 --- a/source/apps/ctf/models.py +++ b/source/apps/ctf/models.py @@ -1,3 +1,65 @@ +import uuid + from django.db import models -# Create your models here. + +class Team(models.Model): + """ + Represent a team participating in the event + """ + + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + name = models.CharField(max_length=32) + + validated_flags = models.ManyToManyField(to="Flag", blank=True, related_name="validated_by_teams") + + def __str__(self): + return f"<{self.__class__.__name__}({self.name!r}, {self.id!r})>" + + +class Flag(models.Model): + """ + Represent a flag that can be validated by the team + """ + + class FlagType(models.IntegerChoices): + NORMAL = 0 + BONUS = 1 + MALUS = 2 + + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + + # the flag might depend on other flags + parents = models.ManyToManyField( + "self", + blank=True, + symmetrical=False, + related_name="children", + ) + + # information about the flags + # TODO(Faraphel): store code as hash ? + code = models.CharField(max_length=32) # the password that must be given to obtain the flag + name = models.CharField(max_length=32) # the name of the flag + description = models.TextField() # the description on how to obtain the flag + + # TODO(Faraphel): replace with or add a points amount you gain ? + type = models.IntegerField(choices=FlagType.choices, default=FlagType.NORMAL) + + def __str__(self): + return f"<{self.__class__.__name__}({self.name!r}, {self.id!r})>" + + +class Hint(models.Model): + """ + Represent a hint that can be given to a team + """ + + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + importance = models.PositiveIntegerField() + description = models.TextField() + penalty = models.PositiveIntegerField() + flag = models.ForeignKey(to=Flag, on_delete=models.CASCADE, related_name="hints") + + def __str__(self): + return f"<{self.__class__.__name__}({self.flag.name!r}, {self.id!r})>" diff --git a/source/apps/ctf/templates/ctf/base/base.html b/source/apps/ctf/templates/ctf/base/base.html index 35ddc74..8ebfeb3 100644 --- a/source/apps/ctf/templates/ctf/base/base.html +++ b/source/apps/ctf/templates/ctf/base/base.html @@ -2,6 +2,7 @@ + {% block title %}{% endblock %} {% block head %} {% endblock %} diff --git a/source/apps/ctf/templates/ctf/flag_list.html b/source/apps/ctf/templates/ctf/flag_list.html new file mode 100644 index 0000000..8704fe7 --- /dev/null +++ b/source/apps/ctf/templates/ctf/flag_list.html @@ -0,0 +1,105 @@ +{% extends "ctf/base/base.html" %} + +{% block title %}Flags{% endblock %} + +{% block head %} + + + + + + +{% endblock %} + +{% block body %} +
+ +{% endblock %} + + diff --git a/source/apps/ctf/templates/ctf/flag_submit.html b/source/apps/ctf/templates/ctf/flag_submit.html new file mode 100644 index 0000000..ff4e8f0 --- /dev/null +++ b/source/apps/ctf/templates/ctf/flag_submit.html @@ -0,0 +1,11 @@ +{% extends "ctf/base/base.html" %} + +{% block title %}Flag - Submit{% endblock %} + +{% block body %} +
+ {% csrf_token %} + {{ form }} + +
+{% endblock %} \ No newline at end of file diff --git a/source/apps/ctf/templates/ctf/team_create.html b/source/apps/ctf/templates/ctf/team_create.html new file mode 100644 index 0000000..67d9789 --- /dev/null +++ b/source/apps/ctf/templates/ctf/team_create.html @@ -0,0 +1,11 @@ +{% extends "ctf/base/base.html" %} + +{% block title %}Team - Create{% endblock %} + +{% block body %} +
+ {% csrf_token %} + {{ form }} + +
+{% endblock %} diff --git a/source/apps/ctf/templates/ctf/team_list.html b/source/apps/ctf/templates/ctf/team_list.html new file mode 100644 index 0000000..c03f003 --- /dev/null +++ b/source/apps/ctf/templates/ctf/team_list.html @@ -0,0 +1,13 @@ +{% extends "ctf/base/base.html" %} + +{% block title %}Teams{% endblock %} + +{% block body %} + + + Create +{% endblock %} diff --git a/source/apps/ctf/urls.py b/source/apps/ctf/urls.py index 3a7d2a0..e7e276a 100644 --- a/source/apps/ctf/urls.py +++ b/source/apps/ctf/urls.py @@ -3,5 +3,9 @@ from django.urls import path from source.apps.ctf import views urlpatterns = [ - path("", views.homepage, name='homepage'), + path("", views.view_homepage, name='homepage'), + path("teams", views.view_teams, name='teams'), + path("teams/create", views.view_team_create, name='team_create'), + path("flags", views.view_flags, name='flags'), + path("flags/submit", views.view_flag_submit, name='flag_submit'), ] diff --git a/source/apps/ctf/views.py b/source/apps/ctf/views.py index 10888f3..b4971f5 100644 --- a/source/apps/ctf/views.py +++ b/source/apps/ctf/views.py @@ -1,5 +1,43 @@ -from django.shortcuts import render +from django.shortcuts import render, redirect + +from source.apps.ctf import forms, models + # Create your views here. -async def homepage(request): +async def view_homepage(request): return render(request, "ctf/homepage.html") + + +async def view_teams(request): + teams = [team async for team in models.Team.objects.all()] + return render(request, "ctf/team_list.html", context={"teams": teams}) + + +async def view_team_create(request): + if request.method == "POST": + form = forms.TeamForm(request.POST) + if form.is_valid(): + # TODO(Faraphel): additional password to prevent unwanted team creation ? + await models.Team.objects.acreate(name=form.cleaned_data["name"]) + return redirect("/") + + else: + form = forms.TeamForm() + + return render(request, "ctf/team_create.html", context={"form": form}) + + +def view_flags(request): + flags = models.Flag.objects.all() + return render(request, "ctf/flag_list.html", context={"flags": flags}) + + +async def view_flag_submit(request): + if request.method == "POST": + form = forms.FlagForm(request.POST) + if form.is_valid(): + return redirect("/") + else: + form = forms.FlagForm() + + return render(request, "ctf/flag_submit.html", context={"form": form})