start convertion to channels websocket and start using django sessions

This commit is contained in:
2024-08-22 20:37:48 +02:00
parent 7b4ef96081
commit 62e2281c69
24 changed files with 549 additions and 16 deletions

View File

@ -4,14 +4,19 @@ RUN apt update
RUN apt upgrade -y RUN apt upgrade -y
RUN apt install -y python3 python3-pip postgresql-client RUN apt install -y python3 python3-pip postgresql-client
RUN pip3 install django psycopg "channels[daphne]" RUN pip3 install requests django psycopg "channels[daphne]"
ARG DB_HOST=; ARG DB_HOST=;
ARG DB_NAME=; ARG DB_NAME=;
ARG DB_USERNAME=; ARG DB_USERNAME=;
ARG DB_PASSWORD=; ARG DB_PASSWORD=;
ARG SECRET_42=;
ARG SECRET_42=;
ENV DB_HOST=${DB_HOST} ENV DB_HOST=${DB_HOST}
ENV PYTHONUNBUFFERED=1
ENV UID_42=${UID_42}
ENV SECRET_42=${SECRET_42}
RUN mkdir -p /var/www/djangoserver/ RUN mkdir -p /var/www/djangoserver/
RUN mkdir -p /var/www/djangoserver/static/ RUN mkdir -p /var/www/djangoserver/static/

View File

@ -12,6 +12,7 @@ import os
from channels.routing import ProtocolTypeRouter, URLRouter from channels.routing import ProtocolTypeRouter, URLRouter
from django.urls import path from django.urls import path
from django.core.asgi import get_asgi_application from django.core.asgi import get_asgi_application
from channels.sessions import SessionMiddlewareStack
from .websocket import WebsocketHandler from .websocket import WebsocketHandler
@ -21,5 +22,5 @@ django = get_asgi_application()
application = ProtocolTypeRouter({ application = ProtocolTypeRouter({
"http": django, "http": django,
"websocket":URLRouter({path("ws",WebsocketHandler.as_asgi())}) "websocket":SessionMiddlewareStack(URLRouter({path("ws",WebsocketHandler.as_asgi())}))
}) })

View File

@ -129,3 +129,10 @@ STATIC_URL = 'static/'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
ASGI_APPLICATION = 'server.asgi.applicatio' ASGI_APPLICATION = 'server.asgi.applicatio'
SESSION_SAVE_EVERY_REQUEST = True
SESSION_COOKIE_NAME = 'sessionid'
SESSION_COOKIE_SECURE = False
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
SESSION_EXPIRE_AT_BROWSER_CLOSE = False

View File

@ -0,0 +1,75 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# createAccount.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2024/08/09 08:08:00 by edbernar #+# #+# #
# Updated: 2024/08/22 19:13:09 by tomoron ### ########.fr #
# #
# **************************************************************************** #
from .login import userList
import random
import re
pattern = r'^(?=.*[a-z])(?=.*[A-Z])(?=.*[\W_]).+$'
# {'username': 'Kumita', 'mail': 'eddydhj@gmail.com', 'password': '3b19482535d1ab2f4e3c629c4e3e5e2d6af0a5f5280be190726a4c3be518a475'}
async def createAccount(socket, content):
try:
content["mail"] = content["mail"].lower()
if (content["mail"].find('@') == -1 or content["mail"].find('.') == -1):
await socket.sendError("Invalid mail", 9006)
return
if (content["username"].find(' ') != -1):
await socket.sendError("Username must not contain spaces", 9007)
return
if (len(content["username"]) < 3):
await socket.sendError("Username must be at least 3 characters long", 9008)
return
if (len(content["username"]) > 20):
await socket.sendError("Username must be at most 20 characters long", 9009)
return
if (content["username"].find(' ') != -1):
await socket.sendError("Username must not contain spaces", 9011)
return
if (content["username"].isalnum() == False):
await socket.sendError("Username must contain only letters and numbers", 9012)
return
if (len(content["password"]) < 8):
await socket.sendError("Password must be at least 8 characters long", 9013)
return
if (bool(re.match(pattern, content["password"]))):
await socket.sendError("Password must contain at least one lowercase letter, one uppercase letter and one special character", 9014)
return
if (content["password"].find(content["username"]) != -1):
await socket.sendError("Password must not contain the username", 9015)
return
# |Tom| Au lieu d'utiliser userList, faire une requête à la base de donnée pour savoir si on a un utilisateur avec cet email ou cet username
if (content["mail"] in userList):
await socket.sendError("Mail already used", 9016)
return
if (content["username"] in userList):
await socket.sendError("Username already used", 9017)
return
content["token"] = generateToken()
while (True):
content["id"] = random.randint(1000000, 9999999)
if (content["id"] not in userList):
break
userList.append(content)
await socket.send({"type": "create_account", "content": "Account created"})
except Exception as e:
await socket.sendError("Error create account", 9005, e)
def generateToken():
list = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
token = ""
for i in range(0, 35):
token += list[random.randint(0, len(list) - 1)]
return token

