Merge pull request #152 from ets-cfuhrman-pfe/feat-auth-only-student

Adds (no) authentication requirement to join a room
This commit is contained in:
Gabriel Moisan Matte 2024-10-29 00:05:02 -04:00 committed by GitHub
commit ba77b73899
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 113 additions and 3 deletions

View file

@ -28,6 +28,7 @@ import OAuthCallback from './pages/AuthManager/callback/AuthCallback';
const App: React.FC = () => { const App: React.FC = () => {
const [isAuthenticated, setIsAuthenticated] = useState(ApiService.isLoggedIn()); const [isAuthenticated, setIsAuthenticated] = useState(ApiService.isLoggedIn());
const [isTeacherAuthenticated, setIsTeacherAuthenticated] = useState(ApiService.isLoggedInTeacher()); const [isTeacherAuthenticated, setIsTeacherAuthenticated] = useState(ApiService.isLoggedInTeacher());
const [isRoomRequireAuthentication, setRoomsRequireAuth] = useState(null);
const location = useLocation(); const location = useLocation();
// Check login status every time the route changes // Check login status every time the route changes
@ -37,7 +38,13 @@ const App: React.FC = () => {
setIsTeacherAuthenticated(ApiService.isLoggedInTeacher()); setIsTeacherAuthenticated(ApiService.isLoggedInTeacher());
}; };
const fetchAuthenticatedRooms = async () => {
const data = await ApiService.getRoomsRequireAuth();
setRoomsRequireAuth(data);
};
checkLoginStatus(); checkLoginStatus();
fetchAuthenticatedRooms();
}, [location]); }, [location]);
const handleLogout = () => { const handleLogout = () => {
@ -76,7 +83,7 @@ const App: React.FC = () => {
{/* Pages espace étudiant */} {/* Pages espace étudiant */}
<Route <Route
path="/student/join-room" path="/student/join-room"
element={isAuthenticated ? <JoinRoom /> : <Navigate to="/login" />} element={( !isRoomRequireAuthentication || isAuthenticated ) ? <JoinRoom /> : <Navigate to="/login" />}
/> />
{/* Pages authentification */} {/* Pages authentification */}

View file

@ -9,9 +9,11 @@ const OAuthCallback: React.FC = () => {
useEffect(() => { useEffect(() => {
const searchParams = new URLSearchParams(location.search); const searchParams = new URLSearchParams(location.search);
const user = searchParams.get('user'); const user = searchParams.get('user');
const username = searchParams.get('username');
if (user) { if (user) {
apiService.saveToken(user); apiService.saveToken(user);
apiService.saveUsername(username || "");
navigate('/'); navigate('/');
} else { } else {
navigate('/login'); navigate('/login');

View file

@ -15,9 +15,11 @@ import LoadingButton from '@mui/lab/LoadingButton';
import LoginContainer from '../../../components/LoginContainer/LoginContainer' import LoginContainer from '../../../components/LoginContainer/LoginContainer'
import ApiService from '../../../services/ApiService'
const JoinRoom: React.FC = () => { const JoinRoom: React.FC = () => {
const [roomName, setRoomName] = useState(''); const [roomName, setRoomName] = useState('');
const [username, setUsername] = useState(''); const [username, setUsername] = useState(ApiService.getUsername());
const [socket, setSocket] = useState<Socket | null>(null); const [socket, setSocket] = useState<Socket | null>(null);
const [isWaitingForTeacher, setIsWaitingForTeacher] = useState(false); const [isWaitingForTeacher, setIsWaitingForTeacher] = useState(false);
const [question, setQuestion] = useState<QuestionType>(); const [question, setQuestion] = useState<QuestionType>();

View file

@ -105,7 +105,43 @@ class ApiService {
} }
} }
public saveUsername(username: string): void {
if (!username || username.length === 0) {
return;
}
const object = {
username: username
}
localStorage.setItem("username", JSON.stringify(object));
}
public getUsername(): string {
const objectStr = localStorage.getItem("username");
if (!objectStr) {
return "";
}
const object = JSON.parse(objectStr)
return object.username;
}
// Route to know if rooms need authentication to join
public async getRoomsRequireAuth(): Promise<any> {
const url: string = this.constructRequestUrl(`/auth/getRoomsRequireAuth`);
const result: AxiosResponse = await axios.get(url);
if (result.status == 200) {
return result.data.roomsRequireAuth;
}
return false;
}
public logout(): void { public logout(): void {
localStorage.removeItem("username");
return localStorage.removeItem("jwt"); return localStorage.removeItem("jwt");
} }

View file

@ -30,6 +30,7 @@ services:
SITE_URL: http://localhost SITE_URL: http://localhost
FRONTEND_PORT: 5173 FRONTEND_PORT: 5173
USE_PORTS: false USE_PORTS: false
AUTHENTICATED_ROOMS: false
volumes: volumes:
- ./server/auth_config.json:/usr/src/app/serveur/config/auth_config.json - ./server/auth_config.json:/usr/src/app/serveur/config/auth_config.json
depends_on: depends_on:

View file

@ -19,3 +19,5 @@ SESSION_Secret='session_secret'
SITE_URL=http://localhost SITE_URL=http://localhost
FRONTEND_PORT=5173 FRONTEND_PORT=5173
USE_PORTS=false USE_PORTS=false
AUTHENTICATED_ROOMS=false

View file

@ -205,3 +205,40 @@ describe(
}); });
}) })
); );
describe(
"Rooms requiring authentication", () => {
// Making a copy of env variables to restore them later
const OLD_ENV_VARIABLES = process.env;
let authConfigInstance;
beforeAll(() => {
authConfigInstance = new AuthConfig();
});
// Clearing cache just in case
beforeEach(() => {
jest.resetModules();
process.env = { ...OLD_ENV_VARIABLES };
});
// Resetting the old values
afterAll(() => {
process.env = OLD_ENV_VARIABLES;
});
// tests cases as [environment variable value, expected value]
const cases = [["true", true], ["false", false], ["", false], ["other_than_true_false", false]];
test.each(cases)(
"Given %p as AUTHENTICATED_ROOMS environment variable value, returns %p",
(envVarArg, expectedResult) => {
process.env.AUTHENTICATED_ROOMS = envVarArg;
const isAuthRequired = authConfigInstance.getRoomsRequireAuth();
expect(isAuthRequired).toEqual(expectedResult);
}
);
}
)

