From 27e5a6615a58faf24290b67b43fa7e8d27dd766e Mon Sep 17 00:00:00 2001 From: Quentin Roussel Date: Thu, 28 Mar 2024 20:47:05 +0100 Subject: [PATCH] improved password protection code --- traque-front/app/admin/layout.js | 2 +- traque-front/app/admin/login/page.js | 11 ++--- traque-front/app/admin/page.js | 42 ++++-------------- traque-front/app/admin/teams/page.js | 28 ++++++++++++ traque-front/app/team/layout.js | 3 +- traque-front/app/team/page.js | 12 ++---- traque-front/app/team/track/page.js | 18 +++----- .../context/adminConnexionContext.jsx | 33 +++++--------- traque-front/context/teamConnexionContext.jsx | 28 ++++-------- traque-front/hook/useAdmin.jsx | 4 +- traque-front/hook/useGame.jsx | 5 ++- traque-front/hook/useLocalStorage.jsx | 4 +- traque-front/hook/usePasswordProtect.jsx | 15 +++++++ traque-front/hook/useSocketAuth.jsx | 43 +++++++++++++++++++ 14 files changed, 137 insertions(+), 111 deletions(-) create mode 100644 traque-front/app/admin/teams/page.js create mode 100644 traque-front/hook/usePasswordProtect.jsx create mode 100644 traque-front/hook/useSocketAuth.jsx diff --git a/traque-front/app/admin/layout.js b/traque-front/app/admin/layout.js index c1d7931..2069567 100644 --- a/traque-front/app/admin/layout.js +++ b/traque-front/app/admin/layout.js @@ -1,4 +1,4 @@ -import { AdminConnexionProvider } from "@/context/adminConnexionContext"; +import { AdminConnexionProvider} from "@/context/adminConnexionContext"; import { AdminProvider } from "@/context/adminContext"; export default function AdminLayout({ children}) { diff --git a/traque-front/app/admin/login/page.js b/traque-front/app/admin/login/page.js index 041b393..013d1b9 100644 --- a/traque-front/app/admin/login/page.js +++ b/traque-front/app/admin/login/page.js @@ -1,16 +1,11 @@ "use client"; import LoginForm from '@/components/team/loginForm' import { useAdminConnexion } from '@/context/adminConnexionContext'; -import { redirect } from 'next/navigation'; -import React, { useEffect } from 'react' +import React from 'react' export default function AdminLoginPage() { - const { login, loggedIn } = useAdminConnexion(); - useEffect(() => { - if (loggedIn) { - redirect("/admin"); - } - }, [loggedIn]); + const {login, useProtect} = useAdminConnexion(); + useProtect(); return ( ) diff --git a/traque-front/app/admin/page.js b/traque-front/app/admin/page.js index 1806388..ab3ef39 100644 --- a/traque-front/app/admin/page.js +++ b/traque-front/app/admin/page.js @@ -1,36 +1,12 @@ "use client"; -import TeamAddForm from '@/components/admin/teamAdd'; -import TeamEdit from '@/components/admin/teamEdit'; -import TeamList from '@/components/admin/teamList'; -import { useAdminConnexion } from '@/context/adminConnexionContext'; -import useAdmin from '@/hook/useAdmin'; -import { redirect } from 'next/navigation'; -import React, { useEffect, useState } from 'react' +import { useAdminConnexion } from "@/context/adminConnexionContext"; - -export default function Admin() { - const [selectedTeamId, setSelectedTeamId] = useState(null); - const { loggedIn } = useAdminConnexion(); - const { addTeam } = useAdmin(); - - useEffect(() => { - if (!loggedIn) { - redirect("/admin/login"); - } - }, [loggedIn]); - - - - return ( -
-
-

Team list

- - +export default function AdminPage() { + const { useProtect } = useAdminConnexion(); + useProtect(); + return ( +
+

Admin page

-
- -
-
- ) -} + ) +} \ No newline at end of file diff --git a/traque-front/app/admin/teams/page.js b/traque-front/app/admin/teams/page.js new file mode 100644 index 0000000..e4e00c5 --- /dev/null +++ b/traque-front/app/admin/teams/page.js @@ -0,0 +1,28 @@ +"use client"; +import TeamAddForm from '@/components/admin/teamAdd'; +import TeamEdit from '@/components/admin/teamEdit'; +import TeamList from '@/components/admin/teamList'; +import { useAdminConnexion } from '@/context/adminConnexionContext'; +import useAdmin from '@/hook/useAdmin'; +import React, { useState } from 'react' + +export default function TeamAdminPage() { + const [selectedTeamId, setSelectedTeamId] = useState(null); + const { addTeam } = useAdmin(); + const { useProtect } = useAdminConnexion(); + useProtect(); + + + return ( +
+
+

Team list

+ + +
+
+ +
+
+ ) +} diff --git a/traque-front/app/team/layout.js b/traque-front/app/team/layout.js index f877101..0d97272 100644 --- a/traque-front/app/team/layout.js +++ b/traque-front/app/team/layout.js @@ -1,7 +1,8 @@ +"use client"; import { TeamConnexionProvider } from "@/context/teamConnexionContext"; import { TeamProvider } from "@/context/teamContext"; -export default function AdminLayout({ children}) { +export default function AdminLayout({ children }) { return ( diff --git a/traque-front/app/team/page.js b/traque-front/app/team/page.js index 141267a..f6ab21d 100644 --- a/traque-front/app/team/page.js +++ b/traque-front/app/team/page.js @@ -1,16 +1,10 @@ "use client" import LoginForm from "@/components/team/loginForm"; -import useGame from "@/hook/useGame"; -import { redirect } from "next/navigation"; -import { useEffect } from "react"; +import { useTeamConnexion } from "@/context/teamConnexionContext"; export default function Home() { - const { login, loggedIn } = useGame(); - useEffect(() => { - if (loggedIn) { - redirect("/team/track"); - } - }, [loggedIn]); + const { login,useProtect } = useTeamConnexion(); + useProtect(); return (
login(parseInt(value))}/> diff --git a/traque-front/app/team/track/page.js b/traque-front/app/team/track/page.js index 96478ac..eeb12d4 100644 --- a/traque-front/app/team/track/page.js +++ b/traque-front/app/team/track/page.js @@ -1,10 +1,9 @@ "use client"; import ActionDrawer from '@/components/team/actionDrawer'; -import Button from '@/components/util/button'; +import { useTeamConnexion } from '@/context/teamConnexionContext'; import useGame from '@/hook/useGame'; import dynamic from 'next/dynamic'; -import { redirect } from 'next/navigation'; -import React, { useEffect } from 'react' +import React, { use } from 'react' //Load the map without SSR const LiveMap = dynamic(() => import('@/components/team/map'), { @@ -12,17 +11,12 @@ const LiveMap = dynamic(() => import('@/components/team/map'), { }); export default function Track() { - const { currentPosition, enemyPosition, loggedIn } = useGame(); - useEffect(() => { - if (!loggedIn) { - redirect("/team"); - } - }, [loggedIn]); - - + const { currentPosition, enemyPosition } = useGame(); + const {useProtect} = useTeamConnexion(); + useProtect(); return (
- +
) diff --git a/traque-front/context/adminConnexionContext.jsx b/traque-front/context/adminConnexionContext.jsx index d2949ad..7f8c992 100644 --- a/traque-front/context/adminConnexionContext.jsx +++ b/traque-front/context/adminConnexionContext.jsx @@ -1,38 +1,27 @@ "use client"; -import { createContext, useContext, useEffect, useMemo, useState } from "react"; +import { createContext, useContext, useEffect, useMemo, } from "react"; import { useSocket } from "./socketContext"; -import { useSocketListener } from "@/hook/useSocketListener"; -import { useLocalStorage } from "@/hook/useLocalStorage"; +import { useSocketAuth } from "@/hook/useSocketAuth"; +import { redirect, usePathname } from "next/navigation"; +import { usePasswordProtect } from "@/hook/usePasswordProtect"; -const adminContext = createContext(); +const adminConnexionContext = createContext(); const AdminConnexionProvider = ({ children }) => { - const [loggedIn, setLoggedIn] = useState(false); - const [savedPassword, setSavedPassword] = useLocalStorage("admin_password", null); const { adminSocket } = useSocket(); + const { login, loggedIn, loading } = useSocketAuth(adminSocket, "admin_password"); + const useProtect = () => usePasswordProtect("/admin/login", "/admin", loading, loggedIn); - useEffect(() => { - if (savedPassword && !loggedIn) { - adminSocket.emit("login", savedPassword); - } - }, [savedPassword]); - - function login(password) { - setSavedPassword(password) - } - - useSocketListener(adminSocket, "login_response", setLoggedIn); - - const value = useMemo(() => ({ login, loggedIn }), [loggedIn]); + const value = useMemo(() => ({ login, loggedIn, loading, useProtect }), [loggedIn, loading]); return ( - + {children} - + ); } function useAdminConnexion() { - return useContext(adminContext); + return useContext(adminConnexionContext); } export { AdminConnexionProvider, useAdminConnexion }; diff --git a/traque-front/context/teamConnexionContext.jsx b/traque-front/context/teamConnexionContext.jsx index 68c2e27..9b82f24 100644 --- a/traque-front/context/teamConnexionContext.jsx +++ b/traque-front/context/teamConnexionContext.jsx @@ -1,32 +1,20 @@ "use client"; -import { createContext, useContext, useEffect, useMemo, useState } from "react"; +import { createContext, useContext, useMemo } from "react"; import { useSocket } from "./socketContext"; -import { useSocketListener } from "@/hook/useSocketListener"; -import { useLocalStorage } from "@/hook/useLocalStorage"; +import { useSocketAuth } from "@/hook/useSocketAuth"; +import { usePasswordProtect } from "@/hook/usePasswordProtect"; const teamConnexionContext = createContext(); const TeamConnexionProvider = ({ children }) => { - const [loggedIn, setLoggedIn] = useState(false); - const [teamId, setTeamId] = useLocalStorage("team_id", null); const { teamSocket } = useSocket(); + const { login, password: teamId, loggedIn, loading } = useSocketAuth(teamSocket, "team_password"); + const useProtect = () => usePasswordProtect("/team", "/team/track", loading, loggedIn); - useEffect(() => { - if (teamId && !loggedIn) { - teamSocket.emit("login", teamId); - } - }, [teamId]); + const value = useMemo(() => ({ teamId, login, loggedIn, loading, useProtect}), [teamId, login, loggedIn, loading]); - function login(id) { - setTeamId(id); - } - - useSocketListener(teamSocket, "login_response", setLoggedIn); - - const value = useMemo(() => ({ teamId, login, loggedIn }), [teamId, login, loggedIn]); - return ( - {children} + {children} ); } @@ -35,5 +23,5 @@ function useTeamConnexion() { return useContext(teamConnexionContext); } -export { TeamConnexionProvider, useTeamConnexion}; +export { TeamConnexionProvider, useTeamConnexion }; diff --git a/traque-front/hook/useAdmin.jsx b/traque-front/hook/useAdmin.jsx index 45e9ede..991bb8f 100644 --- a/traque-front/hook/useAdmin.jsx +++ b/traque-front/hook/useAdmin.jsx @@ -2,7 +2,7 @@ import { useAdminContext } from "@/context/adminContext"; import { useSocket } from "@/context/socketContext"; export default function useAdmin(){ - const {teams, started} = useAdminContext(); + const {teams, started } = useAdminContext(); const {adminSocket} = useSocket(); function pollTeams() { @@ -42,6 +42,6 @@ export default function useAdmin(){ adminSocket.emit("stop_game"); } - return { teams, started, pollTeams, getTeam, getTeamName, reorderTeams, addTeam, removeTeam, startGame, stopGame, setTeamName }; + return {teams, started, pollTeams, getTeam, getTeamName, reorderTeams, addTeam, removeTeam, startGame, stopGame, setTeamName }; } \ No newline at end of file diff --git a/traque-front/hook/useGame.jsx b/traque-front/hook/useGame.jsx index b9e200b..d255dd1 100644 --- a/traque-front/hook/useGame.jsx +++ b/traque-front/hook/useGame.jsx @@ -6,12 +6,13 @@ import { useTeamContext } from "@/context/teamContext"; export default function useGame() { const {teamSocket} = useSocket(); - const {loggedIn, login, teamId} = useTeamConnexion(); + const {teamId} = useTeamConnexion(); const {currentPosition, enemyPosition} = useTeamContext(); function sendCurrentPosition() { teamSocket.emit("send_position"); } - return { sendCurrentPosition, login, enemyPosition, currentPosition, loggedIn, teamId }; + + return { sendCurrentPosition, enemyPosition, currentPosition, teamId }; } \ No newline at end of file diff --git a/traque-front/hook/useLocalStorage.jsx b/traque-front/hook/useLocalStorage.jsx index 103da22..14a63ce 100644 --- a/traque-front/hook/useLocalStorage.jsx +++ b/traque-front/hook/useLocalStorage.jsx @@ -3,6 +3,7 @@ import { useEffect, useState } from "react"; export function useLocalStorage(key, initialValue) { const [storedValue, setStoredValue] = useState(initialValue); + const [loading, setLoading] = useState(true); useEffect(() => { try { @@ -11,6 +12,7 @@ export function useLocalStorage(key, initialValue) { } catch (error) { console.log(error); } + setLoading(false); }, []); const setValue = value => { @@ -23,5 +25,5 @@ export function useLocalStorage(key, initialValue) { } } - return [storedValue, setValue]; + return [storedValue, setValue, loading]; } \ No newline at end of file diff --git a/traque-front/hook/usePasswordProtect.jsx b/traque-front/hook/usePasswordProtect.jsx new file mode 100644 index 0000000..581bce4 --- /dev/null +++ b/traque-front/hook/usePasswordProtect.jsx @@ -0,0 +1,15 @@ +"use client"; +import { redirect, usePathname } from "next/navigation"; +import { useEffect } from "react"; + +export function usePasswordProtect(loginPath, redirectPath, loading, loggedIn) { + const path = usePathname(); + useEffect(() => { + if (!loggedIn && !loading && path !== loginPath) { + redirect(loginPath); + } + if(loggedIn && !loading && path === loginPath) { + redirect(redirectPath) + } + }, [loggedIn, loading, path]); +} \ No newline at end of file diff --git a/traque-front/hook/useSocketAuth.jsx b/traque-front/hook/useSocketAuth.jsx new file mode 100644 index 0000000..7f4bfdb --- /dev/null +++ b/traque-front/hook/useSocketAuth.jsx @@ -0,0 +1,43 @@ +import {useEffect, useState} from 'react'; +import { useSocketListener } from './useSocketListener'; +import { useLocalStorage } from './useLocalStorage'; +import { usePathname } from 'next/navigation'; + +const LOGIN_MESSAGE = "login"; +const LOGIN_RESPONSE_MESSAGE = "login_response"; + +export function useSocketAuth(socket, passwordName) { + const [loggedIn, setLoggedIn] = useState(false); + const [loading, setLoading] = useState(true); + const [waitingForResponse, setWaitingForResponse] = useState(true); + const [savedPassword, setSavedPassword, savedPasswordLoading] = useLocalStorage(passwordName, null); + + useEffect(() => { + if (savedPassword && !loggedIn) { + socket.emit(LOGIN_MESSAGE, savedPassword); + } + }, [savedPassword]); + + function login(password) { + setSavedPassword(password) + } + + useSocketListener(socket, LOGIN_RESPONSE_MESSAGE,(loginResponse) => { + setWaitingForResponse(false); + setLoggedIn(loginResponse); + }); + + useEffect(() => { + //There is a password saved and we recieved a login response + if(savedPassword && !waitingForResponse && !savedPasswordLoading) { + setLoading(false); + } + //We tried to load the saved password but it is not there + else if (savedPassword == null && !savedPasswordLoading) { + setLoading(false); + } + }, [waitingForResponse, savedPasswordLoading, savedPassword]); + + + return {login,password: savedPassword, loggedIn, loading}; +} \ No newline at end of file