View File

@ -0,0 +1,69 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# getPrivateListMessage.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2024/08/03 22:53:14 by edbernar #+# #+# #
# Updated: 2024/08/22 19:13:09 by tomoron ### ########.fr #
# #
# **************************************************************************** #
import random
listMessage = {
"type": "private_list_message",
"content": [
{
"from": 0,
"content": "",
"date": "10:05 31/07/2024"
},
{
"from": 0,
"content": "",
"date": "10:05 31/07/2024"
},
{
"from": 0,
"content": "",
"date": "10:06 31/07/2024"
},
{
"from": 0,
"content": "",
"date": "10:06 31/07/2024"
},
{
"from": 0,
"content": "",
"date": "10:45 31/07/2024"
},
{
"from": 0,
"content": "",
"date": "10:46 31/07/2024"
}
]
}
def generate_random_string():
char = "abcdefghijklmnopqrstuvwxyz 123456789"
string = ""
for i in range(20):
string += char[random.randint(0, len(char) - 1)]
return string
async def getPrivateListMessage(socket, content):
# |TOM| Requete pour avoir la liste des messages privés grace à l'id de l'utilisateur
copyListMessage = listMessage.copy()
for message in copyListMessage["content"]:
if (random.randint(1, 10) % 2 == 0):
message["from"] = 9999999
else:
message["from"] = content["id"]
message["content"] = generate_random_string()
await socket.send(copyListMessage)

View File

@ -0,0 +1,60 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# getPrivateListUser.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: edbernar <edbernar@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2024/08/03 15:10:23 by edbernar #+# #+# #
# Updated: 2024/08/22 19:13:09 by tomoron ### ########.fr #
# #
# **************************************************************************** #
import asyncio
import json
data = [
{
"name": "Nessundorma",
"status": "online",
"pfp": "https://wallpapers-clan.com/wp-content/uploads/2023/05/cool-pfp-02.jpg",
"id": 145564
},
{
"name": "Succotash",
"status": "offline",
"pfp": "https://i.pinimg.com/200x/28/75/96/287596f98304bf1adc2c411619ae8fef.jpg",
"id": 256981
},
{
"name": "Astropower",
"status": "online",
"pfp": "https://ashisheditz.com/wp-content/uploads/2024/03/cool-anime-pfp-demon-slayer-HD.jpg",
"id": 301547
},
{
"name": "Assaultive",
"status": "offline",
"pfp": "https://i1.sndcdn.com/artworks-1Li0JIJrQGlojD3y-AEiNkw-t500x500.jpg",
"id": 432448
},
{
"name": "Redshock",
"status": "offline",
"pfp": "https://cdn.pfps.gg/pfps/7094-boy-pfp.png",
"id": 543211
},
{
"name": "Parley",
"status": "offline",
"pfp": "https://pbs.twimg.com/media/EscE6ckU0AA-Uhe.png",
"id": 654123
}
]
async def getPrivateListUser(socket, content=None):
# |TOM| Faire une requête à la base de données pour récupérer la liste des
# utilisateurs qui doivent apparaitre dans la liste du chat privé
# (ceux qui ont eu conversation avec l'utilisateur)
# Si user existe pas, faire ça : await socket.sendError("User not found", 9008)
await socket.send({"type": "private_list_user", "content": data})

View File

