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

Implemented mock routes #7

Merged
merged 8 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,4 @@ test-report.xml
Thumbs.db
ehthumbs.db
Desktop.ini
codebase_review.txt
Spkap marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Use Python base image
FROM python:3.12-slim
FROM python:3.12
Spkap marked this conversation as resolved.
Show resolved Hide resolved

# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1
Expand Down
11 changes: 11 additions & 0 deletions backend/atlas_backend/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.exceptions import InvalidToken
from .models import User

class CustomJWTAuthentication(JWTAuthentication):
def get_user(self, validated_token):
try:
user_id = validated_token['user_id']
return User.objects.get(id=user_id)
except (KeyError, User.DoesNotExist):
raise InvalidToken('Token contained no recognizable user identification')
64 changes: 30 additions & 34 deletions backend/atlas_backend/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from django.db import models
from django.contrib.auth.models import AbstractUser, BaseUserManager, Group
from django.core.validators import MinValueValidator

from django.contrib.auth.hashers import make_password, check_password
from django.db.models import CharField, TextField, IntegerField, BooleanField, DateTimeField

class CustomUserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
Expand Down Expand Up @@ -30,33 +31,32 @@ def get_by_natural_key(self, email):
"""
return self.get(email=email)


class Team(models.Model):
name = models.CharField(max_length=100, unique=True)
description = models.TextField(blank=True)
team_size = models.IntegerField(default=1)
challenges = models.ManyToManyField("Challenge", blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
password = models.CharField(max_length=128, default=make_password('default_password'))
team_email = models.EmailField(unique=True, default='[email protected]')

def __str__(self):
return self.name

def set_password(self, raw_password):
self.password = make_password(raw_password)

def check_password(self, raw_password):
return check_password(raw_password, self.password)

class User(AbstractUser):
email = models.EmailField(unique=True)
team = models.ForeignKey(
Team,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="members",
)
team = models.ForeignKey(Team, on_delete=models.SET_NULL, null=True, blank=True, related_name="members")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

objects = CustomUserManager()

USERNAME_FIELD = "email"
REQUIRED_FIELDS = []

Expand All @@ -66,15 +66,14 @@ def __str__(self):
def has_role(self, role_name):
return self.groups.filter(name=role_name).exists()


class Challenge(models.Model):
CATEGORY_CHOICES = [
("web", "Web"),
("crypto", "Cryptography"),
("pwn", "Binary Exploitation"),
("reverse", "Reverse Engineering"),
("forensics", "Forensics"),
("misc", "Miscellaneous"),
('web', 'Web'),
('crypto', 'Cryptography'),
('pwn', 'Binary Exploitation'),
('reverse', 'Reverse Engineering'),
('forensics', 'Forensics'),
('misc', 'Miscellaneous'),
]

title = models.CharField(max_length=200, unique=True)
Expand All @@ -86,50 +85,47 @@ class Challenge(models.Model):
max_team_size = models.IntegerField(default=4)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_hidden = models.BooleanField(default=False)
hints = models.JSONField(default=list, blank=True)
file_links = models.JSONField(default=list, blank=True)

def __str__(self):
return self.title


class Container(models.Model):
STATUS_CHOICES = [
("running", "Running"),
("exited", "Exit"),
("exited", "Exited"),
("error", "Error"),
]

team = models.ForeignKey(Team, on_delete=models.CASCADE)
challenge = models.ForeignKey(Challenge, on_delete=models.CASCADE)
team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name="containers")
challenge = models.ForeignKey(Challenge, on_delete=models.CASCADE, related_name="containers")
container_id = models.CharField(max_length=100)
ssh_host = models.CharField(max_length=200)
ssh_port = models.IntegerField()
ssh_user = models.CharField(max_length=100)
ssh_key = models.TextField()
status = models.CharField(
max_length=20, choices=STATUS_CHOICES, default="exited"
)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="exited")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

def __str__(self):
return f"{self.team.name} - {self.challenge.title}"


class Submission(models.Model):
team = models.ForeignKey(Team, on_delete=models.CASCADE)
challenge = models.ForeignKey(Challenge, on_delete=models.CASCADE)
points_awarded = models.IntegerField(validators=[MinValueValidator(0)])
timestamp = models.DateTimeField(auto_now_add=True)
team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name="submissions")
challenge = models.ForeignKey(Challenge, on_delete=models.CASCADE, related_name="submissions")
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='submissions', default=1)
flag_submitted = models.CharField(max_length=200, default="")
is_correct = models.BooleanField(default=False)
points_awarded = models.IntegerField(validators=[MinValueValidator(0)])
attempt_number = models.IntegerField(default=1)
timestamp = models.DateTimeField(auto_now_add=True)

class Meta:
unique_together = (
"team",
"challenge",
) # Used to enforce one submission per challenge for any team
unique_together = ("team", "challenge")
Spkap marked this conversation as resolved.
Show resolved Hide resolved
ordering = ["-timestamp"]

def __str__(self):
return f"{self.team.name} - {self.challenge.title}"
return f"{self.team.name} - {self.challenge.title}"
60 changes: 38 additions & 22 deletions backend/atlas_backend/serializers.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,54 @@
from rest_framework import serializers
from django.contrib.auth.models import Group
from .models import User, Challenge

from .models import User, Challenge, Team, Submission, Container

class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
vg239 marked this conversation as resolved.
Show resolved Hide resolved
fields = ['id', 'username', 'email', 'role', 'team', 'created_at', 'updated_at']

fields = ['id', 'username', 'email', 'team']

class SignupSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'email', 'password']
extra_kwargs = {
'password': {'write_only': True},
}
extra_kwargs = {'password': {'write_only': True}}

def create(self, validated_data):
user = User(
username=validated_data['username'],
email=validated_data['email']
)
user.set_password(validated_data['password'])
user.save()


default_group, _ = Group.objects.get_or_create(name='user')
user.role = default_group
user.save()

return user
return User.objects.create_user(**validated_data)

class ChallengeSerializer(serializers.ModelSerializer):
is_solved = serializers.SerializerMethodField()

class Meta:
model = Challenge
fields = ['id', 'title', 'description', 'category', 'max_points', 'max_team_size', 'created_at', 'updated_at']
fields = ['id', 'name', 'description', 'points', 'category',
imApoorva36 marked this conversation as resolved.
Show resolved Hide resolved
'difficulty', 'is_solved']

def get_is_solved(self, obj):
user_team = self.context.get('user_team')
if not user_team:
return False
return Submission.objects.filter(
team=user_team,
challenge=obj,
is_correct=True
).exists()

class TeamSerializer(serializers.ModelSerializer):
members = UserSerializer(many=True, read_only=True)
total_score = serializers.IntegerField(read_only=True)
member_count = serializers.IntegerField(read_only=True)
solved_count = serializers.IntegerField(read_only=True)

class Meta:
model = Team
fields = ['id', 'name', 'description', 'team_size', 'members',
'total_score', 'member_count', 'solved_count', 'challenges']

imApoorva36 marked this conversation as resolved.
Show resolved Hide resolved
class SubmissionSerializer(serializers.ModelSerializer):
challenge_name = serializers.CharField(source='challenge.title')
submitted_by = serializers.CharField(source='user.username')

class Meta:
model = Submission
fields = ['id', 'challenge_name', 'submitted_by', 'flag_submitted',
'is_correct', 'points_awarded', 'attempt_number', 'timestamp']
31 changes: 23 additions & 8 deletions backend/atlas_backend/urls.py
imApoorva36 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,18 +1,33 @@
# backend/atlas_backend/urls.py
from django.urls import path
from . import views
from rest_framework_simplejwt.views import TokenRefreshView

urlpatterns = [
# Auth routes
path('auth/register', views.signup, name='signup'),
path('auth/login', views.signin, name='signin'),
path('auth/forgot-password', views.request_password_reset, name='request_password_reset'),
path('auth/reset-password', views.reset_password, name='reset_password'),
path('auth/refresh', views.token_refresh, name='token_refresh'),

# Challenge routes
path('challenges', views.get_challenges, name='get_challenges'),
path('challenges/<int:challenge_id>/submit', views.submit_flag, name='submit_flag'),
path('challenges/<int:challenge_id>/start', views.start_challenge, name='start_challenge'),
path('challenges/create', views.create_challenge, name='create_challenge'),

# Team routes
path('teams', views.get_teams, name='get_teams'),
path('teams/join', views.join_team, name='join_team'),
path('teams/create', views.create_team, name='create_team'),
path('teams/leave', views.leave_team, name='leave_team'),
path('teams/profile', views.team_profile, name='team_profile'),

# Scoreboard route
path('scoreboard', views.get_scoreboard, name='get_scoreboard'),
]
path('auth/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),

# Admin routes
path('auth/admin/login', views.admin_login, name='admin_login'),
path('challenges/admin', views.admin_get_challenges, name='admin_get_challenges'),
path('challenges/create', views.create_challenge, name='create_challenge'),
path('challenges/<int:challenge_id>/update', views.update_challenge, name='update_challenge'),
path('challenges/<int:challenge_id>/delete', views.delete_challenge, name='delete_challenge'),
path('challenges/<int:challenge_id>', views.get_challenge_detail, name='get_challenge_by_id'),


]
Loading