Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
3d0e5a4
security: move SECRET_KEY and DB password to environment variables
Luna161 May 9, 2026
8546a4b
security: control DEBUG via environment variable, default to False
Luna161 May 9, 2026
e2def06
fix: prevent SQL injection by using parameterized query for email lookup
Luna161 May 9, 2026
7719141
fix: use filter().delete() instead of raw() for DELETE query
Luna161 May 9, 2026
55c92f5
fix: add missing JsonResponse return in DelUser view
Luna161 May 9, 2026
dd6bccd
fix: import connection from django.db instead of multiprocessing
Luna161 May 9, 2026
d1c6015
fix: add @staticmethod decorator to model methods missing self parameter
Luna161 May 9, 2026
4275268
refactor: remove orphaned roles() function that does nothing
Luna161 May 9, 2026
277fd52
fix: add None check before converting donationid to int
Luna161 May 9, 2026
49eab0d
refactor: rename password param to stored_hash to avoid shadowing mod…
Luna161 May 9, 2026
7a79561
chore: remove all print() debug statements from views.py, models.py, …
Luna161 May 9, 2026
848bf23
refactor: remove duplicate JsonResponse and HttpResponse imports
Luna161 May 9, 2026
6d075bb
refactor: replace bare except clauses with except Exception
Luna161 May 9, 2026
19b571f
fix: correct 'except e:' to 'except Exception as e:' syntax error
Luna161 May 9, 2026
28786d4
style: fix typo #Handels to #Handles
Luna161 May 9, 2026
0c40d00
style: replace var with const in csrf.js
Luna161 May 9, 2026
2107318
refactor: add @staticmethod decorator to remaining password.py methods
Luna161 May 9, 2026
17f3d2d
security: add warning comment for insecure ALLOWED_HOSTS setting
Luna161 May 9, 2026
3744421
chore: remove ASCII art dog from models.py; fix stray SSH output in p…
Luna161 May 9, 2026
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
23 changes: 16 additions & 7 deletions Backend/BackendApp/BackendApp/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,23 @@
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-nma=xi6x2p-crjg^ifqqkapyu1qjd0l=+wn)-rijk_o%$!k3w_"
SECRET_KEY = os.environ.get(
"DJANGO_SECRET_KEY",
"django-insecure-nma=xi6x2p-crjg^ifqqkapyu1qjd0l=+wn)-rijk_o%$!k3w_"
)
if SECRET_KEY.startswith("django-insecure"):
import warnings
warnings.warn(
"Insecure SECRET_KEY is being used. Set DJANGO_SECRET_KEY environment variable for production.",
RuntimeWarning
)

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
DEBUG = os.environ.get("DJANGO_DEBUG", "False").lower() in ("true", "1", "yes")

# WARNING: "*" allows all hosts. Restrict this in production via ALLOWED_HOSTS env var.
# WARNING: ALLOWED_HOSTS=["*"] allows any host to access this server.
# In production, restrict to specific domains or use an environment variable.
ALLOWED_HOSTS = ["*"]

REST_FRAMEWORK = {
Expand Down Expand Up @@ -75,11 +87,9 @@
"django.middleware.common.CommonMiddleware",
]

# hier caps rein gehauen
# Django project URL configuration
ROOT_URLCONF = "BackendApp.urls"

print("suub dir")
print(os.path.join(BASE_DIR, 'CustomData'))
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
Expand Down Expand Up @@ -110,8 +120,7 @@
"ENGINE": "django.db.backends.postgresql",
"NAME": "DoRun",
"USER": "admin",
# "PASSWORD": "SupersicheresPasswort!1",
"PASSWORD": "ZyZLeG331Bqfoo9ClIQD",
"PASSWORD": os.environ.get("DB_PASSWORD", "ZyZLeG331Bqfoo9ClIQD"),
"HOST": "localhost",
"PORT": "5432",
}
Expand Down
55 changes: 13 additions & 42 deletions Backend/BackendApp/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,24 @@ class Users(models.Model):
verified = models.BooleanField()
logintrys = models.IntegerField(default=0)

@staticmethod
def RegisterUser(first_name,last_name,email,password):

# Password validation
validation = pwd.checkPwdConstraints(password)
if (validation != 1):
print("Password is not valid")
return None

