Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update cert #15

Merged
merged 35 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
2cbccd4
Implemented webhook on solve
JasonLovesDoggo Apr 6, 2024
45e4152
fmt
JasonLovesDoggo Apr 6, 2024
6ba634a
Added checks for chal cluster connection fail
JasonLovesDoggo Apr 6, 2024
cc8b389
Fix: attr error
JasonLovesDoggo Apr 6, 2024
33f4e5e
Remove creating db object when user has alr submitted.
JasonLovesDoggo Apr 6, 2024
e6f6251
Added webhook to admin
JasonLovesDoggo Apr 6, 2024
84152fe
fixed naming issues
JasonLovesDoggo Apr 6, 2024
b1616c2
Completed webhook feature. + refactor to singals.py
JasonLovesDoggo Apr 6, 2024
2c59af7
Optimized imports + fmt
JasonLovesDoggo Apr 6, 2024
2500655
removed comment
JasonLovesDoggo Apr 6, 2024
7fa3004
disabled lookup when in debug
JasonLovesDoggo Apr 6, 2024
153df0e
renamed cache reset method to better reflect it's use.
JasonLovesDoggo Apr 6, 2024
dc60d53
Feat/Fix: Users are now ranked by -points -last_correct_submission
JasonLovesDoggo Apr 6, 2024
0d31021
replace == with is (pythonic!!!!!)
JasonLovesDoggo Apr 6, 2024
f273841
remove duplicated AUTHENTICATION_BACKENDS def
JasonLovesDoggo Apr 6, 2024
8c579ef
Describe issues with implementing fetching the rank of a single user.
JasonLovesDoggo Apr 6, 2024
7e31cfc
remove unused/useless code.
JasonLovesDoggo Apr 6, 2024
c92e9c0
Added user list and contest lb filtering (without templates)
JasonLovesDoggo Apr 6, 2024
097b720
Added templates for searching.
JasonLovesDoggo Apr 6, 2024
bd866e1
Added disclaimer
JasonLovesDoggo Apr 6, 2024
001b734
block concurrent workflows
JasonLovesDoggo Apr 6, 2024
a02aed7
test block concurrent workflows
JasonLovesDoggo Apr 6, 2024
672837e
upgrade bleach to latest. NOTE: it's been deprecated and will only su…
JasonLovesDoggo Apr 6, 2024
9937701
Added some additional db indexes
JasonLovesDoggo Apr 6, 2024
8c530f2
Attempted to speed up problems list.
JasonLovesDoggo Apr 6, 2024
1f6c02a
OKAY I LIED - made problem lookup case insensitive
JasonLovesDoggo Apr 6, 2024
af891db
Keep query params on pagination next.
JasonLovesDoggo Apr 6, 2024
496b80b
remove debug print ( I need to change deployment hash to debug startup )
JasonLovesDoggo Apr 6, 2024
bdb2244
fix: slow shutdowns
JasonLovesDoggo Apr 6, 2024
49cb637
Updated ctf req timeout to 20s and have docker kill after 21
JasonLovesDoggo Apr 6, 2024
c6dc381
1 more docker build
JasonLovesDoggo Apr 6, 2024
7f12d3a
reset config as there is no reason to keep it.
JasonLovesDoggo Apr 6, 2024
cced0a4
Optimized potential fix for last accepted submission.
JasonLovesDoggo Apr 15, 2024
9fbf309
Use Usernames when team name isn't defined
JasonLovesDoggo Apr 15, 2024
92d4295
don't include people with zero scores in api lb
JasonLovesDoggo Apr 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/container.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# GitHub recommends pinning actions to a commit SHA.
# Github recommends pinning actions to a commit SHA.
# To get a newer version, you will need to update the SHA.
# You can also reference a tag or branch, but the action may change without warning.

Expand All @@ -7,6 +7,11 @@ name: Create and publish a Docker image
on:
push:

