Skip to content

Commit

Permalink
Add first test for completes models and network parts
Browse files Browse the repository at this point in the history
  • Loading branch information
KwikKill committed Jun 3, 2024
1 parent 01ed888 commit ae15bfd
Show file tree
Hide file tree
Showing 15 changed files with 310 additions and 132 deletions.
36 changes: 36 additions & 0 deletions backend/assets/misc/device_names.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
Dark Vador
Padme Amidala
R2-D2
C-3PO
BB-8
Obi-Wan Kenobi
Jar Jar Binks
Yoda
Dark Maul
Palpatine
Jango Fett
Boba Fett
Dooku
Luke Skywalker
Leia Organa
Han Solo
Chewbacca
Jabba Le Hutt
Kylo Renn
Grievous
Qui-Gon Jinn
Dark Maul
Rey
Dark Bane
Gial Ackbar
Armitage Hux
Maz Kanata
Ki-Adi-Mundi
Plo Koon
Phasma
Kylo Ren
Wilhuff Tarkin
Shmi Skywalker
Kit Fisto
L3-37
K-2SO
Empty file.
64 changes: 64 additions & 0 deletions backend/langate/network/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""
Network module. This module is responsible for the device and user connexion management.
"""

import sys, logging

from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _

from ..modules import netcontrol

logger = logging.getLogger(__name__)

class NetworkConfig(AppConfig):
"""Configuration of the Network Django App"""

name = "langate.network"
verbose_name = _("Network module")
default_auto_field = "django.db.models.BigAutoField"

def ready(self):
"""
Reconnecting devices registered in the Device set but not in the Ipset
This is important when for example the Ipset is flushed, you want the users that are already
registered on the gate to be automatically reconnected when the gate restarts.
This is important to maintain the consistency between the device state from django's point of view
and the device state from the Ipset's point of view.
"""
from langate.network.models import Device, UserDevice

if not any(
x in sys.argv
for x in
[
'makemigrations',
'migrate',
'shell',
'createsuperuser',
'flush',
'collectstatic',
'test',
]
):

logger.info(_("[PortalConfig] Adding previously connected devices to the ipset"))

for dev in Device.objects.all():
connect_res = netcontrol.query("connect_user", {"mac": dev.mac, "name": dev.name})
if not connect_res["success"]:
# If the device is a User device, we want to add it to the ipset
userdev = UserDevice.objects.filter(mac=dev.mac).first()
if userdev is not None:
logger.info(
"[PortalConfig] Could not connect device %s owned by user %s",
dev.mac,
userdev.user.username
)
else:
logger.info("[PortalConfig] Could not connect device %s", dev.mac)

if dev.whitelisted:
mark_res = netcontrol.query("set_mark", {"mac": dev.mac, "mark": 100})
if not mark_res["success"]:
logger.info("[PortalConfig] Could not set mark 0 for device %s", dev.name)
Empty file.
99 changes: 99 additions & 0 deletions backend/langate/network/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import random, logging

from django.contrib.auth.base_user import AbstractBaseUser as AbstractBaseUser

from django.db import models
from django.utils.translation import gettext_lazy as _

from langate.user.models import User
from langate.modules import netcontrol

logger = logging.getLogger(__name__)

def generate_dev_name():
"""
Generate a random device name based on a list of names
"""
try:
with open("assets/misc/device_names.txt", "r", encoding="utf-8") as f:
lines = f.read().splitlines()
return random.choice(lines)

except FileNotFoundError:
return "Computer"


class Device(models.Model):
"""
A device is a machine connected to the network
"""

name = models.CharField(max_length=100)
mac = models.CharField(max_length=17)
whitelisted = models.BooleanField(default=False)

class UserDevice(Device):
"""
A user device is a device that is connected to the network by a user
"""

user = models.ForeignKey(User, on_delete=models.CASCADE)
ip = models.GenericIPAddressField(blank=False)

# Area of the device, i.e. LAN or WiFi
area = models.CharField(max_length=4, default="LAN")

class DeviceManager(models.Manager):
"""
Manager for the Device and UserDevice models
"""

@staticmethod
def create_device(mac, name, whitelisted=False):
"""
Create a device with the given mac address and name
"""
if not name:
name = generate_dev_name()

netcontrol.query("connect", { "mac": mac, "name": name })
logger.info("Connected device %s (the mac %s has been connected)", name, mac)

device = Device.objects.create(mac=mac, name=name, whitelisted=whitelisted)
return device

@staticmethod
def delete_device(mac):
"""
Delete a device with the given mac address
"""
netcontrol.query("disconnect_user", { "mac": mac })
logger.info("Disconnected device %s from the internet.", mac)

device = Device.objects.get(mac=mac)
device.delete()
return device

@staticmethod
def create_user_device(user: User, ip, name=None):
"""
Create a device with the given mac address
"""
if not name:
name = generate_dev_name()

r = netcontrol.query("get_mac", { "ip": ip })
mac = r["mac"]
area = "LAN"

netcontrol.query("connect_user", { "mac": mac, "name": user.username })

logger.info(
"Connected device %s (owned by %s) at %s to the internet.",
mac,
user.username,
ip
)

device = UserDevice.objects.create(mac=mac, name=name, user=user, ip=ip, area=area)
return device
30 changes: 30 additions & 0 deletions backend/langate/network/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""Data Serializers for the langate Network module"""
# pylint: disable=too-few-public-methods

