Websocket server

- Added function to create account with request "create_account"
    - Can create new account (but it s temporary because no db)
    - Doing 42 connection
This commit is contained in:
edbernar
2024-08-09 10:32:33 +02:00
parent c2a4ea3a3a
commit 0bab70a44c
9 changed files with 215 additions and 44 deletions

View File

@ -11,3 +11,4 @@
- 9007 : Invalid username or password - 9007 : Invalid username or password
- 9008 : User not found - 9008 : User not found
- 9009 : Invalid message sent - 9009 : Invalid message sent
- 9010 : Invalid token 42

View File

@ -0,0 +1,24 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* connectedWith42.js :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: edbernar <edbernar@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/08/09 09:15:24 by edbernar #+# #+# */
/* Updated: 2024/08/09 09:18:26 by edbernar ### ########.fr */
/* */
/* ************************************************************************** */
import { sendRequest } from "../websocket.js";
function connectedWith42Func()
{
const token42 = window.location.search.split('code=')[1];
console.log("connectedWith42Func");
sendRequest("login", {type: "by42", token: token42});
console.log(token42);
}
export { connectedWith42Func };

View File

@ -3,10 +3,10 @@
/* ::: :::::::: */ /* ::: :::::::: */
/* createConnectDiv.js :+: :+: :+: */ /* createConnectDiv.js :+: :+: :+: */
/* +:+ +:+ +:+ */ /* +:+ +:+ +:+ */
/* By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ */ /* By: edbernar <edbernar@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2024/08/07 18:14:53 by edbernar #+# #+# */ /* Created: 2024/08/07 18:14:53 by edbernar #+# #+# */
/* Updated: 2024/08/08 23:49:20 by edbernar ### ########.fr */ /* Updated: 2024/08/09 09:06:59 by edbernar ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@ -16,7 +16,6 @@ import { sendRequest } from "../websocket.js";
/* /*
Todo (Eddy) : Todo (Eddy) :
- Gerer coté serveur le type "createAccount"
- ajouter un message de confirmation de création de compte et un message d'erreur - ajouter un message de confirmation de création de compte et un message d'erreur
- une fleche pour revenir en arriere - une fleche pour revenir en arriere
- remettre sur l'ecran de login quand le compte est créé - remettre sur l'ecran de login quand le compte est créé
@ -25,9 +24,6 @@ import { sendRequest } from "../websocket.js";
- Ajouter un message d'erreur si le mail est invalide - Ajouter un message d'erreur si le mail est invalide
- Connexion par 42 - Connexion par 42
Todo (Tom) :
- Mettre des pages temporaires accesibles qu'on envoie par mail pour confirmer le compte
*/ */
function createConnectDiv(divLogin) function createConnectDiv(divLogin)
@ -38,6 +34,7 @@ function createConnectDiv(divLogin)
const inputPass = document.createElement("input"); const inputPass = document.createElement("input");
const buttonLogin = createButton(inputLogin, inputPass); const buttonLogin = createButton(inputLogin, inputPass);
const buttonNewAcc = createButtonNewAcc(divConnect, divLogin); const buttonNewAcc = createButtonNewAcc(divConnect, divLogin);
const buttonConnect42 = document.createElement("button");
addGlobalBg(); addGlobalBg();
divConnect.setAttribute("id", "connectDiv"); divConnect.setAttribute("id", "connectDiv");
@ -48,15 +45,21 @@ function createConnectDiv(divLogin)
inputPass.setAttribute("autocomplete", "current-password"); inputPass.setAttribute("autocomplete", "current-password");
inputPass.setAttribute("placeholder", "password"); inputPass.setAttribute("placeholder", "password");
buttonLogin.innerHTML = "Connect"; buttonLogin.innerHTML = "Connect";
buttonConnect42.innerHTML = "Connect with 42";
form.appendChild(inputLogin); form.appendChild(inputLogin);
form.appendChild(inputPass); form.appendChild(inputPass);
form.appendChild(buttonLogin); form.appendChild(buttonLogin);
form.appendChild(buttonNewAcc); form.appendChild(buttonNewAcc);
form.appendChild(buttonConnect42);
divConnect.appendChild(form); divConnect.appendChild(form);
form.addEventListener('submit', (e) => { form.addEventListener('submit', (e) => {
e.preventDefault(); e.preventDefault();
buttonLogin.click(); buttonLogin.click();
}); });
buttonConnect42.addEventListener('click', (e) => {
e.preventDefault();
window.location.replace("https://api.intra.42.fr/oauth/authorize?client_id=u-s4t2ud-d9d6d46bd0be36dc13718981df4bfcf37e574ea364a07fcb5c39658be0f5706c&redirect_uri=http%3A%2F%2F127.0.0.1%3A5500%2Fsite%2F&response_type=code");
});
return (divConnect); return (divConnect);
} }
@ -167,6 +170,10 @@ function createNewAccount(e)
} }
else if (inputUsername.value.length < 3) else if (inputUsername.value.length < 3)
CN.new("Error", "Username must be at least 3 characters long", CN.defaultIcon.error); CN.new("Error", "Username must be at least 3 characters long", CN.defaultIcon.error);
else if (inputUsername.value.length > 20)
CN.new("Error", "Username must be at most 20 characters long", CN.defaultIcon.error);
else if (inputUsername.value.search(' ') !== -1)
CN.new("Error", "Username must not contain spaces", CN.defaultIcon.error);
else if (inputUsername.value.search(/[^a-zA-Z0-9]/) !== -1) else if (inputUsername.value.search(/[^a-zA-Z0-9]/) !== -1)
CN.new("Error", "Username must contain only letters and numbers", CN.defaultIcon.error); CN.new("Error", "Username must contain only letters and numbers", CN.defaultIcon.error);
else if (inputPass.value.length < 8) else if (inputPass.value.length < 8)
@ -180,7 +187,7 @@ function createNewAccount(e)
else else
{ {
hashPassword(inputPass.value).then((hash) => { hashPassword(inputPass.value).then((hash) => {
sendRequest("createAccount", {username: inputUsername.value, mail: inputMail.value, password: hash}); sendRequest("create_account", {username: inputUsername.value, mail: inputMail.value, password: hash});
}).catch((err) => { }).catch((err) => {
CN.new("Error", "An error occured while trying to create a new account", CN.defaultIcon.error); CN.new("Error", "An error occured while trying to create a new account", CN.defaultIcon.error);
}); });

View File

@ -6,7 +6,7 @@
/* By: edbernar <edbernar@student.42.fr> +#+ +:+ +#+ */ /* By: edbernar <edbernar@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2024/08/07 17:40:15 by edbernar #+# #+# */ /* Created: 2024/08/07 17:40:15 by edbernar #+# #+# */
/* Updated: 2024/08/08 17:07:12 by edbernar ### ########.fr */ /* Updated: 2024/08/09 09:20:03 by edbernar ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@ -14,6 +14,7 @@ import { createNotification as CN } from "../notification/main.js";
import { userMeInfo, waitForLogin } from "../typeResponse/typeLogin.js"; import { userMeInfo, waitForLogin } from "../typeResponse/typeLogin.js";
import { createConnectDiv } from "./createConnectDiv.js"; import { createConnectDiv } from "./createConnectDiv.js";
import { createThreeDiv } from "./createThreeDiv.js"; import { createThreeDiv } from "./createThreeDiv.js";
import { connectedWith42Func } from "./connectedWith42.js";
function login() function login()
{ {
@ -43,4 +44,5 @@ function showLoginDiv()
document.body.appendChild(divLogin); document.body.appendChild(divLogin);
} }
export { login }; export { login };

View File

@ -3,10 +3,10 @@
/* ::: :::::::: */ /* ::: :::::::: */
/* websocket.js :+: :+: :+: */ /* websocket.js :+: :+: :+: */
/* +:+ +:+ +:+ */ /* +:+ +:+ +:+ */
/* By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ */ /* By: edbernar <edbernar@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2024/07/31 22:17:24 by edbernar #+# #+# */ /* Created: 2024/07/31 22:17:24 by edbernar #+# #+# */
/* Updated: 2024/08/07 22:14:03 by edbernar ### ########.fr */ /* Updated: 2024/08/09 09:21:28 by edbernar ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@ -14,6 +14,7 @@ import { typeErrorInvalidPassword } from "./typeErrorResponse/typeErrorInvalidPa
import { typePrivateListMessage } from "./typeResponse/typePrivateListMessage.js"; import { typePrivateListMessage } from "./typeResponse/typePrivateListMessage.js";
import { typeNewPrivateMessage } from "./typeResponse/typeNewPrivateMessage.js"; import { typeNewPrivateMessage } from "./typeResponse/typeNewPrivateMessage.js";
import { typePrivateListUser } from "./typeResponse/typePrivateListUser.js"; import { typePrivateListUser } from "./typeResponse/typePrivateListUser.js";
import { connectedWith42Func } from "./login/connectedWith42.js";
import { typeLogin } from "./typeResponse/typeLogin.js"; import { typeLogin } from "./typeResponse/typeLogin.js";
/* /*
@ -33,7 +34,8 @@ const errorFunction = [typeErrorInvalidPassword];
let status = 0; let status = 0;
function getCookie(name) { function getCookie(name)
{
const value = `; ${document.cookie}`; const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`); const parts = value.split(`; ${name}=`);
let token = null; let token = null;
@ -47,7 +49,7 @@ function getCookie(name) {
} }
socket.onopen = () => { socket.onopen = () => {
let token = getCookie("token"); let token = getCookie("token");
status = 1; status = 1;
console.log('Connected'); console.log('Connected');
@ -57,7 +59,10 @@ socket.onopen = () => {
sendRequest("login", {type: "byToken", token: token}); sendRequest("login", {type: "byToken", token: token});
} }
else else
{
connectedWith42Func();
typeLogin(null); typeLogin(null);
}
}; };
socket.onmessage = (event) => { socket.onmessage = (event) => {
@ -95,17 +100,29 @@ function sendRequest(type, content) {
let coc = null; let coc = null;
if (status === 0) if (status === 0)
{
console.warn('Not connected');
return ; return ;
}
if (content instanceof Object) if (content instanceof Object)
coc = JSON.stringify(content); coc = JSON.stringify(content);
else else
coc = content; coc = content;
if (getCookie("token"))
socket.send(JSON.stringify({ {
type: type, socket.send(JSON.stringify({
// token: token, type: type,
content: content token: getCookie("token"),
})); content: content
}));
}
else
{
socket.send(JSON.stringify({
type: type,
content: content
}));
}
} }
export { socket, sendRequest }; export { socket, sendRequest };

View File

@ -3,27 +3,32 @@
# ::: :::::::: # # ::: :::::::: #
# main.py :+: :+: :+: # # main.py :+: :+: :+: #
# +:+ +:+ +:+ # # +:+ +:+ +:+ #
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ # # 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/07 21:22:18 by edbernar ### ########.fr # # Updated: 2024/08/09 09:03:31 by edbernar ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
from typeRequets.getPrivateListMessage import getPrivateListMessage from typeRequets.getPrivateListMessage import getPrivateListMessage
from typeRequets.getPrivateListUser import getPrivateListUser from typeRequets.getPrivateListUser import getPrivateListUser
from typeRequets.sendPrivateMessage import sendPrivateMessage from typeRequets.sendPrivateMessage import sendPrivateMessage
from typeRequets.createAccount import createAccount
from typeRequets.login import login from typeRequets.login import login
from Class.User import User, connected_clients from Class.User import User, connected_clients
import websockets import websockets
import asyncio import asyncio
import json import json
# Todo : # Todo (Eddy):
# - verifier que l'utilisateur n'est pas déjà connecté pour éviter les doublons # - verifier que l'utilisateur n'est pas déjà connecté pour éviter les doublons
# Todo (Tom) :
# - Mettre des pages temporaires accesibles qu'on envoie par mail pour confirmer le compte
typeRequest = ["login", "get_private_list_user", "get_private_list_message", "send_private_message"] typeRequest = ["login", "get_private_list_user", "get_private_list_message",
functionRequest = [login, getPrivateListUser, getPrivateListMessage, sendPrivateMessage] "send_private_message", "create_account"]
functionRequest = [login, getPrivateListUser, getPrivateListMessage,
sendPrivateMessage, createAccount]
async def handler(websocket, path): async def handler(websocket, path):
if (path != "/"): if (path != "/"):
@ -41,7 +46,7 @@ async def handler(websocket, path):
try: try:
userClass.printDebug(jsonRequest, 0) userClass.printDebug(jsonRequest, 0)
if (jsonRequest["type"] in typeRequest): if (jsonRequest["type"] in typeRequest):
if jsonRequest["type"] == "login": if (jsonRequest["type"] == "login" or jsonRequest["type"] == "create_account"):
await functionRequest[typeRequest.index(jsonRequest["type"])](userClass, jsonRequest["content"]) await functionRequest[typeRequest.index(jsonRequest["type"])](userClass, jsonRequest["content"])
else: else:
if (await userClass.verifyToken(jsonRequest["token"]) == False): if (await userClass.verifyToken(jsonRequest["token"]) == False):

View File

@ -0,0 +1,74 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# createAccount.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: edbernar <edbernar@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2024/08/09 08:08:00 by edbernar #+# #+# #
# Updated: 2024/08/09 08:52:38 by edbernar ### ########.fr #
# #
# **************************************************************************** #
from typeRequets.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(userClass, content):
try:
content["mail"] = content["mail"].lower()
if (content["mail"].find('@') == -1 or content["mail"].find('.') == -1):
await userClass.sendError("Invalid mail", 9006)
return
if (content["username"].find(' ') != -1):
await userClass.sendError("Username must not contain spaces", 9007)
return
if (len(content["username"]) < 3):
await userClass.sendError("Username must be at least 3 characters long", 9008)
return
if (len(content["username"]) > 20):
await userClass.sendError("Username must be at most 20 characters long", 9009)
return
if (content["username"].find(' ') != -1):
await userClass.sendError("Username must not contain spaces", 9011)
return
if (content["username"].isalnum() == False):
await userClass.sendError("Username must contain only letters and numbers", 9012)
return
if (len(content["password"]) < 8):
await userClass.sendError("Password must be at least 8 characters long", 9013)
return
if (bool(re.match(pattern, content["password"]))):
await userClass.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 userClass.sendError("Password must not contain the username", 9015)
return
if (content["mail"] in userList):
await userClass.sendError("Mail already used", 9016)
return
if (content["username"] in userList):
await userClass.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 userClass.send({"type": "create_account", "content": "Account created"})
except Exception as e:
await userClass.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

@ -3,15 +3,17 @@
# ::: :::::::: # # ::: :::::::: #
# login.py :+: :+: :+: # # login.py :+: :+: :+: #
# +:+ +:+ +:+ # # +:+ +:+ +:+ #
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ # # By: edbernar <edbernar@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2024/08/03 08:10:38 by edbernar #+# #+# # # Created: 2024/08/03 08:10:38 by edbernar #+# #+# #
# Updated: 2024/08/08 22:31:18 by edbernar ### ########.fr # # Updated: 2024/08/09 09:41:55 by edbernar ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
from typeRequets.login42.login42 import main42login
import requests import requests
import json import json
import os
# Les requêtes de login peuvent être de 3 types: # Les requêtes de login peuvent être de 3 types:
# <-- {"type" : "login", "content" : {"type": "byToken", "token": "123456"}} # <-- {"type" : "login", "content" : {"type": "byToken", "token": "123456"}}
@ -54,7 +56,7 @@ userList = [
"token": "poiuygfvbdsv5c21vcxvcxhgbjqnkmds546", "token": "poiuygfvbdsv5c21vcxvcxhgbjqnkmds546",
"mail": "ddddd", "mail": "ddddd",
"password": "ddddd", "password": "ddddd",
"id": 2371234 "id": 6423457
} }
] ]
@ -84,26 +86,15 @@ async def loginByPass(userClass, content):
return return
await userClass.send({"type": "error", "content": "Invalid username or password", "code": 9007}) await userClass.send({"type": "error", "content": "Invalid username or password", "code": 9007})
async def verifyToken42(token42):
url = "https://api.intra.42.fr/v2/me"
headers = {
"Authorization": f"Bearer {token42}"
}
response = requests.get(url, headers=headers)
# |Eddy| Regarder ce que renvoie la requete quand elle est valide pour savoir qui rechercher
# dans la base de données
return (response.status_code == 200)
async def loginBy42(userClass, content): async def loginBy42(userClass, content):
# |TOM| Requete pour récuperer les informations de l'utilisateur selon l'intra de la personne # |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 # et créer un token si celui-ci n'existe pas
for user in userList: try:
if (await verifyToken42(content["token42"])): main42login(content)
jsonVar = {"type": "login", "content": {"username": user["username"], "token": user["token"], "id": user["id"]}} except Exception as e:
await userClass.send(json.dumps(jsonVar)) await userClass.sendError("Invalid 42 token", 9008, e)
return
jsonVar = {"type": "error", "content": "Invalid 42 token", "code": 9008}
await userClass.send(json.dumps(jsonVar))
async def login(userClass, content): async def login(userClass, content):
# |TOM| Faire 3 types de requêtes: # |TOM| Faire 3 types de requêtes:

View File

@ -0,0 +1,50 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# login42.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: edbernar <edbernar@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2024/08/09 09:32:17 by edbernar #+# #+# #
# Updated: 2024/08/09 10:03:54 by edbernar ### ########.fr #
# #
# **************************************************************************** #
import requests
import json
import os
UID42 = os.environ.get("uid")
SECRET42 = os.environ.get("secret")
TOKENURL = 'https://api.intra.42.fr/oauth/token'
INFOURL = 'https://api.intra.42.fr/v2/me'
access_token = ""
def main42login(content):
global access_token
print(UID42)
print(SECRET42)
data = {
'grant_type': 'client_credentials',
'client_id': UID42,
'client_secret': SECRET42,
}
response = requests.post(TOKENURL, data=data)
access_token = response.json()["access_token"]
data = {
'grant_type': 'authorization_code',
'client_id': UID42,
'client_secret': SECRET42,
'code': content["token"],
'redirect_uri': 'http://localhost:3000',
}
response = requests.get('https://api.intra.42.fr/v2/me', headers={'Authorization': 'Bearer ' + access_token})
if (response.status_code != 200):
raise Exception("")
response = response.json()
print(response)