concurrency:
group: ${{ github.workflow }}-${{ github.GITHUB_REF }}
cancel-in-progress: true


env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
Expand Down
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@ RUN set -eux; cd /app/public/scss; mkdir out; for f in *.scss; \
mv out/* .; \
chmod a+r /app/public/scss/*.css

STOPSIGNAL SIGTERM
# Gunicorn listens to SIGTERM
RUN apt-get purge -y sassc && \
rm -rf /var/lib/apt/lists/* && \
rm -rf /var/cache/*
EXPOSE 28730
CMD /app/.venv/bin/gunicorn \
--bind :28730 \
--error-logfile - \
--timeout 120 \
--timeout 20 \
--config /app/container/gunicorn.py \
mCTF.wsgi:application
93 changes: 90 additions & 3 deletions gameserver/admin.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from adminsortable2.admin import SortableInlineAdminMixin, SortableAdminBase
from adminsortable2.admin import SortableAdminBase, SortableInlineAdminMixin
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.flatpages.admin import FlatPageAdmin
from django.contrib.flatpages.models import FlatPage
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _

from . import models
from .models import Submission, ContestSubmission
from .utils.actions import *

User = get_user_model()

Expand Down Expand Up @@ -212,6 +215,7 @@ class ContestAdmin(SortableAdminBase, admin.ModelAdmin):
"summary",
"start_time",
"end_time",
"first_blood_webhook",
"tags",
"max_team_size",
"is_public",
Expand All @@ -225,6 +229,7 @@ class ContestAdmin(SortableAdminBase, admin.ModelAdmin):
"start_time",
"end_time",
]
actions = [recalculate_score]

def has_view_permission(self, request, obj=None):
if request.user.has_perm("gameserver.view_contest"):
Expand Down Expand Up @@ -277,11 +282,92 @@ class UserAdmin(admin.ModelAdmin):
"username",
"full_name",
]
actions = [recalculate_user_scores, recalculate_all_user_scores]


class UserScoreAdmin(admin.ModelAdmin):
fields = (
"user",
"points",
"flag_count",
"last_correct_submission",
"last_correct_submission_obj",
)
list_display = [
"user",
"points",
"last_correct_submission",
]
ordering = ["-points"]
search_fields = [
"user__username",
"user__full_name",
]
readonly_fields = [
"user",
"points",
"flag_count",
"last_correct_submission",
"last_correct_submission_obj",
]

def last_correct_submission_obj(self, obj):
try:
obj = Submission.objects.filter(
user=obj.user, is_correct=True, problem__is_public=True
).latest("date_created")
except Submission.DoesNotExist:
return "No correct submissions"
return format_html("<a href='{url}'>{url}</a>", url=obj.get_absolute_admin_url())

last_correct_submission_obj.allow_tags = True

last_correct_submission_obj.short_description = "Last correct submission URL"


class ContestScoreAdmin(admin.ModelAdmin):
fields = (
"participation",
"points",
"flag_count",
"last_correct_submission",
"last_correct_submission_obj",
)
list_display = [
"participation",
"points",
"last_correct_submission",
]
list_filter = ["participation__contest"]
ordering = ["-points"]
search_fields = [
"participation__team__name",
"participation__team__members__username",
]
readonly_fields = [
"participation",
"points",
"flag_count",
"last_correct_submission",
"last_correct_submission_obj",
]

def last_correct_submission_obj(self, obj):
try:
obj = ContestSubmission.objects.filter(
participation=obj.participation, submission__is_correct=True
).latest("submission__date_created")
except ContestSubmission.DoesNotExist:
return "No correct submissions"
return format_html("<a href='{url}'>{url}</a>", url=obj.get_absolute_admin_url())

last_correct_submission_obj.allow_tags = True
last_correct_submission_obj.short_description = "Last correct submission URL"


admin.site.register(User, UserAdmin)
admin.site.register(models.ContestScore)
admin.site.register(models.UserScore)
admin.site.register(models.ContestScore, ContestScoreAdmin)
admin.site.register(models.UserScore, UserScoreAdmin)
admin.site.register(models.Problem, ProblemAdmin)
admin.site.register(models.Submission, SubmissionAdmin)
admin.site.register(models.ProblemType)
Expand All @@ -295,6 +381,7 @@ class UserAdmin(admin.ModelAdmin):
admin.site.register(models.Contest, ContestAdmin)
admin.site.register(models.ContestTag)
admin.site.register(models.ContestParticipation)
admin.site.register(models.ContestSubmission)
admin.site.site_header = "mCTF administration"
admin.site.site_title = "mCTF admin"

Expand Down
50 changes: 30 additions & 20 deletions gameserver/api/routes.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from django.db.models import F, OuterRef, Subquery, Case, When, Q
import datetime
from typing import Any, List

from django.db.models import F, OuterRef, Max, Subquery, Case, When, Value, BooleanField, TextField
from django.shortcuts import get_object_or_404
from ninja import NinjaAPI, Schema

from gameserver.models.cache import ContestScore
from gameserver.models.contest import ContestProblem, ContestSubmission, Contest
from ninja import NinjaAPI, Schema
from typing import List, Any
from gameserver.models.profile import User
from gameserver.models.contest import Contest, ContestProblem, ContestSubmission, ContestParticipation

import datetime

def unicode_safe(string):
return string.encode("unicode_escape").decode()
Expand All @@ -32,11 +34,12 @@ class CTFSchema(Schema):
lastAccept: Any = None

@staticmethod
def resolve_lastAccept(obj) -> int:
def resolve_lastAccept(obj: dict) -> int:
"""Turns a datetime object into a timestamp."""
if obj["lastAccept"] is None:
print(obj, ' - DEBUG PRINT')
if obj['lastAccept'] is None:
return 0
return int(obj["lastAccept"].timestamp())
return int(obj['lastAccept'].timestamp())

@staticmethod
def resolve_team(obj):
Expand All @@ -60,21 +63,28 @@ def ctftime_standings(request, contest_name: str):
.order_by("-submission__date_created")
.values("submission__date_created")
)

standings = (
ContestScore.ranks(contest=contest_id)
.annotate(
pos=F("rank"),
score=F("points"),
team=F("participation__team__name"),
# team=Coalesce(F("participation__team__name"), F("participation__participants__username")),
# Using Coalesce and indexing
# team=Case(
# When(F("participation__team__isnull")==True, then=Q(("participation__participants")[0]["username"])),
# default=F("team_name"),
# output_field=TextField(),
# ),
lastAccept=Subquery(last_sub_time),
is_solo=Case(
When(participation__team_id=None, then=Value(False)),
default=Value(True),
output_field=BooleanField(),
),
team=Case(
When(participation__team_id=None, then=Subquery( # If the team is None, use the username of the participant ( solo player )
User.objects.filter(contest_participations=OuterRef("participation_id")).values(
"username")[:1]
),),
default=F("participation__team__name"),
output_field=TextField(),
),
lastAccept=Max("participation__submission__submission__date_created"),
)
.filter(score__gt=0)
.values("pos", "score", "team", "lastAccept")
)
task_names = (
Expand All @@ -85,9 +95,9 @@ def ctftime_standings(request, contest_name: str):

return {"standings": standings, "tasks": task_names}


@api.get("/contests", response=List[ContestOutSchema])
def contests(request):
return (
Contest.objects.filter(is_public=True)
.values("name", "slug", "start_time", "end_time", "max_team_size", "description", "summary")
return Contest.objects.filter(is_public=True).values(
"name", "slug", "start_time", "end_time", "max_team_size", "description", "summary"
)
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Generated by Django 4.0.1 on 2024-03-11 01:49

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion

import gameserver.models.profile


Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Generated by Django 5.0.4 on 2024-04-04 13:44

from django.db import migrations, models

import gameserver


Expand Down
Loading
Loading