from django.contrib.auth import authenticate
from django.contrib.auth.password_validation import validate_password
from django.utils.translation import gettext_lazy as _

from rest_framework import serializers
from rest_framework.validators import UniqueValidator

from .models import Device, UserDevice

class DeviceSerializer(serializers.ModelSerializer):
"""Serializer for a Device"""

class Meta:
"""Meta class, used to set parameters"""

model = Device
exclude = ()


class UserDeviceSerializer(serializers.ModelSerializer):
"""Serializer for an UserDevice"""

class Meta:
"""Meta class, used to set parameters"""

model = UserDevice
exclude = ()
Empty file.
7 changes: 7 additions & 0 deletions backend/langate/network/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""url for network"""
from django.urls import path

from . import views

urlpatterns = [
]
23 changes: 23 additions & 0 deletions backend/langate/network/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import sys

from datetime import datetime

from django.contrib.auth import login, logout
from django.contrib.auth.models import Group, Permission
from django.contrib.auth.tokens import default_token_generator
from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import PermissionDenied, BadRequest
from django.http import JsonResponse
from django.utils.translation import gettext_lazy as _
from django.utils import timezone
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.decorators.http import require_GET
from rest_framework import generics, permissions, status
from rest_framework.authentication import SessionAuthentication
from rest_framework.response import Response
from rest_framework.views import APIView
from django.core.exceptions import ValidationError

from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi

1 change: 1 addition & 0 deletions backend/langate/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
"rest_framework",
"corsheaders",
"langate.user",
"langate.network",
"drf_yasg",
]

Expand Down
1 change: 1 addition & 0 deletions backend/langate/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
urlpatterns = [
path("", include(router.urls)),
path("user/", include("langate.user.urls")),
path("network/", include("langate.network.urls")),
]
if getenv("DEV", "1") == "1":
from drf_yasg.views import get_schema_view
Expand Down
52 changes: 33 additions & 19 deletions backend/langate/user/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Module for the definition of models tied to users
"""
from datetime import datetime
import logging

from django.contrib.auth.base_user import AbstractBaseUser as AbstractBaseUser
from django.contrib.auth.base_user import BaseUserManager
Expand All @@ -11,7 +12,7 @@
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.core.validators import FileExtensionValidator
from django.core.validators import MinValueValidator

class UserManager(BaseUserManager):
"""
Expand Down Expand Up @@ -45,15 +46,24 @@ def create_superuser(self, username, password, **extra_fields):
if password is None:
raise TypeError(_("Superusers must have a password."))
user = self.create_user(username, password, **extra_fields)
user.is_superuser = True
user.is_staff = True
user.role = Role.ADMIN
user.is_active = True
user.save()

return user

class Role(models.TextChoices):
"""
Enum for the role of a user
"""

PLAYER = "player", _("Player")
MANAGER = "manager", _("Manager")
GUEST = "guest", _("Guest")
STAFF = "staff", _("Staff")
ADMIN = "admin", _("Admin")

class User(AbstractBaseUser, PermissionsMixin):
class User(AbstractBaseUser):
"""
A user is simply our own abstraction defined above the standard Django User class.
"""
Expand All @@ -64,25 +74,29 @@ class User(AbstractBaseUser, PermissionsMixin):
max_length=150,
unique=True,
)
is_staff = models.BooleanField()
is_active = models.BooleanField()
role = models.CharField(
max_length=50,
choices=Role.choices,
default=Role.PLAYER,
)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField()

# The number of devices a user can have, minimum is 2
max_device_nb = models.IntegerField(
default=3,
validators=[MinValueValidator(2)],
)

# Relevant for the player role
tournament = models.CharField(max_length=100, null=True)
team = models.CharField(max_length=100, null=True)

USERNAME_FIELD = "username"
objects = UserManager()

class Meta:
"""Meta options"""

verbose_name = _("User")
verbose_name_plural = _("Users")

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

USERNAME_FIELD = "username"
is_staff = models.BooleanField(
verbose_name="Part of the insalan team", default=False
)
is_superuser = models.BooleanField(
verbose_name="Admin of the insalan team", default=False
)
is_active = models.BooleanField(default=True)
objects = UserManager()
Loading

0 comments on commit ae15bfd

Please sign in to comment.