View file

@ -44,7 +44,7 @@ class AuthManager{
async login(userInfo,req,res,next){ async login(userInfo,req,res,next){
const tokenToSave = jwt.create(userInfo.email, userInfo._id,userInfo.roles); const tokenToSave = jwt.create(userInfo.email, userInfo._id,userInfo.roles);
res.redirect(`/auth/callback?user=${tokenToSave}`); res.redirect(`/auth/callback?user=${tokenToSave}&username=${userInfo.name}`);
console.info(`L'utilisateur '${userInfo.name}' vient de se connecter`) console.info(`L'utilisateur '${userInfo.name}' vient de se connecter`)
} }

View file

@ -175,6 +175,17 @@ class AuthConfig {
} }
} }
// Check if students must be authenticated to join a room
getRoomsRequireAuth() {
const roomRequireAuth = process.env.AUTHENTICATED_ROOMS;
if (!roomRequireAuth || roomRequireAuth !== "true") {
return false;
}
return true;
}
} }

View file

@ -20,6 +20,17 @@ class authController {
} }
} }
async getRoomsRequireAuth(req, res, next) {
const authC = new AuthConfig();
const roomsRequireAuth = authC.getRoomsRequireAuth();
const response = {
roomsRequireAuth
}
return res.json(response);
}
} }
module.exports = new authController; module.exports = new authController;

View file

@ -5,5 +5,6 @@ const jwt = require('../middleware/jwtToken.js');
const authController = require('../controllers/auth.js') const authController = require('../controllers/auth.js')
router.get("/getActiveAuth",authController.getActive); router.get("/getActiveAuth",authController.getActive);
router.get("/getRoomsRequireAuth", authController.getRoomsRequireAuth);
module.exports = router; module.exports = router;