@ -0,0 +1,110 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# login.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2024/08/03 08:10:38 by edbernar #+# #+# #
# Updated: 2024/08/22 19:12:23 by tomoron ### ########.fr #
# #
# **************************************************************************** #
from .login42.login42 import main42login
import requests
import json
import os
# Les requêtes de login peuvent être de 3 types:
# <-- {"type" : "login", "content" : {"type": "byToken", "token": "123456"}}
# <-- {"type" : "login", "content" : {"type": "byPass", "mail": "aaa@a.com", "pass": "dasd"}}
# <-- {"type" : "login", "content" : {"type": "by42", "token": "1dsa23dsa456"}}
# --> {"type" : "login", "content" : {"username": "". "token": "", "id": 0}}
userList = [
{
"username": "Eddy",
"token": "54dsadw8f4a6w5f4a62s4f984fa62f4as65",
"mail": "aaaaa",
"password": "ed968e840d10d2d313a870bc131a4e2c311d7ad09bdf32b3418147221f51a6e2", # not hashed : aaaaa
"id": 2135421,
"id42": -1
},
{
"username": "Hugo",
"token": "dsa4d6sa4sa1hfd1jhgk6g4k21bn65m4nb4",
"mail": "bbbbb",
"password": "bbbbb",
"id": 9892154,
"id42": -1
},
{
"username": "Mathis",
"token": "8cb1qjlfndc12mn2l1mn654xzkkhad54cxz",
"mail": "ccccc",
"password": "6304fbfe2b22557c34c42a70056616786a733b3d09fb326308c813d6ab712ec0", # not hashed : ccccc
"id": 2371234,
"id42": -1
},
{
"username": "Tom",
"token": "poiuygfvbdsv5c21vcxvcxhgbjqnkmds546",
"mail": "ddddd",
"password": "ddddd",
"id": 6423457,
"id42": -1
}
]
async def loginByToken(socket, content):
# |TOM| Requete pour savoir si le token est valide
for user in userList:
if (user["token"] == content["token"]):
jsonVar = {"type": "login", "content": {"username": user["username"], "token": user["token"], "id": user["id"]}}
socket.username = jsonVar["content"]["username"]
socket.token = jsonVar["content"]["token"]
socket.id = jsonVar["content"]["id"]
await socket.send(jsonVar)
return
jsonVar = {"type": "error", "content": "Invalid token", "code": 9001}
await socket.send(json.dumps(jsonVar))
async def loginByPass(socket, content):
# |TOM| Requete pour savoir si le mail et le mot de passe sont valides
# et créer un token si celui-ci n'existe pas
for user in userList:
if (user["mail"] == content["mail"] and user["password"] == content["password"]):
jsonVar = {"type": "login", "content": {"username": user["username"], "token": user["token"], "id": user["id"]}}
socket.username = jsonVar["content"]["username"]
socket.token = jsonVar["content"]["token"]
socket.id = jsonVar["content"]["id"]
await socket.send(jsonVar)
return
await socket.send({"type": "error", "content": "Invalid username or password", "code": 9007})
async def loginBy42(socket, content):
# |TOM| Requete pour récuperer les informations de l'utilisateur selon l'intra de la personne
# et créer un token si celui-ci n'existe pas
try:
await main42login(socket, content, userList)
except Exception as e:
await socket.sendError("Invalid 42 token", 9010, e)
async def login(socket, content):
# |TOM| Faire 3 types de requêtes:
# - byToken: Récupérer les informations de l'utilisateur en fonction de son token
# - byPass: Récupérer les informations de l'utilisateur en fonction de mail et de son mot de passe
# - by42: Récupérer les informations de l'utilisateur en fonction de son token42 (qui sera different du token)
try:
if (content["type"] == "byToken"):
await loginByToken(socket, content)
elif (content["type"] == "byPass"):
await loginByPass(socket, content)
elif (content["type"] == "by42"):
await loginBy42(socket, content)
else:
await socket.sendError("Invalid login type", 9006)
except Exception as e:
await socket.sendError("Invalid request", 9005, e)

View File