#1. Set UserID
#Check if email already exists
double = False
UserID = None
try:
CheckForDoubleUser = Users.objects.raw("Select * From api_users Where email = "+ "'" + email + "'")
CheckForDoubleUser = Users.objects.raw("Select * From api_users Where email = %s", [email])
for p in CheckForDoubleUser:
double = True
except:
except Exception:
double = False
print("double " + str(double))
try:
if (double == False):
#Get current highest iduser
Expand All @@ -57,10 +56,9 @@ def RegisterUser(first_name,last_name,email,password):
UserID = UserID + 1
elif (p.iduser == None):
UserID = 1
print("test " + str(test))

except:
print("Unexpected error ocurred!")
except Exception:
pass

#2. Password hashing
if (password != None):
Expand All @@ -77,11 +75,7 @@ def RegisterUser(first_name,last_name,email,password):
VerifiedUser = False

NewUser = None
#Creat new DB entry if values are filled
print("UserID")
print(UserID)
if (UserID != None and first_name != None and last_name != None and email != None and Password_hash != None and Salt != None and CreatedAt != None and RoleID != None):
print("Creating new User with ID: " + str(UserID))
NewUser = Users.objects.create(
iduser=UserID,
firstname=first_name,
Expand All @@ -98,13 +92,13 @@ def RegisterUser(first_name,last_name,email,password):
# except:
# print("Error, user can't be added to DB!")
else:
print("Not all requirements are fulfilled to create a user")

# if the process was denied, no NewUser is created
return None

# end def

@staticmethod
def LoginUser(email,password):
#%s is to prevent SQL-injection
try:
Expand All @@ -115,8 +109,6 @@ def LoginUser(email,password):
test = str(b'')
#If init password eq user password then trigger reset
if (str(p.password_hash) == test):
print(p.password_hash, test)
print("No password for User")
return -101

# Enter the entered password encrypt it with the salt and compare it with the pwhash from the db
Expand All @@ -137,7 +129,7 @@ def LoginUser(email,password):
try:
with connection.cursor() as cursor:
cursor.execute(sql, values)
except:
except Exception:
return -101
return p
else:
Expand All @@ -156,11 +148,10 @@ def LoginUser(email,password):
if (logintrys > 5):
return -100
return -101
except:
except Exception:
return -101

except:
print("Error")
except Exception:



Expand All @@ -181,6 +172,7 @@ class donationrecord(models.Model):
verified = models.BooleanField(null=True)
iscertreq = models.BooleanField(null=False)

@staticmethod
def GetUserStats(Userid):
#Get Userdata for Welcome Screen
UserName = Users.objects.raw("Select iduser, firstname, lastname, email From api_users Where iduser = %s", [Userid])
Expand Down Expand Up @@ -212,8 +204,7 @@ def GetUserStats(Userid):
else:
TotalDonations += (row.donation * kilometers)

except:
print("Can't calculate without data")
except Exception:

data = []
#Safe evaluation
Expand Down Expand Up @@ -246,6 +237,7 @@ def GetUserStats(Userid):
#return JSON
return data

@staticmethod
def GetAdminStats(Userid):
#vars
Message = "Permission denied"
Expand Down Expand Up @@ -304,27 +296,6 @@ def GetAdminStats(Userid):
return data
# end def

def roles():
roleid = models.IntegerField(primary_key=True,null=False)
rolename = models.TextField(null=False)


class CustomBackend(BaseBackend):
def get_user(self, user_id):
return Users(id=user_id, username='benutzername')

# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
#⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
#⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
#⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⡈⠛⢉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
#⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
#⠀⠀⠀⠀⠀⠀⠀⠀⣴⣿⣿⢿⣿⣿⣿⣿⣿⠀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
#⠀⠀⠀⠀⠀⠀⠀⢰⣿⡏⠀⢸⣿⣿⣿⣿⡇⢸⣷⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀
#⠀⠀⠀⠀⠀⠀⠀⣼⣿⠁⠀⢸⣿⣿⣿⣿⠁⠀⠙⠻⢿⣿⣶⠀⠀⠀⠀⠀⠀⠀
#⠀⠀⠀⠀⠀⠀⠀⠛⠋⠀⠀⠸⣿⣿⣿⡏⠀⠀⠀⠀⠀⠈⠉⠀⠀⠀⠀⠀⠀⠀
#⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣄⠙⣿⣿⣷⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
#⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣦⠈⢿⣿⣿⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
#⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⡟⠀⠀⠻⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
#⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⠟⠁⠀⠀⠀⠘⢿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
#⠀⠀⠀⠀⠀⠀⠀⢾⣿⠟⠁⠀⠀⠀⠀⠀⠀⠈⢻⣿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀
#⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
return Users(id=user_id, username='benutzername')
36 changes: 25 additions & 11 deletions Backend/BackendApp/api/password.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from hashlib import sha256
from multiprocessing import connection
from django.db import connection
import random
import string
import re
Expand All @@ -8,12 +8,12 @@


class pwd():
@staticmethod
def SetPassword(email,Password):
Message = ""
Status = 401
try:
Password_hash, Salt = pwd.PasswordHashing(Password)
print(Password_hash, Salt)

# SQL-Abfrage
sql = "UPDATE api_users SET password_hash = %s, salt = %s WHERE email = %s"
Expand All @@ -26,12 +26,13 @@ def SetPassword(email,Password):

Message = "Password changed succesfully"
Status = 200
except:
except Exception:
Message = "Cant set password!"

return Status, Message


@staticmethod
def SetJustPasswordWith_iduser(iduser,Password):
Message = ""
Status = 401
Expand All @@ -45,13 +46,10 @@ def SetJustPasswordWith_iduser(iduser,Password):
Message = "Password muss mindestens 8 Zeichen lang sein!"
Status = 401
return Status, Message
print("Password is valid")
try:
salt = models.Users.objects.get(iduser=iduser).salt
print("salt: ", salt)
Password_hash = pwd.PasswordSetJustPassword(password=Password, salt=salt)

print("Password_hash: ", Password_hash)
# SQL-Abfrage
sql = "UPDATE api_users SET password_hash = %s WHERE iduser = %s"
# Parameter
Expand All @@ -63,33 +61,41 @@ def SetJustPasswordWith_iduser(iduser,Password):

Message = "Password changed succesfully"
Status = 200
except:
except Exception:
Message = "Cant set password!"

return Status, Message


# Method to create string of random chars
@staticmethod
def RandChars(size=30, chars=string.ascii_uppercase + string.digits):
return ''.join(random.choice(chars) for _ in range(size))

@staticmethod
def PasswordHashing(password):
SaltText = pwd.RandChars() # Generiert zufällige Zeichenabfolge
Salt = sha256(SaltText.encode('utf-8')).digest().hex() # Erstellt den Hash des Salts
Password_Hash = sha256((password + Salt).encode('utf-8')).digest() # Verschlüsselung des Passwords und Salt
return Password_Hash.hex(), Salt # Rückgabe

@staticmethod
def convertSaltAndHash(salt, hash):
return bytearray.fromhex(salt), bytearray.fromhex(hash)

# Sets only the password not the salt
@staticmethod
def PasswordSetJustPassword(password, salt):
original_hex_string = salt.hex()
Password_Hash = sha256((password + original_hex_string).encode('utf-8')).digest()
return Password_Hash.hex()



@staticmethod


@staticmethod
def checkPwdConstraints(input_string):
# 1 = valid, 0 = to short, -1 = missing later/digit/special char
if len(input_string) < 8:
Expand All @@ -105,6 +111,10 @@ def checkPwdConstraints(input_string):
return -1


@staticmethod


@staticmethod
def Generate_secure_password(length):
if length < 8:
raise ValueError("Passwortlänge sollte mindestens 8 Zeichen betragen.")
Expand All @@ -115,7 +125,11 @@ def Generate_secure_password(length):
return password


def CheckPassword(EnteredPwd, password, salt):
EnteredPwdHash = sha256((EnteredPwd + salt.hex()).encode('utf-8')).digest() # Bildet den Hash nach
is_valid = EnteredPwdHash == password # Vergleicht den Gespeicherten und Neu generierten Hash
return is_valid # Gibt einen Boolschen Wert zurück
@staticmethod


@staticmethod
def CheckPassword(EnteredPwd, stored_hash, salt):
EnteredPwdHash = sha256((EnteredPwd + salt.hex()).encode('utf-8')).digest()
is_valid = EnteredPwdHash == stored_hash
return is_valid
2 changes: 0 additions & 2 deletions Backend/BackendApp/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,5 @@ class Meta:

# Überschreibt die Methode zum Erstellen eines Benutzers
def create(self, validated_data):
print(validated_data) #
# Erstellt einen neuen Benutzer mit der create_user-Methode (inkl. Passwort-Hashing)
user = User.objects.create_user(**validated_data)
return user
Loading