diff --git a/package.json b/package.json index d895272..e007433 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,13 @@ "type": "module", "scripts": { "build": "tsc && tsc-alias", - "start": "tsx src/server.ts" + "start": "tsx src/server.ts", + "fmt": "dprint fmt" }, "devDependencies": { + "@types/bcrypt": "^5.0.2", "@types/bun": "^1.0.4", + "dprint": "^0.46.2", "tsc-alias": "^1.8.10", "tsx": "^4.7.0", "typescript": "^5.3.3" @@ -15,11 +18,14 @@ "dependencies": { "@codemirror/collab": "^6.1.1", "@codemirror/state": "^6.4.1", + "@fastify/cookie": "^9.3.1", "@fastify/cors": "^9.0.0", + "@fastify/session": "^10.9.0", "@fastify/type-provider-typebox": "^4.0.0", "@fastify/websocket": "^10.0.1", "@sinclair/typebox": "^0.32.9", "dprint": "^0.46.1", + "bcrypt": "^5.1.1", "fastify": "^4.27.0", "nanoid": "^5.0.4", "sqlite3": "^5.1.7", diff --git a/src/database.ts b/src/database.ts index 3aa61be..79f02d4 100644 --- a/src/database.ts +++ b/src/database.ts @@ -4,6 +4,11 @@ import sqlite3 from "sqlite3"; const dbDirectory = "./src/db"; const dbFilePath = `${dbDirectory}/database.db`; +export type error = { + errno: number; + code: string; +}; + /* Fonction pour exécuter une requête sur la base de données */ /* Fonction pour exécuter une requête de modification de la base de données (INSERT, UPDATE, DELETE) */ @@ -44,9 +49,9 @@ export function getDB( db: sqlite3.Database, query: string, params: any[], -): Promise { +): Promise { return new Promise((resolve, reject) => { - db.get(query, params, (err, row: any) => { + db.get(query, params, (err, row: T) => { if (err) { reject(err); } else { @@ -112,17 +117,48 @@ export function createRegisteredUserTable(db: sqlite3.Database): Promise { } /* Insérer un utilisateur dans la table registered_user */ -export function insertUser( +export async function insertUser( db: sqlite3.Database, login: string, password: string, permissions: number, -) { +): Promise { const insertUserQuery = `INSERT INTO registered_user (login, password, permissions) VALUES (?, ?, ?)`; - return runDB(db, insertUserQuery, [login, password, permissions]); + try { + await runDB(db, insertUserQuery, [login, password, permissions]); + return true; + } catch (e) { + const error = e as error; + if (error.code === "SQLITE_CONSTRAINT") { + return false; + } else { + throw e; + } + } +} + +/* Vérifier si un utilisateur existe dans la table registered_user */ +export async function verifyUser( + db: sqlite3.Database, + login: string, +): Promise { + const verifyUserQuery = `SELECT login, password FROM registered_user WHERE login = ?`; + + const res = await getDB(db, verifyUserQuery, [login]); + + if (!res) { + return null; + } else { + return res; + } } +export type User = { + login: string; + password: string; +}; + /* Modifier le login d'un utilisateur dans la table registered_user */ export function updateUserLogin( db: sqlite3.Database, diff --git a/src/server.ts b/src/server.ts index b4798e0..27635ad 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,13 +1,16 @@ -import { rebaseUpdates, Update } from "@codemirror/collab"; -import { ChangeSet, Text } from "@codemirror/state"; import cors from "@fastify/cors"; -import { Type, TypeBoxTypeProvider } from "@fastify/type-provider-typebox"; import websocket, { WebSocket } from "@fastify/websocket"; +import { Type, TypeBoxTypeProvider } from "@fastify/type-provider-typebox"; import Fastify, { FastifyReply } from "fastify"; import { nanoid } from "nanoid"; import { allocateBuffer, getRunner } from "./runner"; import { Pull, Push } from "zeromq"; +import { ChangeSet, Text } from "@codemirror/state"; +import { Update, rebaseUpdates } from "@codemirror/collab"; import * as db from "./database"; +import bcrypt from "bcrypt"; +import { fastifySession } from "@fastify/session"; +import { fastifyCookie } from "@fastify/cookie"; const sender = new Push(); await sender.bind(`tcp://127.0.0.1:5557`); @@ -39,9 +42,28 @@ const fastify = Fastify({ type Fastify = typeof fastify; await fastify.register(cors, { origin: process.env.ALLOW_ORIGIN || "*", + credentials: true, + methods: ["GET", "POST", "PUT", "DELETE"], }); -fastify.register(websocket); +fastify.register(fastifyCookie); +fastify.register(fastifySession, { + secret: "8jYuS75JZuxb6C72nDtH2cY6hnV4B7i35r5c39gQ3h9G9DApAweBsQ47dU9DGpk5", + cookie: { + secure: false, + sameSite: "none", + partitioned: true, + }, + saveUninitialized: false, + cookieName: "session-id", +}); + +declare module "fastify" { + interface Session { + userKey: string | null; + } +} +fastify.register(websocket); fastify.register(async function(fastify: Fastify) { fastify.get( "/live/:roomId", @@ -180,11 +202,56 @@ fastify.post( }, async (request, reply) => { const { login, password, permissions } = request.body; - db.insertUser(database, login, password, permissions); - reply.send({ success: true }); + + // Hashage du mot de passe + const saltRounds = 10; + const hashedPassword = await bcrypt.hash(password, saltRounds); + + const success = await db.insertUser( + database, + login, + hashedPassword, + permissions + ); + reply.send({ success }); + } +); + +/* Route pour vérifier si un utilisateur existe */ +fastify.post( + "/users/login", + { + schema: { + body: Type.Object({ + login: Type.String(), + password: Type.String(), + }), + }, + }, + async (request, reply) => { + const { login, password } = request.body; + const user = await db.verifyUser(database, login); + + if (user === null || !(await bcrypt.compare(password, user.password))) { + reply.send({ success: false }); + } else { + request.session.userKey = generateId(); + reply.send({ success: true }); + } + + bcrypt.compare(password, user!.password) + .then(res => reply.send({ sucess: res })) + .catch(err => reply.send({ sucess: false })); }, ); +/* Route pour se déconnecter */ +fastify.get("/users/logout", async (request, reply) => { + console.log(request.session.userKey); + request.session.destroy(); + reply.send({ success: true }); +}); + /* Route pour mettre à jour le login d'un utilisateur */ fastify.put( "/users/:id/login", @@ -205,7 +272,7 @@ fastify.put( const { newLogin } = request.body; db.updateUserLogin(database, id, newLogin); reply.send({ success: true }); - }, + } ); /* Route pour mettre à jour le mot de passe d'un utilisateur */ @@ -226,9 +293,13 @@ fastify.put( async (request, reply) => { const { id } = request.params; const { newPassword } = request.body; - db.updateUserPassword(database, id, newPassword); + + const saltRounds = 10; + const hashedPassword = await bcrypt.hash(newPassword, saltRounds); + + await db.updateUserPassword(database, id, hashedPassword); reply.send({ success: true }); - }, + } ); /* Route pour mettre à jour les permissions d'un utilisateur */ @@ -251,7 +322,7 @@ fastify.put( const { newPermissions } = request.body; await db.updateUserPermissions(database, id, newPermissions); reply.send({ success: true }); - }, + } ); /* Route pour supprimer un utilisateur par son ID */ @@ -270,7 +341,7 @@ fastify.delete( const { id } = request.params; await db.deleteUserById(database, id); reply.send({ success: true }); - }, + } ); /* Route pour supprimer un utilisateur par son login */ @@ -287,7 +358,7 @@ fastify.delete( const { login } = request.params; await db.deleteUserByLogin(database, login); reply.send({ success: true }); - }, + } ); /* Route pour supprimer tous les utilisateurs */ @@ -316,9 +387,11 @@ fastify.get( }, async (request, reply) => { const { id } = request.params; - const user = await db.selectUserById(database, id); - reply.send(user); - }, + if (request.session.userKey) { + const user = await db.selectUserById(database, id); + reply.send(user); + } + } ); /* Route pour récupérer un utilisateur par son login */ @@ -335,7 +408,7 @@ fastify.get( const { login } = request.params; const user = await db.selectUserByLogin(database, login); reply.send(user); - }, + } ); /* Route pour créer un language */ @@ -353,7 +426,7 @@ fastify.post( const { designation, version } = request.body; db.insertLanguage(database, designation, version); reply.send({ success: true }); - }, + } ); /* Route pour mettre à jour la désignation d'un language */ @@ -376,7 +449,7 @@ fastify.put( const { newDesignation } = request.body; db.updateLanguageDesignation(database, id, newDesignation); reply.send({ success: true }); - }, + } ); /* Route pour mettre à jour la version d'un language */ @@ -399,7 +472,7 @@ fastify.put( const { newVersion } = request.body; db.updateLanguageVersion(database, id, newVersion); reply.send({ success: true }); - }, + } ); /* Route pour supprimer un language */ @@ -418,7 +491,7 @@ fastify.delete( const { id } = request.params; db.deleteLanguage(database, id); reply.send({ success: true }); - }, + } ); /* Route pour supprimer tous les languages */ @@ -443,7 +516,7 @@ fastify.get( const { id } = request.params; const language = await db.selectLanguageById(database, id); reply.send(language); - }, + } ); /* Route pour récupérer tous les languages */ @@ -469,7 +542,7 @@ fastify.post( const { id_user, link, id_language, code } = request.body; db.insertWork(database, link, id_user, id_language, code); reply.send({ success: true }); - }, + } ); /* Route pour récupérer tous les works */ @@ -500,7 +573,7 @@ fastify.delete( const { id } = request.params; db.deleteWork(database, id); reply.send({ success: true }); - }, + } ); /* Route pour récupérer un work par son ID */ @@ -519,7 +592,7 @@ fastify.get( const { id } = request.params; const work = await db.selectWorkById(database, id); reply.send(work); - }, + } ); /* Forward output est une fonction asynchrone qui permet de récupérer les messages envoyés par le container et de les renvoyer au client */