@ -0,0 +1,72 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# login42.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2024/08/09 09:32:17 by edbernar #+# #+# #
# Updated: 2024/08/22 19:13:31 by tomoron ### ########.fr #
# #
# **************************************************************************** #
import requests
import json
import os
UID42 = os.environ.get("UID_42")
SECRET42 = os.environ.get("SECRET_42")
TOKENURL = 'https://api.intra.42.fr/oauth/token'
INFOURL = 'https://api.intra.42.fr/v2/me'
REDIRECT = 'http://127.0.0.1:5500/site/'
# |Eddy| Changer le redirect quand il y aura un vrai serveur
access_token = ""
if (UID42 == None or SECRET42 == None):
print("Please set the environment variables uid and secret")
exit()
async def main42login(socket, content, userList):
global access_token
print(content['token'])
data = {
'grant_type': 'authorization_code',
'client_id': UID42,
'client_secret': SECRET42,
'code': content['token'],
'redirect_uri': REDIRECT
}
response = requests.post(TOKENURL, data=data)
if (response.status_code != 200):
raise Exception(f"Problem with the request (access_token {response.status_code})")
response = response.json()
headers = {
'Authorization': f'Bearer {response["access_token"]}',
}
response = requests.get(INFOURL, headers=headers)
if (response.status_code != 200):
raise Exception(f"Problem with the request (user info {response.status_code})")
response = response.json()
# |Tom| Au lieu d'utiliser userList, faire une requête à la base de donnée pour savoir si on a un utilisateur avec cet id42
i = 0
while (i < len(userList)):
if (userList[i]['id42'] == response['id']):
break
i += 1
if (i == len(userList)):
await socket.sendError("Not user registered with this 42 account", 9011)
return
else:
await socket.send({
"type": "login",
"content": {
"username": userList[i]['username'],
"token": userList[i]['token'],
"id": userList[i]['id']
}
})

View File

@ -0,0 +1,35 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# sendPrivateMessage.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2024/08/04 13:44:11 by edbernar #+# #+# #
# Updated: 2024/08/22 19:13:31 by tomoron ### ########.fr #
# #
# **************************************************************************** #
from datetime import datetime
async def sendPrivateMessage(socket, content):
# |Tom| Requete pour vérifier si l'user existe
# Si user existe pas, faire ça : await socket.sendError("User not found", 9008)
# Sinon l'ajouter à la base de données
# |Eddy| Si user existe, envoyer le message privé aux deux personnes concernées
# sachant que le receveur doit être connecté. Dans le cas contraire, uniquement
# l'envoyeur recevra le message.
try:
time = content["time"]
time = datetime.strptime(time, "%Y-%m-%dT%H:%M:%S.%fZ")
time = time.strftime("%H:%M %d/%m/%Y")
jsonVar = {"type": "new_private_message", "content": {
"from": content["from"],
"channel": content["to"],
"content": content["content"],
"date": time
}}
await socket.send(jsonVar)
except Exception as e:
await socket.sendError("Invalid message sent", 9009, e)

View File

@ -2,4 +2,8 @@ from django.http import HttpResponse
from .models import User from .models import User
def index(request): def index(request):
if(request.session.get("visited", False)):
print("already visited")
request.session["visited"] = True
return HttpResponse("AAAAAAAAAAAAAAAAAAAAAAA") return HttpResponse("AAAAAAAAAAAAAAAAAAAAAAA")

View File

