diff --git a/site/interface/listError.txt b/site/interface/listError.txt index c78b396..56af8b2 100644 --- a/site/interface/listError.txt +++ b/site/interface/listError.txt @@ -10,4 +10,5 @@ - 9006 : Invalid login type - 9007 : Invalid username or password - 9008 : User not found -- 9009 : Invalid message sent \ No newline at end of file +- 9009 : Invalid message sent +- 9010 : Invalid token 42 \ No newline at end of file diff --git a/site/interface/site/login/connectedWith42.js b/site/interface/site/login/connectedWith42.js new file mode 100644 index 0000000..308e267 --- /dev/null +++ b/site/interface/site/login/connectedWith42.js @@ -0,0 +1,24 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* connectedWith42.js :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: edbernar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* 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 }; \ No newline at end of file diff --git a/site/interface/site/login/createConnectDiv.js b/site/interface/site/login/createConnectDiv.js index e7674ed..c5a0e66 100644 --- a/site/interface/site/login/createConnectDiv.js +++ b/site/interface/site/login/createConnectDiv.js @@ -3,10 +3,10 @@ /* ::: :::::::: */ /* createConnectDiv.js :+: :+: :+: */ /* +:+ +:+ +:+ */ -/* 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) : - - Gerer coté serveur le type "createAccount" - ajouter un message de confirmation de création de compte et un message d'erreur - une fleche pour revenir en arriere - 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 - Connexion par 42 - Todo (Tom) : - - Mettre des pages temporaires accesibles qu'on envoie par mail pour confirmer le compte - */ function createConnectDiv(divLogin) @@ -38,6 +34,7 @@ function createConnectDiv(divLogin) const inputPass = document.createElement("input"); const buttonLogin = createButton(inputLogin, inputPass); const buttonNewAcc = createButtonNewAcc(divConnect, divLogin); + const buttonConnect42 = document.createElement("button"); addGlobalBg(); divConnect.setAttribute("id", "connectDiv"); @@ -48,15 +45,21 @@ function createConnectDiv(divLogin) inputPass.setAttribute("autocomplete", "current-password"); inputPass.setAttribute("placeholder", "password"); buttonLogin.innerHTML = "Connect"; + buttonConnect42.innerHTML = "Connect with 42"; form.appendChild(inputLogin); form.appendChild(inputPass); form.appendChild(buttonLogin); form.appendChild(buttonNewAcc); + form.appendChild(buttonConnect42); divConnect.appendChild(form); form.addEventListener('submit', (e) => { e.preventDefault(); 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); } @@ -167,6 +170,10 @@ function createNewAccount(e) } else if (inputUsername.value.length < 3) 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) CN.new("Error", "Username must contain only letters and numbers", CN.defaultIcon.error); else if (inputPass.value.length < 8) @@ -180,7 +187,7 @@ function createNewAccount(e) else { 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) => { CN.new("Error", "An error occured while trying to create a new account", CN.defaultIcon.error); }); diff --git a/site/interface/site/login/main.js b/site/interface/site/login/main.js index a467f9b..2b565a7 100644 --- a/site/interface/site/login/main.js +++ b/site/interface/site/login/main.js @@ -6,7 +6,7 @@ /* 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 { createConnectDiv } from "./createConnectDiv.js"; import { createThreeDiv } from "./createThreeDiv.js"; +import { connectedWith42Func } from "./connectedWith42.js"; function login() { @@ -43,4 +44,5 @@ function showLoginDiv() document.body.appendChild(divLogin); } + export { login }; \ No newline at end of file diff --git a/site/interface/site/websocket.js b/site/interface/site/websocket.js index 56df964..4d3b044 100644 --- a/site/interface/site/websocket.js +++ b/site/interface/site/websocket.js @@ -3,10 +3,10 @@ /* ::: :::::::: */ /* websocket.js :+: :+: :+: */ /* +:+ +:+ +:+ */ -/* 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 { typeNewPrivateMessage } from "./typeResponse/typeNewPrivateMessage.js"; import { typePrivateListUser } from "./typeResponse/typePrivateListUser.js"; +import { connectedWith42Func } from "./login/connectedWith42.js"; import { typeLogin } from "./typeResponse/typeLogin.js"; /* @@ -33,7 +34,8 @@ const errorFunction = [typeErrorInvalidPassword]; let status = 0; -function getCookie(name) { +function getCookie(name) +{ const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); let token = null; @@ -47,7 +49,7 @@ function getCookie(name) { } socket.onopen = () => { - let token = getCookie("token"); + let token = getCookie("token"); status = 1; console.log('Connected'); @@ -57,7 +59,10 @@ socket.onopen = () => { sendRequest("login", {type: "byToken", token: token}); } else + { + connectedWith42Func(); typeLogin(null); + } }; socket.onmessage = (event) => { @@ -95,17 +100,29 @@ function sendRequest(type, content) { let coc = null; if (status === 0) + { + console.warn('Not connected'); return ; + } if (content instanceof Object) coc = JSON.stringify(content); else coc = content; - - socket.send(JSON.stringify({ - type: type, - // token: token, - content: content - })); + if (getCookie("token")) + { + socket.send(JSON.stringify({ + type: type, + token: getCookie("token"), + content: content + })); + } + else + { + socket.send(JSON.stringify({ + type: type, + content: content + })); + } } export { socket, sendRequest }; \ No newline at end of file diff --git a/websocket-server/main.py b/websocket-server/main.py index 954bb2b..29d51ec 100644 --- a/websocket-server/main.py +++ b/websocket-server/main.py @@ -3,27 +3,32 @@ # ::: :::::::: # # main.py :+: :+: :+: # # +:+ +:+ +:+ # -# 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.getPrivateListUser import getPrivateListUser from typeRequets.sendPrivateMessage import sendPrivateMessage +from typeRequets.createAccount import createAccount from typeRequets.login import login from Class.User import User, connected_clients import websockets import asyncio import json -# Todo : +# Todo (Eddy): # - 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"] -functionRequest = [login, getPrivateListUser, getPrivateListMessage, sendPrivateMessage] +typeRequest = ["login", "get_private_list_user", "get_private_list_message", + "send_private_message", "create_account"] +functionRequest = [login, getPrivateListUser, getPrivateListMessage, + sendPrivateMessage, createAccount] async def handler(websocket, path): if (path != "/"): @@ -41,7 +46,7 @@ async def handler(websocket, path): try: userClass.printDebug(jsonRequest, 0) 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"]) else: if (await userClass.verifyToken(jsonRequest["token"]) == False): diff --git a/websocket-server/typeRequets/createAccount.py b/websocket-server/typeRequets/createAccount.py new file mode 100644 index 0000000..a671744 --- /dev/null +++ b/websocket-server/typeRequets/createAccount.py @@ -0,0 +1,74 @@ +# **************************************************************************** # +# # +# ::: :::::::: # +# createAccount.py :+: :+: :+: # +# +:+ +:+ +:+ # +# By: edbernar +#+ +:+ +#+ # +# +#+#+#+#+#+ +#+ # +# 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 \ No newline at end of file diff --git a/websocket-server/typeRequets/login.py b/websocket-server/typeRequets/login.py index c9912c7..a9ed497 100644 --- a/websocket-server/typeRequets/login.py +++ b/websocket-server/typeRequets/login.py @@ -3,15 +3,17 @@ # ::: :::::::: # # login.py :+: :+: :+: # # +:+ +:+ +:+ # -# 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 json +import os # Les requêtes de login peuvent être de 3 types: # <-- {"type" : "login", "content" : {"type": "byToken", "token": "123456"}} @@ -54,7 +56,7 @@ userList = [ "token": "poiuygfvbdsv5c21vcxvcxhgbjqnkmds546", "mail": "ddddd", "password": "ddddd", - "id": 2371234 + "id": 6423457 } ] @@ -84,26 +86,15 @@ async def loginByPass(userClass, content): return 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): # |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 - for user in userList: - if (await verifyToken42(content["token42"])): - jsonVar = {"type": "login", "content": {"username": user["username"], "token": user["token"], "id": user["id"]}} - await userClass.send(json.dumps(jsonVar)) - return - jsonVar = {"type": "error", "content": "Invalid 42 token", "code": 9008} - await userClass.send(json.dumps(jsonVar)) + try: + main42login(content) + except Exception as e: + await userClass.sendError("Invalid 42 token", 9008, e) async def login(userClass, content): # |TOM| Faire 3 types de requêtes: diff --git a/websocket-server/typeRequets/login42/login42.py b/websocket-server/typeRequets/login42/login42.py new file mode 100644 index 0000000..325d588 --- /dev/null +++ b/websocket-server/typeRequets/login42/login42.py @@ -0,0 +1,50 @@ +# **************************************************************************** # +# # +# ::: :::::::: # +# login42.py :+: :+: :+: # +# +:+ +:+ +:+ # +# By: edbernar +#+ +:+ +#+ # +# +#+#+#+#+#+ +#+ # +# 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) + \ No newline at end of file