@ -1,13 +1,112 @@
from channels.generic.websocket import WebsocketConsumer from channels.generic.websocket import WebsocketConsumer
import json
from .typeRequets.getPrivateListMessage import getPrivateListMessage
from .typeRequets.getPrivateListUser import getPrivateListUser
from .typeRequets.sendPrivateMessage import sendPrivateMessage
from .typeRequets.createAccount import createAccount
from .typeRequets.login import login
typeRequest = ["login", "get_private_list_user", "get_private_list_message",
"send_private_message", "create_account"]
functionRequest = [login, getPrivateListUser, getPrivateListMessage,
sendPrivateMessage, createAccount]
from random import randint
class WebsocketHandler(WebsocketConsumer): class WebsocketHandler(WebsocketConsumer):
debugMode = True
session = None
def connect(self): def connect(self):
print("new client")
self.accept() self.accept()
print("AAAAAAAAAAAAA") print(self.scope["session"].get("number"))
if(self.scope["session"].get("number") == None):
self.scope["session"]["number"] = randint(0,2147483647)
self.scope["session"].save()
print("new number : ", self.scope["session"].get("number"))
else:
print("remembered number : ", self.scope["session"].get("number"))
def disconnect(self, close_code): def disconnect(self, close_code):
print("CCCCCCCCCCCCCC") print("you can go, we never wanted you anyway")
def receive(self, message): def receive(self, text_data):
print("BBBBBBBBBBBB") print(self.scope["session"].get("number"))
self.send(text_data=json.dumps({"AAAAA":"received"})) print("someone is talking")
try:
jsonRequest = json.loads(text_data)
except json.JSONDecodeError:
self.sendError("Invalid JSON", 9002)
return
try:
self.printDebug(jsonRequest, 0)
if (jsonRequest["type"] in typeRequest):
if (jsonRequest["type"] == "login" or jsonRequest["type"] == "create_account"):
functionRequest[typeRequest.index(jsonRequest["type"])](self, jsonRequest["content"])
else:
if (self.verifyToken(jsonRequest["token"]) == False):
return
functionRequest[typeRequest.index(jsonRequest["type"])](self, jsonRequest["content"])
else:
self.sendError("Invalid type", 9004)
except Exception as e:
self.sendError("Invalid request", 9005, e)
def sendError(self, message, code, error=None):
try:
jsonVar = {"type": "error", "content": message, "code": code}
self.printDebug(jsonVar, 2, error)
self.send(text_data=json.dumps(jsonVar))
except Exception as e:
if (self.debugMode):
print("\033[0;31m|------ Error in sendError ------|\033[0;0m")
print(e)
def printDebug(self, request, typeRequest, error=None):
try:
if (self.debugMode and typeRequest == 0):
print("\033[0;34m|----- New received request -----|\033[0;0m")
#print("User :", self.username)
#print("Token :", self.token)
#print("Id :", self.id)
try:
print("Var type :", type(request["type"]))
print("Type :", request["type"])
except:
pass
try:
print("Content type :", request["content"])
except:
pass
elif (self.debugMode and typeRequest == 1):
print("\033[0;32m|------- New sent request -------|\033[0;0m")
#print("To :", self.username)
#print("Id :", self.id)
try:
print("Var type :", type(request["type"]))
print("Type :", request["type"])
except:
pass
try:
print("Content :", request["content"])
except:
pass
if ("type" not in request or "content" not in request):
print("Warning : not usual json format")
elif (self.debugMode and typeRequest == 2):
print("\033[0;31m|------------- Error ------------|\033[0;0m")
#print("User :", self.username)
#print("Token :", self.token)
#print("Id :", self.id)
print("Error message :", request["content"])
print("Error code :", request["code"])
if (error != None):
print("Error python :", error)
print("File :", error.__traceback__.tb_frame.f_globals["__file__"])
print("Line :", error.__traceback__.tb_lineno)
except Exception as e:
print("\033[0;31m|------ Error in printDebug -----|\033[0;0m")
print(e)

View File

@ -6,7 +6,7 @@
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ # # By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2024/08/03 15:54:14 by edbernar #+# #+# # # Created: 2024/08/03 15:54:14 by edbernar #+# #+# #
# Updated: 2024/08/07 21:20:17 by edbernar ### ########.fr # # Updated: 2024/08/22 19:12:27 by tomoron ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
@ -45,7 +45,7 @@ class User():
print("\033[0;31m|------ Error in sendError ------|\033[0;0m") print("\033[0;31m|------ Error in sendError ------|\033[0;0m")
async def send(self, content):
try: try:
if (type(content) == dict): if (type(content) == dict):
self.printDebug(content, 1) self.printDebug(content, 1)

View File

@ -1,4 +1,4 @@
# **************************************************************************** # **************************************************************************** #
# # # #
# ::: :::::::: # # ::: :::::::: #
# main.py :+: :+: :+: # # main.py :+: :+: :+: #
@ -6,7 +6,7 @@
# By: edbernar <edbernar@student.42.fr> +#+ +:+ +#+ # # By: edbernar <edbernar@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2024/08/03 08:10:40 by edbernar #+# #+# # # Created: 2024/08/03 08:10:40 by edbernar #+# #+# #
# Updated: 2024/08/09 09:03:31 by edbernar ### ########.fr # # Updated: 2024/08/22 15:54:03 by tomoron ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
@ -31,10 +31,6 @@ functionRequest = [login, getPrivateListUser, getPrivateListMessage,
sendPrivateMessage, createAccount] sendPrivateMessage, createAccount]
async def handler(websocket, path): async def handler(websocket, path):
if (path != "/"):
await websocket.sendError("Invalid path", 9003)
await websocket.close()
return
userClass = User(websocket) userClass = User(websocket)
try: try:
async for resquet in userClass.websocket: async for resquet in userClass.websocket:
@ -66,4 +62,4 @@ start_server = websockets.serve(handler, "localhost", 8000, subprotocols=['12345
asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_until_complete(start_server)
print("Server started") print("Server started")
asyncio.get_event_loop().run_forever() asyncio.get_event_loop().run_forever()